Summary: add Spec 434 Exchange PowerShell evidence capture adapter and prerequisite/identity/content-only guards; cap Exchange evidence at content_backed while preserving Graph capture. Validation: php artisan test --filter=Spec434 --compact; ./vendor/bin/pint --dirty --test; git diff --cached --check. Product Surface: N/A - no rendered UI surface changed; no deployment impact. Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de> Reviewed-on: #501
14 KiB
Tasks: Exchange Evidence Capture Adapter and Content-Only Guard
Input: Design documents from specs/434-exchange-evidence-capture-adapter-content-only-guard/
Prerequisites: spec.md, plan.md, checklists/requirements.md
Implementation Mode: backend adapter/guard implementation only. No UI, routes, jobs, schedules, listeners, migrations, customer output, compare/render, certification, restore, or tenant_id ownership truth unless the spec is amended first.
Implementation Note (2026-07-08): T007-T021 were covered by
apps/platform/tests/Feature/TenantConfiguration/Spec434ExchangeEvidenceCaptureAdapterTest.php
rather than split into separate Unit files because the adapter boundary, unsafe-identity zero resource/evidence assertions,
OperationRun summary-count updates, same-scope checks, and evidence/resource absence are DB-backed
behavior. The file keeps fixtures local and also exercises direct identity-gate paths for existing
conflict and unsupported-identity states. No literal writer spy is used; writer non-reachability is
proven by adapter code order plus zero resource/evidence database assertions.
Test Governance Checklist
- Lane assignment is named as Unit/Feature fast-feedback/confidence and is the narrowest sufficient proof.
- New or changed tests stay in the smallest honest family; no browser or heavy-governance family is added accidentally.
- Shared helpers, factories, seeds, fixtures, and context defaults stay cheap by default; Spec 434 fixtures remain local.
- Planned validation commands cover adapter/guard behavior without pulling in unrelated lane cost.
- Browser proof is explicitly
N/A - no rendered UI surface changed. - Human Product Sanity and Product Surface implementation-report close-out are
N/A - no rendered UI surface changed. - Any migration, UI, customer-output, or durable empty-collection proof need is escalated to spec amendment or follow-up spec.
Phase 1: Preflight
Purpose: Lock scope, repo truth, and completed-spec guardrails before runtime work.
- T001 Capture current branch, HEAD, and
git status --shortinspecs/434-exchange-evidence-capture-adapter-content-only-guard/implementation-report.md. - T002 Confirm Specs 429-433 are completed or implementation-closed read-only context and are not edited.
- T003 Confirm Spec 433 implementation report is PASS WITH CONDITIONS with no merge-blocking adapter, readiness, redaction, no-promotion, or no-UI findings.
- T004 Confirm no existing
specs/434-*package is overwritten and this package remains the only active Spec 434 target. - T005 Confirm implementation diff starts with no runtime UI, route, job, schedule, listener, migration, customer-output, report, Review Pack, PDF, restore, certification, compare/render, or
tenant_idscope. - T006 Confirm
tenant_configuration.captureis the only evidence-owning OperationRun type for this slice.
Phase 2: Tests First - Adapter and Eligibility
Purpose: Define expected adapter boundaries before implementation.
- T007 [P] Add feature-backed tests in
apps/platform/tests/Feature/TenantConfiguration/Spec434ExchangeEvidenceCaptureAdapterTest.phpproving the adapter accepts onlytransportRule,remoteDomain, andinboundConnector. - T008 [P] Add feature-backed tests proving wrong OperationRun type, missing OperationRun, wrong workspace, wrong managed environment, and wrong provider connection block before evidence append.
- T009 [P] Add feature-backed tests proving missing Spec 433 readiness,
exchange_powershell_invokeprovider capability status other thanSupported, missing Spec 432 runner boundary, invalidExchangePowerShellInvocationResultshape, and missing Spec 430 verified command contract block before evidence append. - T010 [P] Add tests proving Exchange capture does not call
ProviderGatewayor the generic Graph list path. - T011 [P] Add tests proving no fake Graph endpoint, fake captured outcome, or
fake_empty_successis used for Exchange eligibility, and persisted outcomes reuse existingCaptureOutcomevalues only.
Phase 3: Tests First - Identity and Content Guard
Purpose: Prove unsafe evidence cannot be appended or over-promoted.
- T012 [P] Add feature-backed tests in
apps/platform/tests/Feature/TenantConfiguration/Spec434ExchangeEvidenceCaptureAdapterTest.phpproving identity conflict blocks before resource upsert/evidence append. - T013 [P] Add feature-backed tests proving missing stable external ID, unsupported identity, duplicate identity, and display-name-only identity block by default.
- T014 [P] Prove unsafe identity does not reach evidence append by adapter code order plus zero resource/evidence assertions; no literal writer spy is used.
- T015 [P] Add feature-backed tests in
apps/platform/tests/Feature/TenantConfiguration/Spec434ExchangeEvidenceCaptureAdapterTest.phpproving Exchange adapter writes cannot exceedcontent_backed. - T016 [P] Add tests proving comparable, renderable, certified, restore-ready, and customer-claimable promotion does not occur through the Spec 434 path.
Phase 4: Tests First - Empty Collection, Redaction, and No Product Surface
Purpose: Prove zero-item and redaction behavior before implementation.
- T017 [P] Add feature-backed tests in
apps/platform/tests/Feature/TenantConfiguration/Spec434ExchangeEvidenceCaptureAdapterTest.phpproving empty collections create no resource row and no evidence row. - T018 [P] Add tests proving empty collections produce only safe flat numeric zero-item summary counts using existing summary keys.
- T019 [P] Add tests proving OperationRun context/messages/summary counts contain no raw payload, stdout, stderr, serialized Exchange object, credential material, token, or provider response body.
- T020 [P] Add static guard assertions in
apps/platform/tests/Feature/TenantConfiguration/Spec434ExchangeEvidenceCaptureAdapterTest.phpproving no new routes, Filament pages/resources/widgets, Livewire components, Blade views, navigation, assets, global search resources, jobs, schedules, listeners, customer-output code, or migrations are introduced. - T021 [P] Add static guard assertions proving no
tenant_id, Exchange-specific evidence table, legacy shim, fallback reader, dual write, or Exchange mini-platform is introduced.
Phase 5: Adapter Boundary Implementation
Purpose: Add the narrow trusted Exchange adapter.
- T022 Create
apps/platform/app/Services/TenantConfiguration/ExchangePowerShellEvidenceCaptureAdapter.phpor repo-canonical equivalent. - T023 Implement same-scope validation for workspace, managed environment, provider connection, and
tenant_configuration.captureOperationRun. - T024 Implement target allowlist for
transportRule,remoteDomain, andinboundConnector. - T025 Implement dependency on Spec 433 combined readiness,
ProviderCapabilityEvaluatorexchange_powershell_invokestatusSupported, and Spec 432 acceptedExchangePowerShellInvocationResultoutput boundary. - T026 Implement dependency on verified Spec 430 command contract metadata.
- T027 Ensure adapter is internal/trusted only and exposes no route, UI action, command, schedule, listener, or public trigger.
Phase 6: Capture Eligibility and Source Contract Semantics
Purpose: Keep Exchange capture honest without pretending it is Graph capture.
- T028 Create
apps/platform/app/Services/TenantConfiguration/ExchangePowerShellCaptureEligibilityGate.phpor repo-canonical equivalent. - T029 Represent Exchange capture eligibility without fake Graph
source_endpointor fakeCapturedsource contract outcome. - T030 Preserve
CoverageSourceContractResolverGraph behavior and add only bounded Exchange-specific eligibility metadata where needed. - T031 Ensure
transportRule,remoteDomain, andinboundConnectorremain pending full content-backed promotion until Spec 435 or equivalent. - T032 Map safe command contract metadata, accepted runner-result metadata, response shape metadata, redaction metadata, and normalization handoff into sanitized internal context without storing raw
ExchangePowerShellInvocationResult::collectionoutside the evidence path.
Phase 7: Identity Hard-Stop Implementation
Purpose: Stop unsafe identity before any evidence append.
- T033 Create
apps/platform/app/Services/TenantConfiguration/ExchangePowerShellIdentityEvidenceGate.phpor repo-canonical equivalent. - T034 Evaluate identity before calling
CoverageEvidenceWriter. - T035 Block identity conflict, missing stable external ID, unsupported identity, duplicate identity, and display-name-only identity by default.
- T036 Return only Spec 434 allowlisted sanitized blocker outcomes without raw payload or provider output.
- T037 Ensure unsafe identity creates no
TenantConfigurationResourceEvidencerow.
Phase 8: Content-Only Guard Implementation
Purpose: Prevent accidental promotion beyond internal content-backed evidence.
- T038 Create
apps/platform/app/Services/TenantConfiguration/ExchangePowerShellContentOnlyEvidenceGuard.phpor repo-canonical equivalent. - T039 Add a max-coverage-level guard for the Exchange adapter path so stored coverage is no higher than
content_backed. - T040 Adjust
CoverageEvidenceWriteror call-site options narrowly enough that existing Graph capture behavior remains unchanged. - T041 Ensure comparable, renderable, certified, restore-ready, and customer-claimable state cannot be set through the Exchange adapter path.
- T042 Ensure no new customer-visible status vocabulary or Product Surface runtime framework is introduced.
Phase 9: Empty Collection and Redaction Implementation
Purpose: Keep zero-item and sensitive-output behavior safe.
- T043 Implement empty collection handling as
exchange_capture_empty_collectionor repo-canonical equivalent and safe summary counts only. - T044 Ensure empty collections create no fake
TenantConfigurationResourceand no fakeTenantConfigurationResourceEvidence. - T045 Ensure durable collection-level empty proof is not implemented in Spec 434.
- T046 Sanitize OperationRun context and failure/outcome mapping so only Spec 434 allowlisted internal codes, existing
CaptureOutcomevalues, existing ProviderReasonCodes orext.*reason codes, and no raw stdout/stderr, serialized Exchange objects, credential material, tokens, or provider response bodies are present. - T047 Use existing redaction/evidence storage policy for non-empty guarded fixture evidence only.
Phase 10: Regression Coverage
Purpose: Prove adjacent completed behavior remains intact.
- T048 Run focused Spec 434 tests:
cd apps/platform && ./vendor/bin/sail artisan test --filter=Spec434 --compact. - T049 Run Spec 433 readiness regression:
cd apps/platform && ./vendor/bin/sail artisan test --filter=Spec433 --compact. - T050 Run Spec 432/431/430 regression:
cd apps/platform && ./vendor/bin/sail artisan test --filter='Spec432|Spec431|Spec430' --compact. - T051 Run Coverage v2/generic capture/identity regression:
cd apps/platform && ./vendor/bin/sail artisan test --filter='Spec415|Spec417|Spec419|Spec420|Spec426|Spec427' --compact. - T052 Run provider capability regression:
cd apps/platform && ./vendor/bin/sail artisan test --filter='ProviderCapabilityRegistryTest|ProviderCapabilityEvaluationTest' --compact. - T053 If Sail is unavailable, document the failure and use the repo's non-Docker fallback
cd apps/platform && php artisan test ...for the same focused filters.
Phase 11: Final Validation and Close-Out
Purpose: Finish the implementation report and review proof.
- T054 Run
cd apps/platform && ./vendor/bin/pint --dirty --test. - T055 Run
git diff --check. - T056 Run
git status --shortand confirm only active Spec 434 and intended runtime/test files changed. - T057 Create
specs/434-exchange-evidence-capture-adapter-content-only-guard/implementation-report.md. - T058 Record branch, HEAD, dirty state before/after, files changed, and completed-spec read-only proof.
- T059 Record Spec 433 prerequisite proof and any PASS WITH CONDITIONS handling.
- T060 Record OperationRun decision:
tenant_configuration.capture, no new OperationRun type, existing summary keys only. - T061 Record adapter boundary proof and generic Graph capture boundary proof.
- T062 Record capture eligibility, provider-capability gate, accepted runner-result gate, outcome/failure-code allowlist, identity hard-stop, content-only writer guard, empty collection policy, and redaction proof.
- T063 Record no compare/render/certification/restore/customer claim, no UI/routes/jobs/schedules/listeners, no migration, no Exchange-specific table, no
tenant_id, no legacy shim, no fallback reader, and no mini-platform proof. - T064 Record tests run, browser
N/A - no rendered UI surface changed, Laravel 12/PHP 8.4/PostgreSQL/Pest 4 compatibility unchanged, Livewire v4 compliance unchanged, provider registration unchanged underapps/platform/bootstrap/providers.php, global search unchanged, destructive/high-impact actions none, asset strategy none, deployment impact none, visible complexity neutral, and deferred work.
Dependencies
- Phase 1 must complete before runtime work.
- Phases 2-4 tests should be created before implementation where practical.
- Phase 5 adapter boundary precedes Phase 6 eligibility and Phase 7 identity wiring.
- Phase 8 content-only guard must be complete before non-empty fixture evidence can be accepted.
- Phase 9 empty collection/redaction must be complete before final regression.
- Phase 11 closes only after all required validation is documented.
Stop Conditions
Stop and amend or split the spec if implementation needs any of the following:
- new OperationRun type;
- migration or durable collection-proof model;
- UI, route, navigation, Filament, Livewire, browser, asset, or global search changes;
- job, schedule, listener, console trigger, or public start surface;
- live Exchange provider calls for validation;
- compare/render/certification/restore/customer output;
- Exchange-specific evidence table;
tenant_idownership truth;- legacy shim, fallback reader, dual write, or Coverage v1 bridge.