TenantAtlas/specs/436-exchange-content-backed-evidence-promotion-slice-1/spec.md
Ahmed Darrazi 053a33c172
Some checks failed
PR Fast Feedback / fast-feedback (pull_request) Failing after 7m12s
feat: promote Exchange content-backed evidence
2026-07-09 15:55:44 +02:00

981 lines
55 KiB
Markdown

# Feature Specification: Exchange Content-Backed Evidence Promotion Slice 1
**Feature Branch**: `feat/436-exchange-content-backed-evidence-promotion-slice-1`
**Created**: 2026-07-08
**Status**: Draft
**Type**: Coverage v2 / Microsoft 365 / Exchange / Content-Backed Evidence / Evidence Normalization / Evidence Hashing
**Priority**: P0
**Risk**: Critical
**Input**: User-provided Spec 436 draft for first Exchange PowerShell content-backed evidence promotion slice.
**Implementation Branch Gate**: Runtime implementation must remain on `feat/436-exchange-content-backed-evidence-promotion-slice-1` per `AGENTS.md`, or stop and record an explicit branch exception before changing runtime files.
## Preparation Selection
- **Selected candidate**: Spec 436 - Exchange Content-Backed Evidence Promotion Slice 1.
- **Source location**: Direct user-provided manual P0 candidate in the current request.
- **Why selected**: `docs/product/spec-candidates.md` has no safe automatic next-best-prep target, but the user supplied the next roadmap slice after merge-ready Spec 435. Spec 436 is the first bounded slice where the three proven Exchange targets may become internal content-backed evidence.
- **Close alternatives deferred**: Spec 437 comparable/renderable promotion, Teams PowerShell slices, certification, restore/apply, customer output, Review Packs, reports, UI/product surfaces, schedules, jobs, and target expansion remain deferred because Spec 436 must first prove safe append-only evidence.
- **Roadmap relationship**: Continues the corrected sequence 429 through 442. Specs 430-435 create command, invocation, runner, readiness, adapter, structured output, target normalizer, and hash-readiness prerequisites. Spec 436 consumes those prerequisites for first Exchange content-backed evidence only.
- **Completed-spec guardrail result**: Specs 430-435 are completed or implementation-closed read-only context and were not modified during preparation. Spec 435 implementation report records PASS and merge-ready. No existing `specs/436-*` package or branch was found before preparation.
- **Smallest viable implementation slice**: Wire the existing Exchange capture adapter to Spec 435 structured envelopes, target-specific normalizers, and hash input builder for exactly `transportRule`, `remoteDomain`, and `inboundConnector`; append internal content-backed evidence only when all gates pass; fail closed otherwise.
- **Feature description fed into Spec Kit**: Promote `transportRule`, `remoteDomain`, and `inboundConnector` to append-only internal content-backed Coverage v2 evidence through the existing Exchange PowerShell capture adapter, using Spec 435 structured envelopes, target normalizers, and hash input builder; Spec 434 identity/content-only guards; Spec 433 readiness; Spec 432 runner boundary; and Spec 430 command contracts, with no UI, no migration, no jobs, no compare/render/certification/restore/customer claims, no `tenant_id`, and no Exchange mini-platform.
## Activated Skills And Gates
- **Activated Codex skill**: `spec-kit-next-best-prep` because this turn prepares Spec Kit artifacts only and explicitly stops before application implementation.
- **Repo skill router result**: No runtime implementation skill is activated in this preparation turn.
- **Planning gates carried into artifacts**: spec readiness, workspace-scope safety, RBAC/action safety, OperationRun truth, evidence-anchor contract, provider freshness semantics, customer-output gate, Product Surface gate, and temporary Coverage v2 / TCM cutover guard.
- **Preparation hard-gate stop conditions**: none. Runtime implementation must stop if it needs UI, routes, jobs, schedules, listeners, migrations, a new OperationRun type, `tenant_id`, Graph gateway fallback, fake Graph endpoints, generic/Teams comparable normalizers for Exchange promotion, compare/render/certification/restore/customer claims, or an Exchange-specific evidence table.
## Spec Candidate Check
- **Problem**: TenantPilot has verified Exchange command contracts, readiness gates, a runner boundary, an adapter boundary, identity guards, structured output envelopes, target-specific normalizer readiness, and hash-readiness proof, but it still has no actual Coverage v2 evidence rows for the first eligible Exchange targets.
- **Today's failure**: Exchange runner success can be proven internally, but the product cannot yet create source-backed append-only evidence for `transportRule`, `remoteDomain`, or `inboundConnector`. If implemented loosely, it could overclaim readiness, persist unsafe identity, treat raw stdout as evidence, leak protected Exchange configuration into OperationRun context, or promote directly to comparable/renderable/certified/customer-ready states.
- **User-visible improvement**: Future operators and reviewers get honest internal evidence truth: selected Exchange resources are content-backed only when readiness, structured output, target normalization, deterministic hashing, canonical identity, redaction, and scope gates all pass. Any blocker creates no evidence and produces safe internal context.
- **Smallest enterprise-capable version**: A backend-only internal capture path for exactly three Exchange resource types, appending existing Coverage v2 evidence rows and updating the latest evidence pointer only when gates pass. No UI, trigger, migration, customer output, restore, certification, compare/render, target expansion, or new operation type.
- **Explicit non-goals**: No compare output, render models, certification, restore/apply, Review Pack/PDF/report output, customer claims, Filament pages, Livewire components, routes, navigation, capture buttons, jobs, schedules, listeners, Teams PowerShell, Exchange Admin API, outboundConnector, acceptedDomain, organizationConfig, mailboxPlan, sharingPolicy, arbitrary commands, mutation commands, Exchange-specific evidence tables, `tenant_id`, legacy shims, fallback readers, or durable collection-level empty proof.
- **Permanent complexity imported**: Adapter wiring changes, possible constructor dependency changes, target-specific evidence promotion tests, feature tests for append-only evidence, and a preparation/implementation report. No new persisted table, OperationRun type, UI framework, product taxonomy, or provider mini-platform.
- **Why now**: Spec 435 is PASS and merge-ready, and Spec 437 comparable/renderable work must not begin until content-backed evidence exists and is proven safe for the eligible targets.
- **Why not local**: The current adapter still has a generic/comparable normalizer path. This safety-critical evidence boundary must be explicit, tested, and reusable by the immediate next Exchange roadmap slice.
- **Approval class**: Core Enterprise.
- **Red flags triggered**: New evidence promotion wiring and foundation-like sequence. Defense: the slice is bounded to three already-proven targets, uses existing Coverage v2 tables, removes unsafe generic promotion paths, and prevents false customer/product claims.
- **Score**: Nutzen: 2 | Dringlichkeit: 2 | Scope: 2 | Komplexitaet: 1 | Produktnaehe: 1 | Wiederverwendung: 2 | **Gesamt: 10/12**
- **Decision**: approve.
## Spec Scope Fields
- **Scope**: managed-environment backend evidence capture under existing workspace, managed environment, provider connection, OperationRun, and Coverage v2 boundaries.
- **Primary Routes**: none.
- **Data Ownership**: Existing Coverage v2 ownership remains `workspace_id`, `managed_environment_id`, and `provider_connection_id`. `tenant_configuration.capture` is the evidence-owning OperationRun. No `tenant_id` ownership truth and no Exchange-specific evidence table.
- **RBAC**: No new user action or route. Capture must remain reachable only through trusted backend flows that already satisfy provider/capture authorization, readiness, and operation scope gates.
For canonical-view specs: N/A, no canonical UI view is introduced or changed.
## No Legacy / No Backward Compatibility Constraint
- **Compatibility posture**: canonical replacement / no compatibility exception.
- **Legacy aliases, fallback readers, hidden routes, duplicate UI, old labels, or historical fixtures kept?**: no.
- **Why clean replacement is safe now**: TenantPilot is pre-production for this path. Spec 436 must replace unsafe generic Exchange promotion behavior rather than preserve it.
## UI Surface Impact
- [x] No UI surface impact
- [ ] Existing page changed
- [ ] New page/route added
- [ ] Navigation changed
- [ ] Filament panel/provider surface changed
- [ ] New modal/drawer/wizard/action added
- [ ] New table/form/state added
- [ ] Customer-facing surface changed
- [ ] Dangerous action changed
- [ ] Status/evidence/review presentation changed
- [ ] Workspace/environment context presentation changed
## UI/Productization Coverage
N/A - no reachable UI surface impact. Spec 436 forbids routes, Filament pages/resources/widgets, Livewire components, Blade views, navigation, actions, assets, global search changes, reports, downloads, and rendered customer/operator surfaces.
## Product Surface Impact
Reference: `docs/product/standards/product-surface-contract.md`.
- **Product Surface Contract applies?**: no runtime UI surface change; gate is recorded as N/A.
- **Page archetype**: N/A.
- **Primary user question**: N/A.
- **Primary action**: N/A.
- **Surface budget result**: N/A.
- **Technical Annex / deep-link demotion**: N/A - no rendered product surface. OperationRun, raw evidence payloads, IDs, source keys, provider diagnostics, protected config, normalized payloads, and hashes stay internal and are not rendered.
- **Canonical status vocabulary**: N/A for UI. Internal capture outcomes are not product-facing status vocabulary.
- **Visible complexity impact**: neutral.
- **Product Surface exceptions**: none.
## Browser Verification Plan
- **Browser proof required?**: no.
- **No-browser rationale**: `N/A - no rendered UI surface changed`.
- **Focused path when required**: N/A.
- **Primary interaction to execute**: N/A.
- **Console, Livewire, Filament, network, and 500-error checks**: N/A.
- **Full-suite failure triage**: N/A.
## Human Product Sanity Check
- **Required?**: no.
- **No-human-sanity rationale**: N/A - no product surface changed.
- **Reviewer questions**: N/A.
- **Planned result location**: implementation report must record `N/A - no rendered UI surface changed`.
## Product Surface Merge Gate Checklist
- [x] No-legacy posture or approved exception recorded.
- [x] Product Surface Impact is completed as `N/A`.
- [x] Browser proof is planned as `N/A - no rendered UI surface changed`.
- [x] Human Product Sanity is not applicable with rationale.
- [x] Product Surface exceptions are `none`.
- [x] Implementation report will state Livewire v4 compliance, provider registration location, global search posture, destructive/high-impact action posture, asset strategy, tests/browser result, deployment impact, visible complexity outcome, and no completed-spec rewrite assertion.
## Cross-Cutting / Shared Pattern Reuse
- **Cross-cutting feature?**: yes, backend evidence/capture readiness only.
- **Interaction class(es)**: evidence persistence, OperationRun execution truth, provider readiness, identity evaluation, capture outcome summarization, redaction.
- **Systems touched**: existing TenantConfiguration/Coverage v2 Exchange PowerShell runner, output guard, capture adapter, target normalizers, hash input builder, canonical identity, evidence writer, provider capability readiness, and OperationRun summary-count conventions.
- **Existing pattern(s) to extend**: `ExchangePowerShellEvidenceCaptureAdapter`, `ExchangePowerShellStructuredOutputEnvelope`, `ExchangePowerShellEvidenceNormalizer`, `ExchangePowerShellTargetEvidenceNormalizer`, `ExchangePowerShellHashInputBuilder`, `CanonicalIdentityResolver`, `ExchangePowerShellIdentityEvidenceGate`, `ExchangePowerShellContentOnlyEvidenceGuard`, `CoverageResourceUpserter`, `CoverageEvidenceWriter`, and `OperationSummaryKeys`.
- **Shared contract / presenter / builder / renderer to reuse**: backend services only; no UI presenter or renderer.
- **Why the existing shared path is sufficient or insufficient**: Existing Coverage v2 resource/evidence storage is sufficient. The current adapter normalization path is insufficient because it still routes through `GenericPayloadNormalizer` and `ExchangeTeamsComparablePayloadNormalizer`, which Spec 436 forbids for Exchange evidence promotion.
- **Allowed deviation and why**: bounded provider-owned Exchange normalizer path is required because Exchange PowerShell object shape is provider-specific. It must remain behind Exchange-named classes and must not become platform-core truth.
- **Consistency impact**: OperationRun remains execution truth, evidence rows are append-only, scope remains workspace + managed environment + provider connection, and customer/product claims remain blocked.
- **Review focus**: verify no Graph gateway fallback, no fake Graph endpoints, no generic/comparable normalizer promotion path, no raw stdout/stderr evidence, no target expansion, and no customer claim.
## OperationRun UX Impact
- **Touches OperationRun start/completion/link UX?**: no rendered UX. It touches internal OperationRun ownership/context requirements for capture evidence.
- **Shared OperationRun UX contract/layer reused**: N/A for UI. Runtime must use existing `tenant_configuration.capture` operation truth and existing OperationRun lifecycle conventions.
- **Delegated start/completion UX behaviors**: N/A.
- **Local surface-owned behavior that remains**: none.
- **Queued DB-notification policy**: N/A. No new queue/start UI or notification opt-in.
- **Terminal notification path**: unchanged central lifecycle behavior for `tenant_configuration.capture`.
- **Exception required?**: none. If implementation needs a new OperationRun type, queued notification, start surface, run link behavior, or UI path, stop and amend/split the spec.
## Provider Boundary / Platform Core Check
- **Shared provider/platform boundary touched?**: yes.
- **Boundary classification**: mixed. Exchange PowerShell command/output/normalizer semantics are provider-owned. Coverage v2 evidence safety, OperationRun truth, identity safety, redaction, scope, and customer-claim boundaries are platform-core.
- **Seams affected**: command contract selection, runner-result acceptance, structured output envelope, target normalizer dispatch, hash input rules, identity resolution, evidence writer maximum level, OperationRun context/summary counts, provider readiness and `exchange_powershell_invoke` capability gates.
- **Neutral platform terms preserved or introduced**: provider connection, managed environment, resource type, evidence, operation run, capture outcome, normalized payload, payload hash, canonical identity, content-backed.
- **Provider-specific semantics retained and why**: `Get-TransportRule`, `Get-RemoteDomain`, `Get-InboundConnector`, `exchange_online_powershell_rest`, and target-specific protected fields remain bounded provider-owned semantics.
- **Why this does not deepen provider coupling accidentally**: provider-specific shape handling stays behind Exchange-named services/tests and persists only through existing provider-neutral Coverage v2 evidence tables.
- **Follow-up path**: Spec 437 may promote comparable/renderable semantics only after Spec 436 proves append-only content-backed evidence safety.
## UI / Surface Guardrail Impact
| Surface / Change | Operator-facing surface change? | Native vs Custom | Shared-Family Relevance | State Layers Touched | Exception Needed? | Low-Impact / `N/A` Note |
|---|---|---|---|---|---|---|
| Exchange content-backed evidence promotion | no | N/A | backend evidence/OperationRun only | backend capture/evidence only | no | `N/A - no rendered UI surface changed` |
## Proportionality Review
- **New source of truth?**: no. Evidence truth remains existing Coverage v2 evidence rows; execution truth remains `OperationRun`.
- **New persisted entity/table/artifact?**: no new table or persisted artifact family. Spec 436 writes existing evidence rows only.
- **New abstraction?**: no broad new abstraction is planned. Runtime may add narrow private helper methods or tests, but must prefer existing Spec 434/435 classes.
- **New enum/state/reason family?**: no persisted enum/status family. If a new capture outcome or summary key is unavoidable, implementation must amend this spec before continuing.
- **New cross-domain UI framework/taxonomy?**: no.
- **Current operator problem**: Future Exchange readiness and compare/render claims would be false unless source-backed evidence exists and is safely scoped, normalized, hashed, and identity-stable.
- **Existing structure is insufficient because**: Spec 434 created the adapter/guard but still uses generic/Teams comparable normalizer paths for actual write-time payload normalization; Spec 435 provides target-specific readiness classes that now must be wired into persistence.
- **Narrowest correct implementation**: replace the adapter's Exchange write path with Spec 435 structured envelope, target normalizers, and hash builder for exactly three targets while retaining existing Coverage v2 tables and guards.
- **Ownership cost**: maintain target-specific Exchange evidence promotion tests and no-promotion guard tests until comparable/renderable promotion is separately approved.
- **Alternative intentionally rejected**: reuse `GenericPayloadNormalizer` or `ExchangeTeamsComparablePayloadNormalizer` because that would blur evidence promotion with generic or comparable/renderable semantics.
- **Release truth**: current-release prerequisite for immediate next Exchange comparable/renderable slice.
## Testing / Lane / Runtime Impact
- **Test purpose / classification**: Unit and Feature.
- **Validation lane(s)**: fast-feedback/confidence; browser N/A.
- **Why this classification and these lanes are sufficient**: Adapter wiring, normalization/hash determinism, identity blocking, append-only evidence, OperationRun sanitization, and no-surface assertions can be proven with fake runner output and DB-backed feature tests.
- **New or expanded test families**: focused Spec 436 TenantConfiguration unit/feature tests.
- **Fixture / helper cost impact**: local Exchange structured output fixtures and existing provider readiness fixtures only; no browser/provider setup defaults should be widened.
- **Heavy-family visibility / justification**: none.
- **Special surface test profile**: N/A.
- **Standard-native relief or required special coverage**: browser `N/A - no rendered UI surface changed`.
- **Reviewer handoff**: confirm tests prove content-backed evidence safety without UI, trigger, migration, `tenant_id`, Graph fallback, raw stdout/stderr evidence, compare/render/certification/restore/customer claims, or mini-platform drift.
- **Budget / baseline / trend impact**: expected focused additions only; no heavy-governance or browser budget impact.
- **Escalation needed**: reject-or-split for evidence scope expansion, UI, migration, target expansion, compare/render, certification, restore, customer output, new operation type, or durable empty-collection proof.
- **Active feature PR close-out entry**: Guardrail / Exception / Smoke Coverage: N/A no rendered UI surface changed.
## Executive Summary
Spec 430 created verified Exchange PowerShell command contracts for `transportRule`, `remoteDomain`, and `inboundConnector`.
Spec 431 registered and gated the Exchange PowerShell invocation operation.
Spec 432 created the production-runner boundary and runtime gate.
Spec 433 created Exchange credential and permission evidence readiness.
Spec 434 created the internal Exchange evidence capture adapter boundary, identity hard-stop, content-only guard, and empty-collection no-evidence policy.
Spec 435 proved structured Exchange output and target-specific normalizer readiness for the three targets.
Spec 436 is the first actual Exchange content-backed evidence promotion slice. It promotes only `transportRule`, `remoteDomain`, and `inboundConnector` to internal content-backed evidence when all gates pass.
Spec 436 must wire the Exchange evidence capture adapter to:
- Spec 435 structured output envelope.
- Spec 435 target-specific normalizers.
- Spec 435 hash input builder.
- Spec 434 identity hard-stop.
- Spec 434 content-only writer guard.
- Spec 433 readiness evaluator.
- Spec 432 production runner boundary.
- Spec 430 verified command contracts.
Spec 436 does not implement compare/render, certification, restore/apply, UI, routes, Filament pages, Livewire components, jobs, schedules, listeners, reports, review packs, customer claims, or target expansion.
## Prerequisites
Spec 436 may begin runtime implementation only after Spec 435 is PASS and merge-ready.
Required proof:
- Spec 435 structured Exchange output envelope exists and excludes raw stdout/stderr.
- Spec 435 normalizer readiness is proven for `transportRule`, `remoteDomain`, and `inboundConnector`.
- Spec 435 hash input rules are deterministic.
- Spec 434 adapter boundary exists and uses `tenant_configuration.capture` as evidence owner.
- Spec 434 identity hard-stop, content-only guard, and empty-collection no-evidence policy exist.
- Spec 433 combined credential/permission readiness exists and blocks client-secret/admin-consent-only paths.
- Spec 432 production runner boundary, output guards, timeout/concurrency guards, and sanitized context exist.
- Spec 430 command contracts exist for `Get-TransportRule`, `Get-RemoteDomain`, and `Get-InboundConnector`; mutation/raw commands are rejected.
Hard stop if runtime implementation:
- Uses `GenericPayloadNormalizer` for Exchange evidence promotion.
- Uses `ExchangeTeamsComparablePayloadNormalizer` for Exchange evidence promotion.
- Routes Exchange capture through Graph provider gateway or fake Graph endpoints.
- Uses `tenant_configuration.exchange_powershell_invocation` as evidence owner.
- Adds a new OperationRun type.
- Bypasses readiness, runner boundary, structured envelope, target normalizer, hash builder, identity hard-stop, or content-only guard.
- Adds UI, routes, Filament, Livewire, jobs, schedules, listeners, migrations, `tenant_id`, compare/render/certification/restore/customer state, target expansion, or an Exchange mini-platform.
## Current Problem
TenantPilot can prove command contracts, invocation gates, production runner safety, credential/permission readiness, adapter boundary safety, structured output, target normalizer readiness, identity readiness, redaction readiness, and hash input readiness. It still cannot create append-only Coverage v2 evidence rows for the first three eligible Exchange targets through the safe Exchange capture path.
## Goal
Implement append-only content-backed evidence for:
- `transportRule`
- `remoteDomain`
- `inboundConnector`
Spec 436 must:
1. Use the existing Spec 434 Exchange evidence capture adapter.
2. Reuse `tenant_configuration.capture` as the evidence-owning OperationRun.
3. Keep `tenant_configuration.exchange_powershell_invocation` as runner/invocation truth only.
4. Use Spec 435 structured output envelope, target normalizers, and hash input builder.
5. Remove or bypass generic/Teams comparable normalizer paths for Exchange evidence promotion.
6. Use Spec 433 combined readiness, Spec 432 runner boundary, and Spec 430 command contracts.
7. Capture only through trusted application flow.
8. Persist raw structured provider item/envelope only in Coverage v2 evidence storage.
9. Persist deterministic normalized payload and deterministic payload hash.
10. Use `CanonicalIdentityResolver` and require safe/stable identity before evidence append.
11. Preserve empty collection no-evidence policy.
12. Keep coverage capped at `content_backed`.
13. Keep claims internal-only/customer-blocked.
14. Prevent compare/render/certification/restore/customer promotion.
## Target Scope
Included resource types:
- `transportRule`
- `remoteDomain`
- `inboundConnector`
Included commands:
- `Get-TransportRule`
- `Get-RemoteDomain`
- `Get-InboundConnector`
Explicitly excluded:
- `outboundConnector`
- `acceptedDomain`
- `organizationConfig`
- `mailboxPlan`
- `sharingPolicy`
- all Teams types
- all Security & Compliance types
- all mutation commands
- all restore commands
- all customer-facing surfaces
No target expansion is allowed.
## Target Eligibility
### transportRule
Eligible when all gates pass.
Required:
- stable identity through `id`, `sourceId`, `Guid`, or `RuleId`
- Spec 435 `ExchangeTransportRuleEvidenceNormalizer`
- redaction for conditions/actions/exceptions
- deterministic hash input
- content-only writer guard
Block if stable identity is missing, duplicate, conflicting, unsafe, normalizer/hash/readiness fails, or output shape is unsafe.
### remoteDomain
Eligible only when stable source identity is present.
Allowed stable identity candidates:
- `id`
- `sourceId`
- `Guid`
- `RemoteDomainId`
- `Identity`
Domain-only identity, display-only identity, and derived-only identity are blocked unless repo rules explicitly prove stability and tests prove no ambiguity. Spec 436 defaults to blocking them.
### inboundConnector
Eligible only when stable connector identity and redaction safety are present.
Allowed stable identity candidates:
- `id`
- `sourceId`
- `Guid`
- `ConnectorId`
- `Identity`
Protected configuration includes IPs, hosts, smart hosts, certificate names, comments, routing metadata, TLS settings, and connector details. Protected configuration must not enter OperationRun context/logs/summaries/customer output.
## Required Final State
For each included type, when all gates pass:
| State | Required value |
|---|---|
| source_contract_verified | true |
| credential_readiness | ready |
| permission_readiness | ready |
| runtime_readiness | ready |
| runner_result | success |
| structured_output_valid | true |
| target_normalizer | target_specific |
| identity_state | stable |
| raw_payload_persisted_in_evidence_store | true |
| normalized_payload_persisted | true |
| payload_hash_persisted | true |
| operation_run_id_linked | true |
| coverage_level | content_backed |
| claim_state | internal_only_or_customer_blocked |
| comparable | false |
| renderable | false |
| certified | false |
| restore_ready | false |
| customer_claimable | false |
When any gate fails:
- no resource row unless repo pattern already created one before the hard-stop and the implementation report proves no evidence was appended
- no evidence row
- exact safe blocker state
- safe OperationRun summary/context only
- no content-backed promotion
- no compare/render/certification/restore/customer claim
PASS WITH CONDITIONS is allowed only if one or two target types become content-backed while another remains fail-closed with exact blocker reason. No fake evidence is allowed.
## Capture Flow
1. Start or receive `tenant_configuration.capture` OperationRun through a trusted flow.
2. Resolve target Exchange command contract.
3. Verify target is in Spec 436 scope.
4. Verify Spec 433 combined readiness.
5. Verify provider scope.
6. Verify runtime readiness.
7. Invoke through Spec 432 runner boundary or fake runner in tests.
8. Convert runner result to Spec 435 structured output envelope.
9. Validate output envelope.
10. Apply Spec 435 target-specific normalizer.
11. Build hash input through Spec 435 hash builder.
12. Extract identity candidates.
13. Run `CanonicalIdentityResolver` or the Spec 434 identity gate that delegates to it.
14. Hard-stop unsafe identity before evidence append.
15. Persist raw structured provider item/envelope in Coverage v2 evidence storage.
16. Persist normalized payload.
17. Persist deterministic hash.
18. Append evidence through Coverage v2 writer with content-only cap.
19. Update safe OperationRun summary/context only.
20. Do not create compare/render/certification/restore/customer state.
No step may bypass OperationRun, readiness, runner boundary, structured envelope, target normalizer, hash builder, identity hard-stop, or content-only guard.
## OperationRun Requirements
Use:
- `OperationRunType::TenantConfigurationCapture` (`tenant_configuration.capture`)
Do not use as evidence owner:
- `OperationRunType::TenantConfigurationExchangePowerShellInvocation` (`tenant_configuration.exchange_powershell_invocation`)
Do not add a new OperationRun type.
OperationRun may store safe context only:
- target resource types
- command contracts
- capture outcomes
- safe blocker reason
- summary counts using allowed `OperationSummaryKeys`
- credential readiness state
- permission readiness state
- runtime readiness state
- `provider_connection_id` if the existing repo pattern stores it in context
OperationRun must not store raw payload, normalized payload, raw stdout, raw stderr, serialized Exchange objects, transcripts, credential material, provider response bodies, mail/message/file content, secret-like metadata, normalizer previews, or hash previews.
## Evidence Persistence Requirements
Use existing Coverage v2 resource/evidence architecture:
- `tenant_configuration_resources`
- `tenant_configuration_resource_evidence`
- `TenantConfigurationResource`
- `TenantConfigurationResourceEvidence`
- `CoverageResourceUpserter`
- `CoverageEvidenceWriter`
Repo-real evidence fields or equivalents:
- `resource_id` as the repo equivalent of tenant configuration resource ID
- `workspace_id`
- `managed_environment_id`
- `provider_connection_id`
- `resource_type_id`
- `operation_run_id`
- `source_contract_key`
- `source_endpoint`
- `source_version`
- `source_schema_hash`
- `source_metadata`
- `raw_payload` jsonb
- `normalized_payload` jsonb
- `payload_hash`
- `permission_context`
- `evidence_state`
- `coverage_level`
- `capture_outcome`
- `captured_at`
Persisted `capture_outcome` values must use existing repo-allowed evidence outcome values. Adapter-only safe outcome strings may remain service/result context, but must not be persisted to evidence rows unless the spec is amended before implementation.
Repo-real resource fields or equivalents:
- `latest_evidence_id`
- `latest_evidence_state`
- `latest_identity_state`
- `latest_claim_state`
- `latest_payload_hash`
- `latest_captured_at`
- canonical/source identity fields and diagnostics
No `tenant_id`.
### Append-Only Rule
Allowed:
- insert new evidence row
- update `latest_evidence_id` pointer according to repo pattern
- derive latest evidence by `latest_evidence_id` or `captured_at`
Forbidden:
- overwrite prior evidence payload
- mutate prior payload hash
- delete previous evidence
- replace evidence in place
### Raw Payload Rule
Raw provider payload means structured provider item/envelope from the Spec 435 output path. It never means raw stdout, raw stderr, PowerShell transcript, terminal output, or serialized shell output.
Raw provider payload may exist only in Coverage v2 evidence storage.
Forbidden locations:
- OperationRun context
- logs
- summary counts
- UI
- reports
- Review Packs
- customer output
- exception messages
## Normalization Requirements
Spec 436 must use Spec 435 target-specific evidence normalizers:
- `ExchangeTransportRuleEvidenceNormalizer`
- `ExchangeRemoteDomainEvidenceNormalizer`
- `ExchangeInboundConnectorEvidenceNormalizer`
- `ExchangePowerShellEvidenceNormalizer`
- `ExchangePowerShellTargetEvidenceNormalizer`
Forbidden normalizers for Exchange evidence promotion:
- `GenericPayloadNormalizer`
- `ExchangeTeamsComparablePayloadNormalizer`
- generic Teams comparable normalizer
- compare/render normalizers
Every normalized payload must include or map to:
- resource type
- workload/source surface
- source contract metadata
- command contract metadata
- source identity
- canonical identity candidate
- payload shape version
- normalizer version
- captured-at context where repo pattern places it outside hash input
Volatile fields must be excluded or demoted. Collection items must be sorted deterministically by stable identity key. Provider ordering must not affect payload hash unless ordering is semantically meaningful.
## Hashing Requirements
Spec 436 must use `ExchangePowerShellHashInputBuilder`.
Required behavior:
- same material normalized payload produces same hash
- material change produces different hash
- volatile field change produces same hash
- provider order changes do not change hash when order is not meaningful
- target type is included in hash context
- normalizer version is included or handled according to repo pattern
Hash must not be based on raw stdout, raw stderr, OperationRun context, runtime timestamps, PowerShell session metadata, credential metadata, or permission metadata.
Hash is persisted only with evidence row unless repo policy already explicitly allows safe hash context.
## Identity Requirements
Spec 436 must use `CanonicalIdentityResolver` directly or via the existing Spec 434 identity gate, and tests must prove the resolver is used before evidence append.
Stable identity is required by default.
Blocked identity states:
- `identity_conflict`
- `missing_external_id`
- `unsupported_identity`
- `display_name_only`
- `duplicate_identity`
- `derived_only_unproven`
Derived identity is blocked by default unless Spec 417 rules explicitly allow it for non-certifying content-backed evidence and target-specific tests prove no ambiguity.
Tests must prove display-name-only, duplicate, conflicting, missing, and unsafe identities do not call the writer and do not create evidence rows.
## Empty Collection Policy
Spec 436 preserves the Spec 434/435 empty-collection policy:
- safe zero-item OperationRun summary allowed
- no resource row
- no evidence row
- no content-backed resource evidence
- durable collection-level empty proof deferred
Permission denied, source unavailable, malformed output, and partial output must never be treated as empty collection.
Forbidden:
- `fake_empty_success`
- fake resource row
- fake evidence row
- empty collection marked as content-backed resource
## Capture Outcomes
Spec 436 may expose safe adapter/result outcomes equivalent to:
- `capture_succeeded_content_backed`
- `capture_succeeded_zero_items_no_resource_evidence`
- `capture_blocked_contract_not_verified`
- `capture_blocked_missing_credential_readiness`
- `capture_blocked_missing_permission_readiness`
- `capture_blocked_runtime_not_ready`
- `capture_blocked_provider_scope`
- `capture_blocked_identity_unsafe`
- `capture_blocked_response_shape_unsafe`
- `capture_blocked_redaction_unsafe`
- `capture_blocked_normalizer_unready`
- `capture_blocked_hash_unready`
- `capture_failed_authentication`
- `capture_failed_authorization`
- `capture_failed_permission`
- `capture_failed_timeout`
- `capture_failed_throttled`
- `capture_failed_output_too_large`
- `capture_failed_output_encoding_unsafe`
- `capture_failed_shape_unsafe`
- `capture_failed_unexpected`
Persisted evidence `capture_outcome` must remain within existing repo enum/constraint values unless this spec is amended before runtime implementation. Existing adapter-level safe outcome strings may be returned in service results and OperationRun safe context only when tested and sanitized.
Implementation tests must prove both successful and blocked captures either persist only repo-allowed `capture_outcome` values or create no evidence row. Adapter-only outcome labels must not leak into persisted evidence when no matching repo value exists.
Forbidden outcomes:
- `fake_empty_success`
- `meta_fallback`
- `policy_record_missing`
- `foundation_not_policy_backed`
- `raw_gap_count`
- `primary_gap_count`
- `customer_ready`
- `certified`
- `renderable`
- `comparable`
## Redaction Requirements
Never persist outside evidence storage:
- raw provider payload
- raw stdout
- raw stderr
- serialized Exchange object
- PowerShell transcript
- credential material
- certificate material
- client secret
- token
- authorization header
- cookie
- mail body
- message content
- file content
OperationRun context must contain only safe states/counts. Logs must not contain raw provider payload. Reports, Review Packs, PDF, UI, and customer output remain unchanged.
## Data / Migration Impact
Default: no migration.
Spec 436 must use existing Coverage v2 evidence/resource tables.
Forbidden:
- Exchange-specific evidence table
- `tenant_id` column
- raw payload table outside existing evidence architecture
- customer output table
- UI state table
- legacy snapshot table
If migration is required, stop and amend the spec before implementation.
## Likely Affected Files
Spec artifacts:
- `specs/436-exchange-content-backed-evidence-promotion-slice-1/spec.md`
- `specs/436-exchange-content-backed-evidence-promotion-slice-1/plan.md`
- `specs/436-exchange-content-backed-evidence-promotion-slice-1/tasks.md`
- `specs/436-exchange-content-backed-evidence-promotion-slice-1/checklists/requirements.md`
- `specs/436-exchange-content-backed-evidence-promotion-slice-1/implementation-report.md`
Likely runtime files:
- `apps/platform/app/Services/TenantConfiguration/ExchangePowerShellEvidenceCaptureAdapter.php`
- `apps/platform/app/Services/TenantConfiguration/ExchangePowerShellCaptureEligibilityGate.php`
- `apps/platform/app/Services/TenantConfiguration/ExchangePowerShellEvidenceNormalizer.php`
- `apps/platform/app/Services/TenantConfiguration/ExchangePowerShellTargetEvidenceNormalizer.php`
- `apps/platform/app/Services/TenantConfiguration/ExchangePowerShellHashInputBuilder.php`
- `apps/platform/app/Services/TenantConfiguration/ExchangePowerShellStructuredOutputEnvelope.php`
- `apps/platform/app/Services/TenantConfiguration/ExchangePowerShellProductionRunner.php`
- `apps/platform/app/Services/TenantConfiguration/ExchangePowerShellOutputGuard.php`
- `apps/platform/app/Services/TenantConfiguration/CoverageEvidenceWriter.php`
- `apps/platform/app/Services/TenantConfiguration/CoverageResourceUpserter.php`
- `apps/platform/app/Services/TenantConfiguration/CanonicalIdentityResolver.php`
- `apps/platform/app/Support/OpsUx/OperationSummaryKeys.php` only if new summary keys are unavoidable and the spec is amended
Likely new test files or repo-canonical equivalents:
- `apps/platform/tests/Unit/Support/TenantConfiguration/Spec436ExchangeEvidencePromotionTest.php`
- `apps/platform/tests/Feature/TenantConfiguration/Spec436ExchangeContentBackedEvidenceFeatureTest.php`
Files that must not change without spec amendment:
- `apps/platform/routes/**`
- `apps/platform/app/Filament/**`
- `apps/platform/app/Livewire/**`
- `apps/platform/resources/views/**`
- `apps/platform/app/Jobs/**`
- `apps/platform/app/Listeners/**`
- `apps/platform/app/Console/**`
- `apps/platform/database/migrations/**`
- customer report / review-pack code
- `apps/platform/app/Support/OperationRunType.php` for adding a new type
- `apps/platform/app/Support/OperationCatalog.php` for adding a new operation type
## User Scenarios And Testing
### User Story 1 - Content-Backed transportRule Evidence (Priority: P1)
As a platform reviewer, I need successful gated `Get-TransportRule` output to append content-backed evidence, so the first Exchange target has source-backed internal proof without product overclaim.
**Independent Test**: Fake a successful structured `transportRule` collection through the Exchange adapter and assert one resource row, one evidence row, raw payload in evidence storage only, normalized target-specific payload, deterministic hash, linked OperationRun, content-backed coverage only, and sanitized OperationRun context.
### User Story 2 - Content-Backed remoteDomain Evidence With Stable Identity Only (Priority: P1)
As a platform reviewer, I need `remoteDomain` evidence to be created only when stable source identity is present, so domain/display-name-only payloads cannot become ambiguous evidence.
**Independent Test**: Fake stable `remoteDomain` output and assert content-backed evidence; fake domain-only, display-only, missing, duplicate, or conflicting identity and assert no writer call and no evidence row.
### User Story 3 - Content-Backed inboundConnector Evidence With Redaction Safety (Priority: P1)
As a platform reviewer, I need `inboundConnector` evidence to preserve raw source configuration only in evidence storage and keep protected routing/config data out of OperationRun/logs/summaries.
**Independent Test**: Fake stable `inboundConnector` output with protected config and assert content-backed evidence plus redaction-safe context/log behavior; fake missing/duplicate identity or redaction-unsafe output and assert no evidence.
### User Story 4 - Fail-Closed Blocking And No-Promotion Safety (Priority: P1)
As a release reviewer, I need every readiness, output, identity, empty, and failure state to fail closed without fake evidence or product promotion.
**Independent Test**: Run focused unit/feature tests for missing credential readiness, client secret, missing/stale/wrong-scope permission, runtime readiness, provider scope, auth/permission/source/output failures, malformed/scalar/warning/binary/oversized/non-zero/timeout output, unsafe identity, empty collection, no UI/triggers, no `tenant_id`, and no compare/render/certification/restore/customer state.
## Non-Functional Requirements
- **NFR-436-001**: Evidence promotion must be deterministic. Equivalent normalized payloads must produce equivalent hashes independent of provider item order when ordering is not meaningful.
- **NFR-436-002**: Evidence promotion must be fail-closed. Any readiness, output, identity, redaction, hash, scope, or runtime uncertainty must create no evidence row.
- **NFR-436-003**: Sensitive Exchange/provider data must remain minimized. Raw structured provider payload is allowed only in Coverage v2 evidence storage; OperationRun context, logs, summaries, reports, UI, and customer output must remain sanitized.
- **NFR-436-004**: The implementation must not add any rendered UI, trigger, migration, queue/scheduler surface, customer output, or deployment/runtime dependency.
- **NFR-436-005**: Test additions must stay in the narrow Unit/Feature lane unless a spec amendment justifies broader PostgreSQL/browser/heavy-governance coverage.
## Functional Requirements
- **FR-436-001**: Runtime MUST use `tenant_configuration.capture` as the evidence-owning OperationRun and MUST NOT add a new OperationRun type.
- **FR-436-002**: Runtime MUST keep `tenant_configuration.exchange_powershell_invocation` as runner/invocation truth only.
- **FR-436-003**: Runtime MUST scope Exchange promotion to `transportRule`, `remoteDomain`, and `inboundConnector`.
- **FR-436-004**: Runtime MUST use Spec 435 structured output envelope before normalization or evidence append.
- **FR-436-005**: Runtime MUST use Spec 435 target-specific normalizers and hash builder for Exchange evidence promotion.
- **FR-436-006**: Runtime MUST NOT use `GenericPayloadNormalizer` or `ExchangeTeamsComparablePayloadNormalizer` for Exchange evidence promotion.
- **FR-436-007**: Runtime MUST require Spec 433 combined readiness and provider capability support before runner/capture success can append evidence.
- **FR-436-008**: Runtime MUST require Spec 432 runner boundary and accepted structured collection output.
- **FR-436-009**: Runtime MUST require Spec 430 verified command contracts.
- **FR-436-010**: Runtime MUST use `CanonicalIdentityResolver` or the Spec 434 identity gate before resource upsert/evidence append.
- **FR-436-011**: Unsafe identity MUST create no evidence row and MUST not call the evidence writer.
- **FR-436-012**: Empty collection MUST create a zero-item safe OperationRun summary and no resource/evidence row.
- **FR-436-013**: Raw payload MUST mean structured provider item/envelope and MUST be persisted only in Coverage v2 evidence storage.
- **FR-436-014**: Raw stdout/stderr/transcripts MUST NOT be evidence and MUST NOT enter OperationRun context, logs, summaries, UI, reports, Review Packs, or customer output.
- **FR-436-015**: Normalized payload and payload hash MUST be deterministic and persisted on each evidence row.
- **FR-436-016**: Evidence rows MUST be append-only and previous payload/hash values MUST NOT be mutated or deleted.
- **FR-436-017**: Latest evidence pointer MUST update according to existing repo pattern.
- **FR-436-018**: Evidence coverage level MUST remain capped at `content_backed`.
- **FR-436-019**: Resource claim state MUST remain internal-only/customer-blocked according to existing repo pattern.
- **FR-436-020**: Runtime MUST not create comparable, renderable, certified, restore-ready, customer-ready, report, Review Pack, PDF, UI, route, job, schedule, listener, migration, `tenant_id`, Graph fallback, fake endpoint, legacy shim, fallback reader, or Exchange mini-platform state.
- **FR-436-021**: OperationRun context and summary counts MUST use safe allowed keys only and MUST exclude raw payloads, normalized payloads, normalizer previews, hash previews, credential material, provider response bodies, stdout/stderr, and protected Exchange config.
- **FR-436-022**: Implementation report MUST include prerequisite proof, target outcomes, evidence persistence proof, append-only/latest-pointer proof, raw payload location proof, structured envelope proof, normalizer/hash/identity/readiness proof, failure blocking proof, redaction proof, no-generic/no-comparable-normalizer proof, no Graph gateway proof, no promotion/customer/restore proof, no UI/trigger proof, no `tenant_id` proof, tests run, and deferred work.
## Acceptance Criteria
### Target Scope
- Only `transportRule`, `remoteDomain`, and `inboundConnector` are in scope.
- No outboundConnector, acceptedDomain, organizationConfig, Teams, mutation commands, restore commands, or customer-facing surfaces.
### Evidence Capture
- Successful gated capture creates append-only content-backed evidence.
- Raw payload is stored only in evidence storage.
- Normalized payload and deterministic hash are stored.
- Persisted `capture_outcome` uses an existing repo-allowed evidence outcome value.
- OperationRun, workspace, managed environment, and provider connection scope are linked.
- Coverage maximum is `content_backed`.
- Claim state remains internal-only/customer-blocked.
### Target Normalization
- `transportRule`, `remoteDomain`, and `inboundConnector` use Spec 435 target normalizers.
- `GenericPayloadNormalizer` is not used for Exchange evidence promotion.
- `ExchangeTeamsComparablePayloadNormalizer` is not used for Exchange evidence promotion.
- Volatile fields are excluded/demoted.
- Collection order is deterministic.
- Sensitive fields are handled according to policy.
### Identity
- `CanonicalIdentityResolver` is used.
- Stable identity is required by default.
- Display-name-only, duplicate, conflicting, missing, remoteDomain domain-only, and inboundConnector missing connector identity block.
- Unsafe identity creates no evidence.
### Blocking
- Missing credential readiness, client secret, missing/stale/wrong-scope permission, unsupported provider capability, runtime readiness failure, provider scope failure, auth/permission/source/output failures, malformed output, unsafe identity, and empty collection create no evidence row.
- Permission denied, source unavailable, malformed output, and partial output are never treated as empty collection.
### OperationRun Safety
- `tenant_configuration.capture` is evidence owner.
- Exchange invocation operation remains runner truth only.
- No new OperationRun type.
- OperationRun context has no raw payload, normalized payload, normalizer/hash preview, raw stdout/stderr, credential material, or protected Exchange config.
- Summary counts use allowed keys or the spec is amended before adding keys.
### Redaction
- Logs have no raw payload.
- Summary counts have no raw payload.
- Credential material is absent.
- Raw provider payload exists only in evidence store.
- No customer/report/review-pack output.
### No Product Promotion
- No comparable, renderable, certified, restore-ready, customer-ready, report, Review Pack, PDF, or customer claim state appears.
### Product Surface / Trigger
- No route, Filament page, Livewire component, navigation, asset, global search, provider registration, destructive UI action, job trigger, schedule trigger, listener trigger, or browser path.
### Architecture
- Uses Coverage v2 evidence architecture.
- Uses Spec 434 adapter/guards.
- Uses Spec 435 normalizers/hash builder.
- Uses Spec 433 readiness evaluator.
- Uses Spec 432 runner boundary.
- No Graph gateway fallback, fake Graph endpoint, Exchange-specific evidence table, `tenant_id`, Exchange mini-platform, legacy shim, fallback reader, or migration.
### Validation
- Focused unit tests pass.
- Focused feature tests pass.
- Required regressions pass.
- Pint passes.
- `git diff --check` passes.
- `git status --short` is documented.
## Implementation Report Requirements
Implementation report must include:
- Candidate gate result.
- Branch and HEAD.
- Dirty state before/after.
- Files changed.
- Spec 435 prerequisite proof.
- Target type outcomes.
- OperationRun proof.
- Evidence persistence proof.
- Append-only proof.
- Latest evidence pointer proof.
- Raw payload location proof.
- Structured envelope proof.
- Normalizer integration proof.
- Hash proof.
- Identity proof.
- Credential readiness proof.
- Permission readiness proof.
- Runtime readiness proof.
- Failure blocking proof.
- Redaction proof.
- No `GenericPayloadNormalizer` proof.
- No `ExchangeTeamsComparablePayloadNormalizer` proof.
- No Graph gateway/fake endpoint proof.
- No compare/render/certification proof.
- No restore/customer claim proof.
- No UI/route/job/schedule/listener proof.
- No `tenant_id` proof.
- No mini-platform proof.
- Tests run.
- Deferred work.
Required target matrix:
| Type | Command | Capture Outcome | Evidence? | Blocker |
|---|---|---|---|---|
| transportRule | Get-TransportRule | captured | yes, content-backed only | none |
| remoteDomain | Get-RemoteDomain | captured | yes, content-backed only | domain-only, display-only, duplicate, and conflicting identities block |
| inboundConnector | Get-InboundConnector | captured | yes, content-backed only | missing and unsafe identities block |
Required evidence matrix:
| Type | Raw Payload | Normalized Payload | Hash | Identity | OperationRun | Content-backed? |
|---|---|---|---|---|---|---|
| transportRule | evidence row only | target normalizer output plus source metadata | Spec435 hash builder over persisted normalized payload | stable source identity required | tenant_configuration.capture linked | yes |
| remoteDomain | evidence row only | target normalizer output plus source metadata | Spec435 hash builder over persisted normalized payload | stable source identity required | tenant_configuration.capture linked | yes |
| inboundConnector | evidence row only | target normalizer output plus source metadata | Spec435 hash builder over persisted normalized payload | stable source identity required | tenant_configuration.capture linked | yes |
Required no-promotion matrix:
| Area | State |
|---|---|
| Compare | No |
| Render | No |
| Certification | No |
| Restore | No |
| Customer claim | No |
| UI | No |
| Jobs/Schedules/Listeners | No |
## Risks
- **R-436-001**: Current adapter uses generic/comparable normalizer dependencies. Mitigation: make replacing/bypassing those paths a hard implementation task and test assertion.
- **R-436-002**: Existing evidence writer can promote renderable coverage if summary builders match. Mitigation: keep Spec 434 content-only cap and assert resulting evidence/resource states.
- **R-436-003**: OperationRun context could accidentally store protected Exchange config. Mitigation: add redaction tests and context/log guard tests for protected config sentinels.
- **R-436-004**: Empty collection could be mistaken for durable proof. Mitigation: keep zero-item summary only and assert no resource/evidence rows.
- **R-436-005**: New outcome strings could conflict with DB constraints. Mitigation: persisted `capture_outcome` stays repo-canonical unless spec is amended.
## Assumptions
- Spec 435 is merge-ready and present at HEAD before implementation.
- Existing Coverage v2 tables are sufficient.
- Existing OperationRun summary keys are sufficient, or implementation stops to amend the spec before adding keys.
- Existing fake runner/test fixtures are sufficient to prove promotion without live Exchange calls.
- `tenant_configuration.capture` remains the canonical evidence-owning OperationRun type.
## Open Questions
None blocking preparation. Runtime implementation must stop and amend if it needs a migration, new summary key, new persisted outcome, new OperationRun type, UI/trigger surface, target expansion, or durable empty-collection proof.
## Follow-Up Spec Candidates
- Spec 437 - Exchange Comparable / Renderable Promotion Slice 1.
- Teams PowerShell Adapter Contract Slice 1.
- Exchange/Teams Certified Core Compare Pack.
- M365 Customer Output & Claim Guard.
## Candidate Selection Gate
PASS. The selected candidate is directly provided by the user, not covered by an existing active or completed Spec 436 package, follows merge-ready Spec 435, aligns with the corrected Exchange roadmap sequence, is narrow enough for a bounded implementation loop, and defers comparable/renderable/customer/product surfaces.
## Spec Readiness Gate
PASS for preparation. `spec.md`, `plan.md`, `tasks.md`, `checklists/requirements.md`, and `implementation-report.md` define a bounded, testable, no-UI, no-migration, no-application-implementation package. Runtime implementation must re-check repo state, Spec 435 merge-readiness, and all hard gates before changing application code.
## Final Candidate Gate
PASS requires:
- Spec 435 is merge-ready.
- Only `transportRule`, `remoteDomain`, and `inboundConnector` are in scope.
- Adapter uses Spec 435 structured envelope, target normalizers, and hash builder.
- `GenericPayloadNormalizer` is not used for Exchange evidence.
- `ExchangeTeamsComparablePayloadNormalizer` is not used for Exchange evidence.
- Evidence owner is `tenant_configuration.capture`.
- Exchange invocation operation remains runner truth only.
- Successful eligible capture creates append-only content-backed evidence.
- Raw payload is persisted only in evidence storage.
- Raw stdout/stderr is not evidence.
- Normalized payload and deterministic hash are persisted.
- `CanonicalIdentityResolver` is used.
- Unsafe identity creates no evidence.
- Blocked states create no evidence.
- Empty collection creates no fake evidence.
- Coverage remains `content_backed` only.
- No compare/render/certification/restore/customer state appears.
- No UI/routes/jobs/schedules/listeners are added.
- No migration is added.
- No `tenant_id` ownership truth is introduced.
- No Exchange mini-platform appears.
- Focused tests and regressions pass.
PASS WITH CONDITIONS is allowed only for bounded follow-ups that do not weaken evidence integrity, identity safety, normalization determinism, redaction, readiness gating, no-promotion posture, no-product-surface posture, ownership, or no-mini-platform posture.
FAIL if credential/permission gates can be bypassed, fake evidence is created, permission denied is treated as empty data, malformed output is treated as success, unsafe identity creates evidence, raw stdout/stderr is treated as evidence, raw payload leaks outside evidence storage, OperationRun stores raw provider output or normalizer/hash preview, coverage promotes to comparable/renderable/certified, restore/customer claims appear, UI/routes/jobs/schedules/listeners are added, migration is added without amendment, `tenant_id` ownership truth is introduced, an Exchange-specific evidence table appears, or tests do not prove content-backed evidence safety.