## Summary - add Spec 437 package for Exchange comparable promotion slice 1 - add ExchangePowerShellComparablePayloadBuilder for normalized-payload-only comparable artifacts - add focused Pest unit/feature coverage for comparable safety, determinism, redaction, and no product-surface promotion ## Validation - cd apps/platform && ./vendor/bin/sail bin pint --dirty --format agent: PASS - cd apps/platform && ./vendor/bin/sail artisan test --filter=Spec437 --compact: failed with Symfony Process signal 9 - cd apps/platform && php artisan test --filter=Spec437 --compact: PASS, 34 tests / 225 assertions - git diff --check: PASS ## Product Surface / Deployment - UI impact: N/A - no rendered UI surface changed - Browser proof: N/A - no rendered UI surface changed - Destructive/high-impact actions: none - Assets: none; no filament:assets requirement introduced - Deployment impact: no env vars, migrations, queues, scheduler, storage, assets, routes, jobs, listeners, commands, or Dokploy runtime behavior changes Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de> Reviewed-on: #504
54 KiB
Feature Specification: Spec 437 - Exchange Comparable Promotion Slice 1
Feature Branch: feat/437-exchange-comparable-promotion-slice-1
Created: 2026-07-09
Status: Draft
Type: Coverage v2 / Microsoft 365 / Exchange / Comparable Payloads / Backend-only
Priority: P0
Risk: High
Mode: Comparable-only promotion for the first Exchange PowerShell content-backed slice; no renderable, no certification, no restore, no UI, no customer claims
Input: User-provided corrected Spec 437 draft for comparable-only Exchange promotion after Spec 436.
Preparation Selection Summary
- Selected candidate: Spec 437 - Exchange Comparable Promotion Slice 1.
- Source location: Direct user-provided corrected Spec 437 draft in this session.
- Why selected:
docs/product/spec-candidates.mdrecords no safe automatic next-best-prep target, but the user provided the next bounded Exchange roadmap slice. Spec 436 is present at HEAD and records PASS for content-backed evidence promotion oftransportRule,remoteDomain, andinboundConnector. - Close alternatives deferred: Spec 438 internal render model, Teams slices, certification, restore/apply, customer output, Review Packs, reports, PDFs, UI/read-model output, jobs, schedules, listeners, target expansion, and Exchange-specific compare persistence remain deferred.
- Roadmap relationship: Continues the corrected Exchange sequence: Specs 429-436 establish source catalog, adapter contracts, invocation, runner boundary, credential/permission readiness, evidence capture guard, structured output/normalizers, and content-backed evidence. Spec 437 may prove only comparable payloads for the three Spec 436 Exchange targets.
- Completed-spec guardrail result: Specs 430-436 are completed or implementation-closed read-only context and were not modified during preparation. No existing
specs/437-*package or branch was found before preparation. Spec 422 is completed historical context only and is not reused because it promoted broader Exchange/Teams comparable/renderable behavior from generic content-backed rows, while Spec 437 is comparable-only from Spec 436 Exchange PowerShell normalized evidence. - Smallest viable implementation slice: Add an Exchange PowerShell comparable payload builder for exactly
transportRule,remoteDomain, andinboundConnector; derive deterministic machine-comparable payloads only from immutable Spec 436normalized_payload; keep evidence rows immutable; add no renderable/customer/product/trigger/persistence expansion. - Feature description fed into Spec Kit: Derive deterministic machine-comparable Exchange payloads for
transportRule,remoteDomain, andinboundConnectorfrom immutable Spec 436 normalized content-backed evidence only, with protected values represented by redacted presence/count/safe-state semantics and no raw payload, stdout, stderr, transcript, OperationRun context, renderable output, certification, restore, UI, routes, jobs, schedules, listeners, migrations, customer output, reports, Review Packs, PDFs,tenant_idownership truth, or Exchange-specific compare table. - Candidate Selection Gate: PASS for a directly provided candidate, scoped to a comparable-only backend slice.
Spec Candidate Check
- Problem: Spec 436 can persist safe content-backed Exchange evidence, but TenantPilot still cannot derive deterministic comparable payloads for the three first Exchange PowerShell targets without risking raw payload or protected configuration leakage.
- Today's failure: Exchange evidence rows can be content-backed, but any compare work would either be absent, rely on generic/older ExchangeTeams compare paths, or risk using raw provider payloads, raw stdout/stderr/transcripts, OperationRun context, domain-only identities, or protected routing/config values.
- User-visible improvement: Future operators and reviewers get honest internal compare readiness: selected Exchange evidence can become machine-comparable without implying renderability, certification, restore, customer readiness, reports, or UI.
- Smallest enterprise-capable version: Comparable builder output for exactly
transportRule,remoteDomain, andinboundConnector, consuming only Spec 436normalized_payloadas material input, returning deterministic machine payloads or explicit blockers. - Explicit non-goals: No render model, no UI, no read-model output, no certification, no restore/apply, no reports, no Review Packs, no PDFs, no customer claims, no Teams, no additional Exchange types, no provider calls, no evidence creation, no migration, no new OperationRun type, no
tenant_id, no Exchange-specific compare table. - Permanent complexity imported: One bounded Exchange PowerShell comparable builder and optional comparator tests. No new persisted entity/table/artifact, no persisted coverage-level promotion, no new enum/status family, no UI framework, no customer-output path, no trigger surface.
- Why now: Spec 436 is PASS at HEAD and is the prerequisite for comparable promotion. Spec 438 render work must not start until comparable payload safety is proven.
- Why not local: Compare safety must be explicit and target-specific because these Exchange types include mail-flow rules, domains, and connector routing/TLS configuration. Reusing generic payload comparison or the old ExchangeTeams comparable normalizer would not enforce Spec 436 normalized-payload-only input and protected-value semantics.
- Approval class: Core Enterprise.
- Red flags triggered: New builder/comparator helper. Defense: the helper is bounded to three concrete resource types, protects evidence/redaction truth, and prevents unsafe customer/product overclaim. No persistence or status family is added.
- Score: Nutzen: 2 | Dringlichkeit: 2 | Scope: 2 | Komplexitaet: 1 | Produktnaehe: 1 | Wiederverwendung: 2 | Gesamt: 10/12
- Decision: approve as a comparable-only backend safety slice.
Spec Scope Fields
- Scope: Workspace + managed-environment scoped Coverage v2 evidence, derived comparable payload behavior only.
- Primary Routes: N/A - no route, page, controller, API route, Filament page/resource/widget, Livewire component, view, report, PDF, or dashboard change.
- Data Ownership: Existing
TenantConfigurationResource,TenantConfigurationResourceEvidence,TenantConfigurationResourceType,OperationRun, and same-scopeProviderConnectionrecords only as read inputs. No new table or ownership column. Comparable state is not persisted intoTenantConfigurationResourceEvidence.coverage_level,TenantConfigurationResource.latest_*, orTenantConfigurationResourceType.default_coverage_level. Provider-native tenant identifiers remain metadata and MUST NOT become platform ownership truth. - RBAC: No new authorization surface or mutating action. Implementation tests must still prove no route/UI/job/customer output is introduced and comparable derivation does not bypass existing evidence scope assumptions.
For canonical-view specs:
- Default filter behavior when tenant-context is active: N/A - no canonical route or rendered view.
- Explicit entitlement checks preventing cross-tenant leakage: Comparable derivation must operate on explicitly provided, already scoped evidence records; no fallback-to-latest evidence or tenant/provider-native ID ownership may be introduced.
No Legacy / No Backward Compatibility Constraint
TenantPilot is pre-production unless this spec explicitly records a compatibility exception.
- Compatibility posture: canonical bounded addition over Spec 436 normalized evidence.
- Legacy aliases, fallback readers, hidden routes, duplicate UI, old labels, or historical fixtures kept?: no.
- Why clean replacement is safe now: Spec 437 does not replace user-facing behavior or persisted customer truth. It forbids legacy adapters, fallback readers, dual writes, Coverage v1 vocabulary, generic Exchange compare fallback, and old customer-facing coverage claims.
UI Surface Impact
Does this spec add, remove, rename, or materially change any reachable UI surface?
- 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
No-impact rationale: Spec 437 is backend comparable-payload preparation and implementation only. It forbids routes, Filament pages/resources/widgets, Livewire components, Blade views, navigation, assets, global search, dashboards, read-model rendering, reports, downloads, Review Packs, PDFs, and customer output.
UI/Productization Coverage
N/A - no reachable UI surface impact. No route inventory, design coverage, page report, screenshot, or browser artifact is required unless the spec is amended or split.
Product Surface Impact
Reference: docs/product/standards/product-surface-contract.md.
- Product Surface Contract applies?: no rendered product surface changes. The contract is recorded as an explicit no-impact gate because this spec touches evidence/comparable semantics that must not leak into UI or customer output.
- 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 at runtime. Spec 437 forbids OperationRun links, raw evidence IDs, raw payloads, provider diagnostics, source keys, reports, PDFs, and rendered technical output.
- Canonical status vocabulary: N/A - no product-facing status vocabulary changes. Any internal derived comparable outcome must not become a customer/product state.
- 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:
specs/437-exchange-comparable-promotion-slice-1/implementation-report.mdmust recordN/A - no rendered UI surface changed.
Product Surface Merge Gate Checklist
- No-legacy posture or approved exception recorded.
- Product Surface Impact is completed with
N/Arationale. - Browser proof is
N/A - no rendered UI surface changed. - Human Product Sanity is not applicable with rationale.
- Product Surface exceptions are
none. - 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/compare semantics only.
- Interaction class(es): Coverage v2 evidence comparable payload derivation; no operator-facing interaction class.
- Systems touched: Existing Coverage v2 evidence storage, Spec 436 normalized evidence, target-specific Exchange normalizer output, hash/provenance metadata.
- Existing pattern(s) to extend: Existing TenantConfiguration services under
apps/platform/app/Services/TenantConfiguration/, Entra comparable/comparator precedent as implementation context, Spec 436 Exchange PowerShell evidence normalizer as source input. - Shared contract / presenter / builder / renderer to reuse: Reuse existing normalized evidence and hash/provenance metadata. Do not reuse
GenericPayloadNormalizerorExchangeTeamsComparablePayloadNormalizerfor Spec 436 Exchange PowerShell evidence. - Why the existing shared path is sufficient or insufficient: Existing Coverage v2 evidence rows are sufficient as source truth. Existing generic/ExchangeTeams compare paths are insufficient because Spec 437 must consume only Spec 436
normalized_payloadand must block protected exact values by target. - Allowed deviation and why: Add bounded provider-specific comparable builder only if implemented as the narrowest safe current-release shape for the three concrete Exchange targets.
- Consistency impact: Comparable output remains machine-readable internal payload, not renderable or customer prose.
- Review focus: Verify no raw payload, stdout/stderr/transcript, OperationRun context, report output, Review Pack output, or UI read model is accepted as builder input.
OperationRun UX Impact
- Touches OperationRun start/completion/link UX?: no.
- Shared OperationRun UX contract/layer reused: N/A.
- Delegated start/completion UX behaviors: N/A.
- Local surface-owned behavior that remains: none.
- Queued DB-notification policy: N/A.
- Terminal notification path: N/A.
- Exception required?: none.
OperationRun requirements for implementation:
- No new OperationRun type.
tenant_configuration.captureremains evidence provenance for Spec 436 rows.tenant_configuration.exchange_powershell_invocationremains runner truth only and must not own comparable output.- OperationRun context must not be compare input and must not store raw payload, comparable payload, deltas, rendered payload, stdout, stderr, transcripts, protected config, or customer-facing text.
Provider Boundary / Platform Core Check
- Shared provider/platform boundary touched?: yes, provider-owned Exchange PowerShell compare semantics over platform-owned Coverage v2 evidence.
- Boundary classification: mixed, with provider-specific logic bounded inside TenantConfiguration/Exchange PowerShell services and platform-core ownership unchanged.
- Seams affected: comparable payload derivation for Exchange PowerShell evidence; no registry expansion, no source contract expansion, no route/UI/customer seam.
- Neutral platform terms preserved or introduced: evidence, comparable payload, normalized payload, resource type, workload, provider connection, managed environment, operation run provenance.
- Provider-specific semantics retained and why:
transportRule,remoteDomain,inboundConnector, Exchange PowerShell command/source metadata, and target redaction semantics are required for this bounded slice. - Why this does not deepen provider coupling accidentally: Provider-specific compare logic is local to Exchange PowerShell content-backed evidence and does not alter platform-core ownership, source-of-truth fields, global status vocabulary, UI, customer output, or persistence schema.
- Follow-up path: Spec 438 may introduce internal render models only after its own preflight and Product Surface/read-model risk review.
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 comparable payload builder | no | N/A | backend evidence/compare only | none | no | N/A - no operator-facing surface change |
Decision-First Surface Role
N/A - no operator-facing surface changed.
Audience-Aware Disclosure
N/A - no operator-facing or customer/auditor surface changed. Customer output remains unchanged and forbidden.
UI/UX Surface Classification
N/A - no operator-facing surface changed.
Operator Surface Contract
N/A - no operator-facing surface changed.
Proportionality Review
- New source of truth?: no. Comparable payloads are derived from immutable Spec 436 evidence.
- New persisted entity/table/artifact?: no. Spec 437 comparable output is derived service/builder output only; it does not persist a comparable artifact or promote persisted evidence/resource/resource-type coverage state.
- New abstraction?: yes, a bounded Exchange PowerShell comparable payload builder, and optionally a bounded comparator if repo architecture needs it.
- New enum/state/reason family?: no. Do not add customer/product status families. Delta classifications are local machine categories only if a comparator is added.
- New cross-domain UI framework/taxonomy?: no.
- Current operator problem: Future compare/render/certification work is blocked until selected Exchange evidence can be compared without leaking protected mail-flow/routing configuration or overclaiming product readiness.
- Existing structure is insufficient because:
GenericPayloadNormalizerandExchangeTeamsComparablePayloadNormalizerdo not enforce Spec 436 normalized-payload-only input for these Exchange PowerShell evidence rows and do not encode the corrected target-specific protected-value constraints. - Narrowest correct implementation: One local builder for exactly three concrete target types, using the existing Spec 436 normalized payload shape and no new persistence.
- Ownership cost: Focused service tests, feature guard tests, and future maintenance when Spec 438 render work consumes comparable output.
- Alternative intentionally rejected: Comparing raw payloads or reusing the generic/ExchangeTeams normalizer. Both would weaken redaction, input-truth, and no-overclaim gates.
- Release truth: Current-release prerequisite for the immediate Exchange roadmap, not future-release speculation.
Compatibility posture
This feature assumes a pre-production environment. Backward compatibility, legacy aliases, migration shims, fallback readers, and compatibility-specific tests are out of scope.
Testing / Lane / Runtime Impact
- Test purpose / classification: Unit for deterministic builders and comparator semantics; Feature for evidence immutability/no-surface/no-tenant/no-OperationRun-type guards.
- Validation lane(s): fast-feedback and confidence. Browser is N/A.
- Why this classification and these lanes are sufficient: The proving purpose is backend deterministic payload derivation and guardrails, not UI rendering or external provider execution.
- New or expanded test families: focused Spec 437 Unit/Feature tests only.
- Fixture / helper cost impact: reuse existing TenantConfiguration evidence factories/helpers where possible; no broad workspace/provider setup defaults should be added.
- Heavy-family visibility / justification: none. Browser/heavy governance remain N/A unless the spec is amended.
- Special surface test profile: N/A.
- Standard-native relief or required special coverage: N/A - no rendered UI.
- Reviewer handoff: Verify tests prove normalized-payload-only input, evidence immutability, protected-value redaction, no product/customer surface, no migration, no tenant_id, no new OperationRun type, no generic/ExchangeTeams normalizer use for Spec 436 Exchange comparable derivation.
- Budget / baseline / trend impact: none expected.
- Escalation needed: none; structural compare/render/customer work is deferred to separate specs.
- Active feature PR close-out entry: Guardrail / Exception / Smoke Coverage.
- Planned validation commands:
cd apps/platform && ./vendor/bin/sail bin pint --dirty --format agentcd apps/platform && ./vendor/bin/sail artisan test --filter=Spec437 --compactcd apps/platform && ./vendor/bin/sail artisan test --filter='Spec436|Spec435|Spec434|Spec433|Spec432|Spec431|Spec430' --compactcd apps/platform && ./vendor/bin/sail artisan test --filter='Spec421|Spec425|Spec415|Spec417|Spec419|Spec420|Spec426|Spec427|ProviderCapabilityRegistry' --compactgit diff --checkgit status --short
Executive Summary
Spec 436 created append-only, content-backed Coverage v2 evidence for the first Exchange PowerShell slice:
transportRuleremoteDomaininboundConnector
Spec 437 introduces deterministic comparable payloads for those three Exchange resource types, derived only from Spec 436 normalized content-backed evidence.
Spec 437 does not implement renderable output, certification, restore/apply, UI, routes, Filament pages, Livewire components, jobs, schedules, listeners, reports, Review Packs, PDFs, or customer claims.
The required output is:
TenantPilot can derive machine-comparable Exchange payloads for transportRule, remoteDomain, and inboundConnector from immutable Spec 436 content-backed evidence, without exposing raw payloads, protected configuration values, customer claims, renderable output, UI, reports, restore, or certification.
Preflight Corrections Applied
This spec applies the corrected preflight decisions:
- Scope is comparable-only.
- Renderable is explicitly deferred.
- Target scope is
transportRule,remoteDomain,inboundConnector. - Builders must consume Spec 436
normalized_payloadonly. - Raw provider payload, stdout, stderr, transcript, and OperationRun context are forbidden as compare input.
- Protected/sensitive values compare only through redacted presence/count/safe-state semantics.
- Existing evidence rows remain immutable.
- No raw/normalized/hash/provenance field may be mutated.
- No new OperationRun type.
- No Exchange invocation OperationRun ownership.
- No customer output.
- No reports, Review Packs, or PDFs.
- No UI/routes/jobs/schedules/listeners.
- Browser proof remains N/A only because this is backend comparable-only.
- No certification, restore, renderable, or customer-ready state.
Strategic Position
Current Exchange roadmap context:
- 429 - Exchange/Teams Source Surface Catalog & Adapter Strategy
- 430 - Exchange PowerShell Adapter Contract Slice 1
- 431 - Exchange PowerShell Invocation Operation Registration & Execution Gate
- 432 - Exchange PowerShell Production Runner Boundary & Runtime Gate
- 433 - Exchange Credential & Permission Evidence Support
- 434 - Exchange Evidence Capture Adapter & Content-Only Guard
- 435 - Exchange Structured Output & Target Normalizer Readiness
- 436 - Exchange Content-Backed Evidence Promotion Slice 1
- 437 - Exchange Comparable Promotion Slice 1
- 438 - Exchange Internal Render Model Slice 1
- 439 - Teams PowerShell Adapter Contract Slice 1
- 440 - Teams Invocation / Evidence Slice
- 441 - Exchange/Teams Certified Core Compare Pack
- 442 - M365 Customer Output & Claim Guard
- 443 - M365 Pilot Readiness Gate
Spec 436 proved selected Exchange types are content-backed. Spec 437 may prove only that selected Exchange types are comparable.
Spec 437 must not prove selected Exchange types are renderable, certified, restore-ready, customer-ready, or customer-claimable.
Prerequisites
Spec 437 may begin implementation only after Spec 436 is merge-ready or merged into the current base branch.
Required Spec 436 proof:
- append-only content-backed evidence exists for
transportRule - append-only content-backed evidence exists for
remoteDomain - append-only content-backed evidence exists for
inboundConnector - evidence owner is
tenant_configuration.capture - Exchange invocation operation remains runner truth only
- Spec 435 structured output envelope is used
- Spec 435 target normalizers are used
- Spec 435 hash builder is used
GenericPayloadNormalizeris not used for Exchange evidenceExchangeTeamsComparablePayloadNormalizeris not used for Spec 436 Exchange PowerShell evidence- raw provider payload is stored only in evidence storage
- raw stdout/stderr/transcript is not evidence
- OperationRun context is sanitized
- empty collection creates no resource/evidence row
- coverage remains content-backed only
- no compare/render/certification/restore/customer state
- no UI/routes/jobs/schedules/listeners/migrations
- no
tenant_idownership truth
Hard stop if any condition fails or implementation attempts any forbidden expansion.
Current Problem
Spec 436 stores safe content-backed Exchange evidence, including deterministic normalized payloads and payload hashes.
The platform still cannot safely compare Exchange evidence across snapshots/runs for:
transportRuleremoteDomaininboundConnector
Spec 437 closes that gap by deriving deterministic comparable payloads from normalized content-backed evidence.
Business / Product Value
Spec 437 reduces the risk of false Exchange readiness claims by proving that the first Exchange PowerShell evidence slice can be compared internally without exposing protected configuration or becoming customer-facing output.
The value is deliberately preparatory: it unlocks later render/certification/customer-output work while preserving the current product truth that Exchange is not yet renderable, certified, restore-ready, or customer-ready.
Primary Users / Operators
- Platform implementers who need a safe comparable payload contract.
- Release reviewers who need proof that Spec 436 evidence can be compared without raw payload leakage.
- Future operator-surface reviewers who will depend on comparable payloads before approving renderable output in Spec 438.
Goal
Implement comparable-only backend promotion for:
transportRuleremoteDomaininboundConnector
Spec 437 must:
- Add an Exchange PowerShell comparable payload builder.
- Add deterministic comparable payloads for all three target types.
- Use Spec 436
normalized_payloadas the only compare input. - Ignore raw provider payload.
- Ignore raw stdout/stderr/transcript.
- Ignore OperationRun context as compare input.
- Preserve immutable evidence rows.
- Produce machine-readable comparable payloads.
- Represent material changes deterministically.
- Ignore volatile fields.
- Handle provider ordering deterministically.
- Redact or omit protected config where required.
- Compare protected config only through safe presence/count/state semantics.
- Keep certification false.
- Keep restore false.
- Keep customer claim false.
- Keep renderable deferred.
- Add no UI/routes/jobs/schedules/listeners/reports/review packs/PDF.
Non-Goals
Spec 437 does not:
- implement renderable output or internal render models
- implement UI display, Filament pages, Livewire components, routes, navigation, dashboards, reports, Review Pack output, PDF output, or customer output
- implement certification, restore/apply, drift UI, baseline compare UI, or customer-safe summaries
- implement Teams, Exchange Admin API, additional Exchange types, Security & Compliance types, restore types, or customer-facing output
- call Microsoft, Exchange, Graph, or PowerShell
- create evidence rows
- modify
raw_payload,normalized_payload,payload_hash, or OperationRun provenance - create Exchange-specific compare tables
- add
tenant_idownership truth - add migrations, jobs, schedules, listeners, console commands, provider registration changes, or runtime triggers
If any of these appear, stop and split or amend the spec.
Target Scope
Included resource types:
transportRuleremoteDomaininboundConnector
Explicitly excluded:
outboundConnectoracceptedDomainorganizationConfigmailboxPlansharingPolicy- all Teams types
- all Security & Compliance types
- all restore types
- all customer-facing output
No target expansion.
Target Eligibility
transportRule
Comparable eligibility: yes.
Prerequisites:
- content-backed evidence exists
- stable identity exists through
id,sourceId,Guid, orRuleId - normalized payload deterministic
- hash deterministic
- sensitive/protected rule values redacted or omitted
Spec 437 must compare:
- stable identity
- enabled/state/mode safe fields
- priority/order if semantically material
- action/condition/exception structures through redacted deterministic representation
- material counts/states
- safe normalized metadata
Spec 437 must not compare:
- raw rule text
- raw transport rule patterns
- raw header values if classified sensitive
- raw email addresses if classified sensitive
- raw provider payload
- raw stdout/stderr
remoteDomain
Comparable eligibility: yes.
Prerequisites:
- content-backed evidence exists with stable source identity
- domain-only/display-only identity remains blocked
- normalized payload deterministic
- hash deterministic
- domain/settings fields classified and redacted where needed
Spec 437 must compare:
- stable identity
- safe normalized settings
- redacted field presence
- safe state/count semantics
- default/custom classification if safe
- material configuration state
Spec 437 must not compare:
- domain-only identity as stable identity
- display-only identity as stable identity
- raw domain values if classified protected
- raw provider payload
- raw stdout/stderr
inboundConnector
Comparable eligibility: yes.
Prerequisites:
- content-backed evidence exists with stable connector identity
- normalized payload deterministic
- hash deterministic
- protected config redaction enforced
Protected config includes IPs, smart hosts, certificate names, comments, routing metadata, TLS settings, and hosts.
Spec 437 must compare:
- stable connector identity
- safe state/presence/count semantics
- redacted protected config markers
- safe TLS/routing state categories
- material normalized settings where safe
Spec 437 must not compare:
- raw IP ranges
- raw host names
- raw smart hosts
- raw certificate names
- raw comments
- raw routing metadata
- raw provider payload
- raw stdout/stderr
Required Final State
For each target, when content-backed evidence is safe and present:
| Required state | Value |
|---|---|
content_backed_prerequisite |
true |
comparable_payload_built |
true |
compare_input |
normalized_payload_only |
raw_payload_used |
false |
stdout_stderr_used |
false |
operation_run_context_used_as_input |
false |
protected_values_redacted_or_omitted |
true |
volatile_fields_ignored |
true |
provider_ordering_deterministic |
true |
material_changes_detectable |
true |
derived_comparable_builder_state |
comparable |
persisted_evidence_coverage_level |
content_backed unchanged |
resource_latest_state |
unchanged |
resource_type_default_coverage_level |
unchanged |
renderable |
false |
certified |
false |
restore_ready |
false |
customer_claimable |
false |
When evidence is missing or unsafe:
- no comparable artifact
- no coverage promotion
- specific blocker state
- no renderable/certification/restore/customer state
Promotion Semantics
Preferred model:
Comparable is derived service/builder output from immutable content-backed evidence. Spec 437 does not persist coverage_level = comparable and does not promote persisted resource, evidence, or resource-type coverage state.
Allowed:
- build comparable payloads from an explicitly supplied content-backed evidence row's
normalized_payload - require
payload_hash,evidence_state = content_backed,coverage_level = content_backed, andcapture_outcome = capturedbefore comparable output is derived - return an explicit machine blocker when evidence is missing, unsafe, unsupported, or not content-backed
- include
payload_hashand safe source metadata as non-material provenance anchors when useful
Forbidden:
- mutate
raw_payload - mutate
normalized_payload - mutate
payload_hash - mutate
coverage_level - mutate OperationRun provenance
- update
TenantConfigurationResource.latest_*fields - update
TenantConfigurationResourceType.default_coverage_level - persist a comparable artifact, compare table, or Exchange-specific compare row
- replace evidence row
- delete previous evidence
- overwrite content-backed evidence
If implementation needs persisted comparable state, renderable output, read-model output, or coverage-level promotion, stop and amend or split the spec.
Comparable Payload Requirements
Comparable payload must:
- use
normalized_payloadonly - be deterministic
- include stable identity
- emit stable identity as a scoped, deployment-keyed HMAC anchor rather than an exact value or unsalted hash
- include resource type
- include
workload = exchange - include safe source contract metadata if present in normalized evidence
- include normalizer version when available
- treat source shape metadata that defines compare semantics, including payload shape version and normalizer version, as hash-relevant contract metadata
- include material safe fields
- exclude volatile fields
- exclude raw payload, stdout/stderr/transcript, credentials, OperationRun context, customer interpretation, and UI/report wording
- derive material identity from
normalized_payload.source_identity; safe evidence metadata may validate provenance but must not override identity or provide material compare fields
Comparable payload must support:
- same normalized material payload => same comparable payload
- material change => changed comparable payload
- volatile change => unchanged comparable payload
- provider ordering change => unchanged comparable payload when order is not meaningful
- redacted sensitive value change => safe redacted state/count delta or no exact-value delta
Target Comparable Builders
Recommended conceptual class:
ExchangePowerShellComparablePayloadBuilder
Optional target helpers:
ExchangeTransportRuleComparablePayloadBuilderExchangeRemoteDomainComparablePayloadBuilderExchangeInboundConnectorComparablePayloadBuilder
Repo-canonical equivalents are allowed if they preserve all gates.
Builder Input
Allowed input:
resource_typenormalized_payload- required
payload_hashas a 64-character lowercase SHA-256 non-material provenance/hash anchor - required content-backed evidence metadata only for scope/provenance/blocker context:
evidence_state = content_backed,coverage_level = content_backed, andcapture_outcome = captured - canonical identity key only when derived from
normalized_payload.source_identity
Forbidden input:
raw_payload- raw stdout
- raw stderr
- PowerShell transcript
- OperationRun context
- logs
- report output
- Review Pack output
- UI read model
- externally supplied material identity that overrides
normalized_payload.source_identity - evidence metadata as material compare fields
Builder Output
Allowed output:
- stable identity key
- scoped HMAC identity anchor
- resource type
- workload
- material safe fields
- redacted field presence
- redacted field count
- safe boolean/state fields
- safe enum/state fields
- deterministic collection summaries
- non-material provenance anchors
- normalizer version metadata
Forbidden output:
- raw provider object
- raw policy text
- raw IPs
- raw hosts
- raw certificate names
- raw comments
- customer-friendly prose
- risk language
- secure/insecure verdicts
- restore instructions
- certification labels
Delta / Compare Semantics
Spec 437 may introduce a comparator only if repo architecture needs it.
Recommended conceptual class:
ExchangePowerShellCoverageComparator
The comparator must compare comparable payloads, not raw or normalized provider payloads.
Allowed machine delta categories:
addedremovedchangedunchangedredacted_value_changedredacted_presence_changedredacted_count_changed
Forbidden delta categories:
critical_riskcustomer_issuenon_compliantcertified_changerestore_requiredsecurity_failure
No customer-facing interpretation.
Redaction / Protected Config
Never expose in comparable payload:
- raw provider payload
- raw stdout
- raw stderr
- PowerShell transcript
- credential material
- tokens
- secrets
- authorization headers
- cookies
- raw transport rule patterns
- raw header values if sensitive
- raw domain values if protected
- raw IP ranges
- raw smart hosts
- raw certificate names
- raw connector comments
- raw routing metadata
Allowed comparable representation:
present = true/falsecount = integerredacted = truestate = safe enum- scoped HMAC identity anchor only when safe and non-reversible according to repo policy
Comparable derivation must fail closed with unsafe_exact_value when protected/sensitive comparable sections contain an exact unredacted value that is not explicitly classified as safe for the target type. Exact-value fields classified as safe must also pass field-specific value policy checks, such as boolean-only fields, bounded enum-only fields, or non-negative integer-only fields.
If exact value comparison would leak protected config, compare only presence/count/safe state or block comparable for that field.
Empty Collection Behavior
Spec 436 empty collection creates no evidence row.
Therefore Spec 437 must not create:
- fake comparable artifact
- fake comparable resource
- collection-level comparable proof
- fake empty delta
Empty collection remains zero summary only: no evidence, no comparable, no renderable.
If no evidence row exists, no comparable payload exists.
Product Surface / Customer Output Safety
Customer output must remain unchanged.
Forbidden:
- customer-safe summary
- customer report
- Review Pack section
- PDF output
- customer route
- customer claim
- customer-ready badge
- certification label
- restore recommendation
Static tests must prove no customer/report/review-pack/PDF code changed.
Data / Migration Impact
Default: no migration.
Existing model should be sufficient.
Forbidden:
- Exchange-specific compare table
- Exchange-specific render table
tenant_idcolumn- customer output table
- UI state table
- legacy snapshot table
If migration is required, stop and amend the spec.
Likely Affected Files
Spec artifacts:
specs/437-exchange-comparable-promotion-slice-1/spec.mdspecs/437-exchange-comparable-promotion-slice-1/plan.mdspecs/437-exchange-comparable-promotion-slice-1/tasks.mdspecs/437-exchange-comparable-promotion-slice-1/checklists/requirements.mdspecs/437-exchange-comparable-promotion-slice-1/implementation-report.md
Likely new runtime files:
apps/platform/app/Services/TenantConfiguration/ExchangePowerShellComparablePayloadBuilder.phpapps/platform/app/Services/TenantConfiguration/ExchangePowerShellCoverageComparator.phpif needed
Likely new tests:
apps/platform/tests/Feature/TenantConfiguration/Spec437ExchangeComparablePromotionFeatureTest.phpapps/platform/tests/Unit/Support/TenantConfiguration/Spec437ExchangeComparablePayloadBuilderTest.php
Existing files that may be referenced or minimally changed if repo pattern requires:
apps/platform/app/Services/TenantConfiguration/CoverageEvidenceWriter.phpapps/platform/app/Services/TenantConfiguration/ExchangePowerShellTargetEvidenceNormalizer.phpapps/platform/app/Services/TenantConfiguration/ExchangePowerShellHashInputBuilder.php
Files that must not change:
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/PDF code
apps/platform/app/Support/OperationRunType.phpapps/platform/app/Support/OperationCatalog.php- live Exchange/Graph/PowerShell runner code
apps/platform/app/Services/TenantConfiguration/CoverageV2ReadinessReadModel.phpunless the spec is amended with product-surface/browser proof
User Scenarios & Testing
User Story 1 - Comparable transportRule Payload (Priority: P1)
As a platform reviewer, I need content-backed transportRule evidence to produce a deterministic comparable payload without raw rule text, raw patterns, or raw headers, so future compare work can detect material mail-flow rule changes without leaking protected values.
Independent Test: Build comparable payloads from equivalent Spec 436 normalized transportRule evidence and assert stable output; mutate safe material state and assert changed output; mutate volatile/provider ordering only and assert unchanged output.
User Story 2 - Comparable remoteDomain Payload With Stable Identity Only (Priority: P1)
As a platform reviewer, I need content-backed remoteDomain evidence to compare only from stable source identity and safe settings, so domain-only or display-only identity cannot become ambiguous comparable proof.
Independent Test: Build comparable payloads from stable Spec 436 normalized remoteDomain evidence; assert domain-only/display-only/missing unsafe identity has no comparable payload; assert protected domain/settings values are represented only through safe semantics.
User Story 3 - Comparable inboundConnector Payload With Protected Config Redaction (Priority: P1)
As a platform reviewer, I need content-backed inboundConnector evidence to compare routing/TLS state without exposing raw IPs, hosts, smart hosts, certificate names, comments, or routing metadata.
Independent Test: Build comparable payloads from stable Spec 436 normalized inboundConnector evidence with protected config and assert only safe presence/count/state markers appear.
User Story 4 - Comparable Promotion Guardrails (Priority: P1)
As a release reviewer, I need Spec 437 to prove no evidence mutation, product surface, customer output, OperationRun type, migration, tenant_id, renderable, certification, or restore expansion, so the comparable-only slice cannot overclaim.
Independent Test: Feature/static tests assert evidence rows are immutable and forbidden file/surface categories remain unchanged.
Edge Cases
- Missing evidence returns no comparable payload and an explicit blocker.
- Empty collection creates no comparable artifact because Spec 436 creates no evidence row.
- Unsafe identity returns no comparable payload.
- Redacted exact protected value changes either produce safe redacted presence/count/state deltas or no exact-value delta.
- Unredacted exact protected/sensitive values in comparable sections block comparable output.
- Volatile normalized fields do not alter comparable payload.
- Provider ordering changes do not alter comparable payload where ordering is not semantically meaningful.
- Existing Spec 436
raw_payload,normalized_payload,payload_hash,operation_run_id, source metadata, and previous rows remain unchanged.
Requirements
Functional Requirements
- FR-437-001: System MUST support comparable payload derivation for exactly
transportRule,remoteDomain, andinboundConnector. - FR-437-002: Comparable builders MUST consume only Spec 436
normalized_payloadas material input; safe evidence metadata andpayload_hashmay be used only for provenance, scope validation, scoped HMAC identity anchors, or blocker context. - FR-437-002a: Comparable builders MUST require
payload_hash,evidence_state = content_backed,coverage_level = content_backed, andcapture_outcome = captured; otherwise they MUST return no comparable payload and an explicit blocker. - FR-437-003: Comparable builders MUST NOT accept or read raw provider payload, raw stdout, raw stderr, PowerShell transcript, OperationRun context, logs, UI read models, report output, or Review Pack output.
- FR-437-004: Comparable payloads MUST be deterministic for the same normalized material payload.
- FR-437-005: Material safe changes MUST change comparable payloads or produce machine delta output.
- FR-437-006: Volatile field changes MUST NOT change comparable payloads.
- FR-437-007: Provider ordering changes MUST NOT change comparable payloads when order is not semantically meaningful.
- FR-437-008: Protected exact values MUST be omitted, redacted, or represented only through safe presence/count/state semantics; unexpected exact unredacted values in protected/sensitive comparable sections MUST block comparable output.
- FR-437-009:
transportRulecomparable payloads MUST exclude raw rule text, raw patterns, raw sensitive header values, and raw email addresses if classified sensitive. - FR-437-010:
remoteDomaincomparable payloads MUST block domain-only/display-only identity and must not expose protected raw domain values. - FR-437-011:
inboundConnectorcomparable payloads MUST exclude raw IP ranges, hosts, smart hosts, certificate names, comments, and routing metadata. - FR-437-012: Empty or missing evidence MUST produce no comparable artifact.
- FR-437-013: Unsafe identity or blocked target MUST produce no comparable artifact.
- FR-437-014: Existing evidence rows MUST remain immutable; implementation MUST NOT mutate
raw_payload,normalized_payload,payload_hash,operation_run_id, source metadata, or prior evidence rows. - FR-437-015: No renderable, certification, restore-ready, customer-ready, customer-claimable, report, Review Pack, or PDF state may be added.
- FR-437-016: No route, Filament page/resource/widget, Livewire component, Blade view, navigation, asset, global search, job, schedule, listener, console command, migration, new OperationRun type,
tenant_id, or Exchange-specific compare table may be added. - FR-437-017: Implementation MUST NOT use
GenericPayloadNormalizerorExchangeTeamsComparablePayloadNormalizeras the Spec 436 Exchange PowerShell comparable input path. - FR-437-018: If a comparator is added, it MUST compare comparable payloads only and use machine categories:
added,removed,changed,unchanged,redacted_value_changed,redacted_presence_changed, andredacted_count_changed. - FR-437-019: Comparator output MUST NOT include customer-facing risk, compliance, certification, restore, or security verdict categories.
- FR-437-020: Implementation MUST NOT persist comparable promotion by changing
TenantConfigurationResourceEvidence.coverage_level,TenantConfigurationResource.latest_*, orTenantConfigurationResourceType.default_coverage_level; comparable remains derived builder/service output for this slice.
Non-Functional Requirements
- NFR-437-001: Comparable derivation MUST be deterministic across equivalent normalized payloads.
- NFR-437-002: Comparable derivation MUST be side-effect free for evidence rows and OperationRun provenance.
- NFR-437-003: Comparable derivation MUST remain local/in-memory service output for this slice and MUST NOT persist comparable output or coverage-level promotion.
- NFR-437-004: Comparable derivation MUST not perform provider calls, Graph calls, Exchange calls, PowerShell execution, queue dispatch, scheduling, or listener-triggered work.
- NFR-437-005: Tests MUST use the narrowest honest Unit/Feature lanes and MUST keep Browser proof
N/A - no rendered UI surface changed.
Assumptions
- Spec 436 is merged into the current base or is merge-ready before implementation begins.
- Spec 436 normalized payload shape remains the source truth for the three target types.
- Existing Coverage v2 evidence tables are sufficient; no migration or Exchange-specific compare table is needed.
- Existing Spec 436 empty-collection behavior remains unchanged: no evidence row means no comparable payload.
- Spec 438 will handle renderable concerns separately and must not be folded into Spec 437.
Risks
- Protected Exchange configuration could leak if comparable payloads include exact values instead of safe presence/count/state semantics.
- Evidence immutability could be weakened if comparable derivation mutates or overwrites source evidence rows.
- Product overclaim could appear if comparable state is treated as renderable, certified, restore-ready, customer-ready, or customer-claimable.
- Scope could expand by reusing the older generic Exchange/Teams comparable/renderable path instead of the corrected Spec 436 normalized-payload-only input path.
- A persistence shortcut could create an Exchange mini-platform, compare table, migration, or tenant_id ownership truth.
Open Questions
None blocking.
Resolved implementation decisions:
- Builder API remains narrow and does not introduce a new DTO/framework; it fails closed unless the caller supplies explicit content-backed provenance (
payload_hash,evidence_state,coverage_level, andcapture_outcome). - Stable identity anchors use scoped app-keyed HMAC values, not exact values or unsalted SHA-256 hashes, because Exchange identities can be low-entropy or guessable in a SaaS setting.
- Comparable hash excludes non-material Evidence/OperationRun provenance, but includes compare contract metadata that changes interpretation of material payloads, especially resource type, source surface, command contract metadata, payload shape version, and normalizer version. This follows Spec 435 hash-input precedent and prevents comparing values normalized under different semantics as if they were equivalent.
- If implementation discovers that existing Coverage v2 architecture cannot represent comparable output without new persistence, stop and amend the spec instead of adding a migration or compare table.
Success Criteria
Measurable Outcomes
- SC-437-001: Focused Spec 437 unit tests prove deterministic comparable payloads for all three targets.
- SC-437-002: Focused Spec 437 feature/static tests prove evidence immutability and no forbidden product/customer/trigger/persistence expansion.
- SC-437-003: Regression tests for Specs 430-436 and relevant Coverage v2/Entra compare guardrails pass or exact unrelated failures are documented.
- SC-437-004: Pint and
git diff --checkpass. - SC-437-005: Implementation report records target matrix, safety matrix, tests, browser N/A, deployment impact, and deferred work.
Acceptance Criteria
Spec 437 passes only if:
- scope is exactly
transportRule,remoteDomain,inboundConnector - scope remains comparable-only
normalized_payloadis the only compare input- raw payload, stdout/stderr/transcript, OperationRun context, logs, report output, and Review Pack output are not compare inputs
- comparable payloads are deterministic and machine-readable
- stable identity field is included
- stable identity exact value is not emitted; identity matching uses a scoped HMAC anchor
- stable identity is derived from
normalized_payload.source_identity, not from externally supplied metadata - content-backed provenance gates require a valid SHA-256
payload_hash,evidence_state = content_backed,coverage_level = content_backed, andcapture_outcome = captured - exact safe fields pass target-specific value policies before they can appear in comparable output
- material safe fields are included
- volatile fields are excluded
- provider ordering is deterministic
- protected values are redacted/omitted
- no customer-facing prose, risk verdict, or certification label appears
- evidence payload/provenance is immutable
- persisted evidence/resource/resource-type coverage state remains unchanged and no
coverage_level = comparablepromotion is written - missing/empty/unsafe evidence creates no comparable artifact
- no renderable/certified/restore/customer/product promotion appears
- no UI/routes/jobs/schedules/listeners/migrations/tenant_id/OperationRun type/Exchange-specific compare table appears
- focused unit/feature tests and regressions pass or failures are documented
Final Candidate Gate
PASS requires:
- Spec 436 is merge-ready or merged into the current base.
- Only
transportRule,remoteDomain, andinboundConnectorare in scope. - Spec 437 remains comparable-only.
- Renderable is deferred.
- Comparable payloads use
normalized_payloadonly. - Raw payload is not compare input.
- Raw stdout/stderr/transcript are not compare input.
- OperationRun context is not compare input.
- Comparable payloads are deterministic.
- Material changes are detectable.
- Volatile changes are ignored.
- Protected values are redacted/omitted.
- Evidence rows remain immutable.
- No renderable, certification, restore, customer-ready/customer-claimable state appears.
- No UI/routes/jobs/schedules/listeners are added.
- No migration is added.
- No
tenant_idownership truth is introduced. - No Exchange-specific compare table is introduced.
- Focused tests and regressions pass.
PASS WITH CONDITIONS is allowed only for bounded follow-ups that do not weaken comparable determinism, redaction, evidence immutability, no-renderable posture, no-customer-output posture, no-product-surface posture, ownership, or no-mini-platform posture.
FAIL if renderable output is added; raw payload/stdout/stderr/transcript/OperationRun context is used as compare input; protected config leaks; evidence payload/provenance is mutated; certification/restore/customer claims appear; UI/routes/jobs/schedules/listeners are added; migration is added without amendment; tenant_id ownership truth is introduced; Exchange-specific compare table appears without approval; or tests do not prove comparable safety.
Implementation Report Requirements
implementation-report.md must include:
- Candidate gate result
- Branch and HEAD
- Dirty state before/after
- Files changed
- Spec 436 prerequisite proof
- Target comparable outcome matrix
- Comparable input proof
- Comparable output proof
- Evidence immutability proof
- Redaction proof
- Determinism proof
- Delta proof if comparator added
- No raw payload proof
- No OperationRun context input proof
- No persisted coverage-level promotion proof
- No renderable proof
- No certification/restore/customer proof
- No UI/route/job/schedule/listener proof
- No tenant_id proof
- No migration proof
- Tests run
- Deferred work
Required target matrix:
| Type | Content-backed prerequisite | Comparable? | Renderable? | Blocker |
|---|---|---|---|---|
transportRule |
Yes | Pending implementation proof | No | Pending implementation proof |
remoteDomain |
Yes | Pending implementation proof | No | Pending implementation proof |
inboundConnector |
Yes | Pending implementation proof | No | Pending implementation proof |
Required safety matrix:
| Area | State |
|---|---|
| Raw payload as input | No |
| Normalized payload as input | Yes |
| OperationRun context as input | No |
| Protected exact values exposed | No |
| Renderable | No |
| Certification | No |
| Restore | No |
| Customer output | No |
| UI | No |
| Jobs/Schedules/Listeners | No |
| Migration | No |
Follow-up Spec Candidates
- Spec 438 - Exchange Internal Render Model Slice 1. Requires its own preflight because renderable output increases Product Surface/read-model/UI/customer-claim risk.
- Teams PowerShell adapter and evidence slices remain separate.
- Exchange/Teams certified compare pack remains separate.
- M365 customer output and claim guard remains separate.