# 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 - [x] Lane assignment is named as Unit/Feature fast-feedback/confidence and is the narrowest sufficient proof. - [x] New or changed tests stay in the smallest honest family; no browser or heavy-governance family is added accidentally. - [x] Shared helpers, factories, seeds, fixtures, and context defaults stay cheap by default; Spec 434 fixtures remain local. - [x] Planned validation commands cover adapter/guard behavior without pulling in unrelated lane cost. - [x] Browser proof is explicitly `N/A - no rendered UI surface changed`. - [x] Human Product Sanity and Product Surface implementation-report close-out are `N/A - no rendered UI surface changed`. - [x] 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. - [x] T001 Capture current branch, HEAD, and `git status --short` in `specs/434-exchange-evidence-capture-adapter-content-only-guard/implementation-report.md`. - [x] T002 Confirm Specs 429-433 are completed or implementation-closed read-only context and are not edited. - [x] T003 Confirm Spec 433 implementation report is PASS WITH CONDITIONS with no merge-blocking adapter, readiness, redaction, no-promotion, or no-UI findings. - [x] T004 Confirm no existing `specs/434-*` package is overwritten and this package remains the only active Spec 434 target. - [x] 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_id` scope. - [x] T006 Confirm `tenant_configuration.capture` is the only evidence-owning OperationRun type for this slice. ## Phase 2: Tests First - Adapter and Eligibility **Purpose**: Define expected adapter boundaries before implementation. - [x] T007 [P] Add feature-backed tests in `apps/platform/tests/Feature/TenantConfiguration/Spec434ExchangeEvidenceCaptureAdapterTest.php` proving the adapter accepts only `transportRule`, `remoteDomain`, and `inboundConnector`. - [x] 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. - [x] T009 [P] Add feature-backed tests proving missing Spec 433 readiness, `exchange_powershell_invoke` provider capability status other than `Supported`, missing Spec 432 runner boundary, invalid `ExchangePowerShellInvocationResult` shape, and missing Spec 430 verified command contract block before evidence append. - [x] T010 [P] Add tests proving Exchange capture does not call `ProviderGateway` or the generic Graph list path. - [x] T011 [P] Add tests proving no fake Graph endpoint, fake captured outcome, or `fake_empty_success` is used for Exchange eligibility, and persisted outcomes reuse existing `CaptureOutcome` values only. ## Phase 3: Tests First - Identity and Content Guard **Purpose**: Prove unsafe evidence cannot be appended or over-promoted. - [x] T012 [P] Add feature-backed tests in `apps/platform/tests/Feature/TenantConfiguration/Spec434ExchangeEvidenceCaptureAdapterTest.php` proving identity conflict blocks before resource upsert/evidence append. - [x] T013 [P] Add feature-backed tests proving missing stable external ID, unsupported identity, duplicate identity, and display-name-only identity block by default. - [x] 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. - [x] T015 [P] Add feature-backed tests in `apps/platform/tests/Feature/TenantConfiguration/Spec434ExchangeEvidenceCaptureAdapterTest.php` proving Exchange adapter writes cannot exceed `content_backed`. - [x] 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. - [x] T017 [P] Add feature-backed tests in `apps/platform/tests/Feature/TenantConfiguration/Spec434ExchangeEvidenceCaptureAdapterTest.php` proving empty collections create no resource row and no evidence row. - [x] T018 [P] Add tests proving empty collections produce only safe flat numeric zero-item summary counts using existing summary keys. - [x] 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. - [x] T020 [P] Add static guard assertions in `apps/platform/tests/Feature/TenantConfiguration/Spec434ExchangeEvidenceCaptureAdapterTest.php` proving 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. - [x] 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. - [x] T022 Create `apps/platform/app/Services/TenantConfiguration/ExchangePowerShellEvidenceCaptureAdapter.php` or repo-canonical equivalent. - [x] T023 Implement same-scope validation for workspace, managed environment, provider connection, and `tenant_configuration.capture` OperationRun. - [x] T024 Implement target allowlist for `transportRule`, `remoteDomain`, and `inboundConnector`. - [x] T025 Implement dependency on Spec 433 combined readiness, `ProviderCapabilityEvaluator` `exchange_powershell_invoke` status `Supported`, and Spec 432 accepted `ExchangePowerShellInvocationResult` output boundary. - [x] T026 Implement dependency on verified Spec 430 command contract metadata. - [x] 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. - [x] T028 Create `apps/platform/app/Services/TenantConfiguration/ExchangePowerShellCaptureEligibilityGate.php` or repo-canonical equivalent. - [x] T029 Represent Exchange capture eligibility without fake Graph `source_endpoint` or fake `Captured` source contract outcome. - [x] T030 Preserve `CoverageSourceContractResolver` Graph behavior and add only bounded Exchange-specific eligibility metadata where needed. - [x] T031 Ensure `transportRule`, `remoteDomain`, and `inboundConnector` remain pending full content-backed promotion until Spec 435 or equivalent. - [x] 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::collection` outside the evidence path. ## Phase 7: Identity Hard-Stop Implementation **Purpose**: Stop unsafe identity before any evidence append. - [x] T033 Create `apps/platform/app/Services/TenantConfiguration/ExchangePowerShellIdentityEvidenceGate.php` or repo-canonical equivalent. - [x] T034 Evaluate identity before calling `CoverageEvidenceWriter`. - [x] T035 Block identity conflict, missing stable external ID, unsupported identity, duplicate identity, and display-name-only identity by default. - [x] T036 Return only Spec 434 allowlisted sanitized blocker outcomes without raw payload or provider output. - [x] T037 Ensure unsafe identity creates no `TenantConfigurationResourceEvidence` row. ## Phase 8: Content-Only Guard Implementation **Purpose**: Prevent accidental promotion beyond internal content-backed evidence. - [x] T038 Create `apps/platform/app/Services/TenantConfiguration/ExchangePowerShellContentOnlyEvidenceGuard.php` or repo-canonical equivalent. - [x] T039 Add a max-coverage-level guard for the Exchange adapter path so stored coverage is no higher than `content_backed`. - [x] T040 Adjust `CoverageEvidenceWriter` or call-site options narrowly enough that existing Graph capture behavior remains unchanged. - [x] T041 Ensure comparable, renderable, certified, restore-ready, and customer-claimable state cannot be set through the Exchange adapter path. - [x] 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. - [x] T043 Implement empty collection handling as `exchange_capture_empty_collection` or repo-canonical equivalent and safe summary counts only. - [x] T044 Ensure empty collections create no fake `TenantConfigurationResource` and no fake `TenantConfigurationResourceEvidence`. - [x] T045 Ensure durable collection-level empty proof is not implemented in Spec 434. - [x] T046 Sanitize OperationRun context and failure/outcome mapping so only Spec 434 allowlisted internal codes, existing `CaptureOutcome` values, existing ProviderReasonCodes or `ext.*` reason codes, and no raw stdout/stderr, serialized Exchange objects, credential material, tokens, or provider response bodies are present. - [x] 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. - [x] T048 Run focused Spec 434 tests: `cd apps/platform && ./vendor/bin/sail artisan test --filter=Spec434 --compact`. - [x] T049 Run Spec 433 readiness regression: `cd apps/platform && ./vendor/bin/sail artisan test --filter=Spec433 --compact`. - [x] T050 Run Spec 432/431/430 regression: `cd apps/platform && ./vendor/bin/sail artisan test --filter='Spec432|Spec431|Spec430' --compact`. - [x] T051 Run Coverage v2/generic capture/identity regression: `cd apps/platform && ./vendor/bin/sail artisan test --filter='Spec415|Spec417|Spec419|Spec420|Spec426|Spec427' --compact`. - [x] T052 Run provider capability regression: `cd apps/platform && ./vendor/bin/sail artisan test --filter='ProviderCapabilityRegistryTest|ProviderCapabilityEvaluationTest' --compact`. - [x] 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. - [x] T054 Run `cd apps/platform && ./vendor/bin/pint --dirty --test`. - [x] T055 Run `git diff --check`. - [x] T056 Run `git status --short` and confirm only active Spec 434 and intended runtime/test files changed. - [x] T057 Create `specs/434-exchange-evidence-capture-adapter-content-only-guard/implementation-report.md`. - [x] T058 Record branch, HEAD, dirty state before/after, files changed, and completed-spec read-only proof. - [x] T059 Record Spec 433 prerequisite proof and any PASS WITH CONDITIONS handling. - [x] T060 Record OperationRun decision: `tenant_configuration.capture`, no new OperationRun type, existing summary keys only. - [x] T061 Record adapter boundary proof and generic Graph capture boundary proof. - [x] 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. - [x] 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. - [x] 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 under `apps/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_id` ownership truth; - legacy shim, fallback reader, dual write, or Coverage v1 bridge.