Implements Feature Spec 438 and associated plan/tasks/update artifacts for Exchange internal render model slice 1. Target branch: platform-dev Follow-up integration path after merge: platform-dev -> dev. Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de> Reviewed-on: #505
50 KiB
Feature Specification: Spec 438 - Exchange Internal Render Model Slice 1
Feature Branch: feat/438-exchange-internal-render-model-slice-1
Created: 2026-07-10
Status: Deferred
Type: Coverage v2 / Microsoft 365 / Exchange / Internal Presentation Model / Backend-only
Priority: P1
Risk: High
Mode: Derived-only internal render model for successful Spec 437 comparable result envelopes; no persisted renderable promotion, no read-model integration, no UI, and no customer output
Input: Direct user-provided corrected Spec 438 candidate.
Candidate Result: FAIL Candidate Class: Defer Candidate Score: 5/12 Implementation permitted: No Implementation state: Not implemented
Deferral Decision
Spec 438 is deferred because no current operator workflow or direct runtime consumer requires an Exchange internal render model.
The proposed builder would be speculative foundation.
No runtime implementation is permitted while this spec remains deferred.
Candidate Score: 5/12
Candidate Result: FAIL
Candidate Class: Defer
Implementation permission: no
Current Runtime Consumer: none
Current Workflow Owner: none
Spec 436:
Content-backed Exchange evidence exists.
Spec 437:
Comparable Exchange payloads exist.
Current limitation:
Those comparable payloads have no proven runtime consumer outside tests.
Spec 438 is not:
- implemented
- completed
- passed
- merge-ready as a runtime change
- approved for implementation
Reactivation Conditions
Spec 438 or its relevant safety rules may only be resumed if all of the following are satisfied:
- A currently reachable Operator or Product workflow has a concrete reproducible Exchange problem.
- A direct Runtime Consumer for Spec 437-comparable payloads is named.
- The consumer uses the output in the same spec.
- The Workflow Owner is explicitly assigned.
- Only actually required Exchange target types are in scope.
- RBAC, Workspace, Environment, and Provider scope are documented.
- Redaction and Protected-Value rules are part of the Consumer spec.
- UI integration includes browser proof and Product Surface proof.
- Customer/Report/Review-pack/PDF proximity includes an Exchange-specific content-safety contract.
- Candidate selection and Spec Readiness support an implementable class.
No new spec may be created only for the unused render model builder.
Deferred Safety Requirements (deferred safety requirements)
The following rules remain valuable but are not implemented:
- Comparable payload as the only allowed source.
- No Evidence fallback.
- No raw_payload fallback.
- No normalized_payload fallback.
- No OperationRun context.
- No legacy ExchangeTeams renderer.
- Fixed type allowlists.
- No generic stringify/map output.
- No protected exact values.
- No HMAC-, source_key-, or hash display.
- No persisted CoverageLevel::Renderable promotion.
- No customer, certification, restore, or remediation claims.
Preparation Selection Summary
- Selected candidate: Spec 438 - Exchange Internal Render Model Slice 1.
- Source location: Direct user-provided candidate in this session. The automatic candidate queue remains empty and is not used to invent adjacent scope.
- Why selected: Spec 437 is merged at current
platform-devbase HEADff1a545c, records PASS and merge-ready, proves comparable result envelopes for exactlytransportRule,remoteDomain, andinboundConnector, and explicitly defers Spec 438 as the next Exchange slice. - Close alternatives deferred: Coverage-level
renderablepromotion,CoverageV2ReadinessReadModelintegration, Filament/Livewire/UI, customer output, reports, Review Packs, PDFs, comparator/delta prose, certification, restore/apply, jobs, schedules, listeners, additional Exchange targets, Teams targets, and a shared presentation framework. - Roadmap relationship: Continues the bounded Exchange sequence from Specs 429-437. It establishes only a safe local transformation boundary over the Spec 437 result contract.
- Completed-spec guardrail result: Specs 429-437 and Spec 422 are completed historical context. They are read-only and MUST NOT be rewritten. No existing Spec 438 branch, directory, or runtime class existed before preparation.
- Smallest viable implementation slice: One local builder with one public array-in/nullable-array-out method, three explicit target mappings, fixed output sections, and focused leakage/determinism/immutability proof.
- Feature description fed into Spec Kit: Derive a deterministic internal-only redaction-safe Exchange render model from successful Spec-437 comparable result envelopes for
transportRule,remoteDomain, andinboundConnector, with no persistence, UI, ReadModel, customer output, OperationRun, migration, or generic renderer. - Branch-base note: The repository guideline still names
dev, but Spec 437 is merged onplatform-devandorigin/devdoes not contain that prerequisite. Preparation therefore starts from current mergedplatform-devtruth and records the deviation instead of branching from a stale base. - Candidate Selection Gate: FAIL. The candidate is directly provided and technically bounded, but no current operator workflow or real consumer requires the standalone layer. PROP-001 and Spec Approval Rubric Rule C override the numeric score. The transformation must be folded into a new or amended consumer-owning candidate; new current-release evidence alone does not reactivate this standalone package.
Spec Candidate Check
- Problem: The proposal anticipates that a future operator presentation will need a bounded transformation contract over Spec 437 comparable payloads.
- Today's failure: No current shipped operator workflow consumes the Spec 437 result and no current decision, delay, or product claim is shown to fail because this builder is absent. The stated legacy-renderer collision is a future risk, not a current workflow failure.
- User-visible improvement: None in this slice. A future combined consumer spec could provide the operator improvement while keeping this mapping local to the real workflow.
- Smallest enterprise-capable version: When a real internal operator consumer exists, one local Exchange-specific builder in that consumer-owning spec, supporting exactly three proven Spec 437 result shapes and returning a model only for a valid successful envelope.
- Explicit non-goals: No consumer integration, shared interface, registry, DTO family, presenter family, persisted state, coverage promotion, customer output, UI, comparator, delta prose, report, Review Pack, PDF, restore, certification, job, trigger, migration, enum, or status family.
- Permanent complexity imported: One provider-owned builder class plus two focused Pest test files. No source of truth, persistence, status axis, route, UI concept, or shared platform contract.
- Why now: The Spec 437 prerequisite is available, but availability alone does not establish current-release necessity. Preparation can preserve the proposed safety contract; implementation is deferred until a real consumer exists.
- Why not local: The correct narrower implementation is local mapping inside the first real operator-consumer spec. Creating that local layer as its own zero-consumer spec is still below the operator domain and fails Rule C.
- Approval class: Defer.
- Red flags triggered: #2 new builder, #4 future-facing foundation language, and #6 micro-spec sequence. Defense: the builder has three concrete target mappings, no generic extension point, no consumer integration, no persistence, and a hard stop against shared-layer growth. Zero current consumers remains an explicit review risk rather than being disguised as reuse.
- Score: Nutzen: 1 | Dringlichkeit: 0 | Scope: 2 | Komplexitaet: 2 | Produktnaehe: 0 | Wiederverwendung: 0 | Gesamt: 5/12. The bounded security value is locally useful, but current urgency, product proximity, and reuse are not repo-proven; the rubric therefore requires defer or merge.
- Decision: FAIL for standalone implementation. Preserve this package as a blocked technical draft, or merge its safety contract into a future spec that owns a current operator problem and real consumer. Any implementation-loop invocation before that gate changes is prohibited.
Spec Scope Fields
- Scope: Provider-owned, in-memory Exchange comparable-to-internal-model transformation only.
- Primary Routes: N/A - no route, controller, API route, Filament page/resource/widget, Livewire component, view, report, PDF, download, or navigation change.
- Data Ownership: None. The public API accepts only an in-memory Spec 437 result envelope. It does not accept or resolve Workspace, ManagedEnvironment, ProviderConnection, Evidence, Resource, ResourceType, or OperationRun records and creates no ownership truth.
- RBAC: N/A - no invocation surface, action, route, record lookup, or mutation. A later consumer MUST define authorization and scope in its own spec.
- Default filter behavior when environment context is active: N/A - no canonical view.
- Entitlement checks preventing cross-environment leakage: N/A in this pure builder. It MUST NOT add a lookup, fallback-to-latest behavior, or provider-native
tenant_idownership path.
No Legacy / No Backward Compatibility Constraint
- Compatibility posture: canonical bounded addition over the Spec 437 result envelope.
- Legacy aliases, fallback readers, hidden routes, duplicate UI, old labels, or historical fixtures kept?: no.
- Why clean addition is safe now: No persisted or external contract changes. The legacy
ExchangeTeamsRenderableSummaryBuilderis neither changed nor reused, and no compatibility shim connects it to the new path.
UI Surface Impact
- 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 438 adds only a local backend transformation service and tests. Any route, ReadModel, Filament, Livewire, Blade, navigation, global-search, asset, report, or download change invalidates this decision and requires a separate product-surface spec.
UI/Productization Coverage
N/A - no reachable UI surface impact. No route inventory, design coverage, page report, screenshot, or browser artifact is required.
Product Surface Impact
Reference: docs/product/standards/product-surface-contract.md.
- Product Surface Contract applies?: Only as a checked no-impact boundary.
- Page archetype: N/A.
- Primary user question: N/A.
- Primary action: N/A.
- Surface budget result: N/A.
- Technical Annex / deep-link demotion: No runtime surface exists. Identity HMAC, source key, hashes, provenance, OperationRun detail, raw payload, and technical logs are excluded from output rather than rendered.
- Canonical status vocabulary: N/A - no product-facing status or badge changes.
- 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: If this reference contract is migrated, the consumer-owning spec's implementation report records its surface decision and visible-complexity outcome; standalone Spec 438 creates no report.
Product Surface Merge Gate Checklist
- No-legacy posture recorded.
- Product Surface Impact is N/A with rationale.
- Browser proof is
N/A - no rendered UI surface changed. - Human Product Sanity is not applicable with rationale.
- Product Surface exceptions are
none. - Any consumer-owning implementation report that adopts this contract must record Livewire v4, provider registration, global search, destructive/high-impact actions, assets, tests/browser, deployment, visible complexity, and completed-spec rewrite posture.
Cross-Cutting / Shared Pattern Reuse
- Cross-cutting feature?: no operator-facing shared interaction family is touched.
- Interaction class(es): provider-owned internal presentation transformation only.
- Systems touched: Spec 437 result envelope and the new local builder.
- Existing pattern(s) to extend: Nullable builder result convention and explicit target mapping under
apps/platform/app/Services/TenantConfiguration/. - Shared contract / presenter / renderer to reuse: none.
ExchangeTeamsRenderableSummaryBuilderis forbidden. - Why existing shared paths are insufficient: The legacy renderer accepts provider-near normalized payload plus context, exposes display-oriented values, and includes generic stringification. It does not implement the Spec 437 envelope boundary.
- Conditional technical fit: One local provider-owned builder would be a proportionate security boundary for the three concrete targets inside a consumer-owning spec. ABSTR-001's security allowance does not waive PROP-001, LAYER-001, Spec Approval Rubric Rule B, or Rule C for this zero-consumer standalone package.
- Consistency impact: Fixed internal-only boundary metadata and protected-value semantics remain identical for all three mappings.
- Review focus: Reject dependencies on Evidence/Resource/OperationRun/ReadModel/customer-output classes or any generic pass-through.
OperationRun UX Impact
- Touches OperationRun start/completion/link UX?: no.
- Shared OperationRun UX contract/layer reused: N/A.
- Delegated UX behaviors: N/A.
- Local surface-owned behavior: none.
- Queued DB-notification policy: N/A.
- Terminal notification path: N/A.
- Exception required?: none.
OperationRun impact is N/A - derived locally from a supplied comparable envelope without a new operation. No OperationRun type, catalog entry, context input, context output, or row mutation is allowed.
Provider Boundary / Platform Core Check
- Shared provider/platform boundary touched?: no shared seam is changed.
- Boundary classification: provider-owned.
- Seams affected: Exchange PowerShell comparable result to internal model only.
- Neutral platform terms preserved: resource type, workload, internal-only boundary flags.
- Provider-specific semantics retained and why: The three Exchange resource types and their fixed safe mappings are the concrete current cases.
- Why this does not deepen provider coupling accidentally: No platform-core interface, registry, taxonomy, persistence, status, or consumer is changed.
- Follow-up path: The first real consumer-owning spec must absorb and adapt this mapping contract and its relevant tasks. Unchanged standalone Spec 438 does not become executable merely because a later consumer is documented; no genericization is pre-approved.
UI / Surface Guardrail Impact
N/A - no operator-facing surface change.
Proportionality Review
- New source of truth?: no.
- New persisted entity/table/artifact?: no.
- New abstraction?: yes - exactly one local final builder class.
- New enum/state/reason family?: no.
- New cross-domain UI framework/taxonomy?: no.
- Current operator problem: None demonstrated. There is no current consumer or operator workflow for the proposed model.
- Existing structure is insufficient because: Not proven for the current release. Spec 437 is machine-comparable and currently has no runtime call site.
- Narrowest correct implementation: Defer the standalone slice; include one local mapping class only when the first real internal operator consumer establishes current need.
- Ownership cost: One security-sensitive mapping and focused tests must remain aligned with the Spec 437 envelope version.
- Alternative selected: Fold the mapping and its leakage boundary into the first consumer spec so security is proven at the same time as the current operator workflow; a generic presenter/registry remains rejected as overproduction.
- Release truth: The contract is a parked preparation artifact only; it is not approved current-release implementation truth.
- Current consumer evidence:
ExchangePowerShellComparablePayloadBuilderhas no application call site outside Spec 437 tests. The reachable Coverage v2 Inspect flow usesCoverageV2ReadinessReadModeland the legacyExchangeTeamsRenderableSummaryBuilderover persistednormalized_payload; current Exchange PowerShell evidence remains capped atContentBacked, so that legacyRenderablepath is not a Spec 437/438 consumer. - Hard proportionality stop: More than one mapping class, a new interface/DTO/registry/presenter family, or persistence invalidates this parked local-only draft. A runtime consumer also remains out of scope here; resolving the product blocker requires migrating the contract into a consumer-owning spec rather than reviving this task package unchanged.
Testing / Lane / Runtime Impact
- Test purpose / classification: Unit for pure envelope validation/mapping/leakage/determinism; Feature for focused database immutability; Heavy-Governance/static review for dependency and changed-file boundary proof; Browser N/A.
- Validation lanes: fast-feedback, confidence, and existing targeted regression filters. No browser lane.
- Why sufficient: The builder is pure in-memory behavior; only the no-write assertion needs database fixtures. Broad repository discovery does not belong in Unit/Feature tests.
- New or expanded test families: one focused Unit file and one focused Feature file; no browser or broad discovery family.
- Fixture/helper cost impact: Reuse minimal Spec 437 envelope fixtures. Database setup is opt-in only in the immutability feature test.
- Heavy-family visibility: Source dependency checks and
git diff --name-onlyare explicit close-out proof, not hidden in the fast lane. - Special surface test profile: N/A.
- Reviewer handoff: Confirm exact input/output shape, null-on-invalid behavior, allowlists, leakage corpus, no source dependencies, minimal fixture cost, and changed-file scope.
- Budget/baseline/trend impact: none expected.
- Escalation needed: defer the standalone candidate; if folded into a consumer-owning spec, keep the focused test design and reject-or-split broad static discovery.
- Active feature PR close-out entry: Guardrail / Exception / Smoke Coverage.
- Planned validation commands:
cd apps/platform && ./vendor/bin/sail artisan test --filter=Spec438 --compactcd apps/platform && ./vendor/bin/sail artisan test --filter='Spec430|Spec431|Spec432|Spec433|Spec434|Spec435|Spec436|Spec437' --compactcd apps/platform && ./vendor/bin/sail artisan test --filter='Spec415|Spec417|Spec419|Spec420|Spec421|Spec422|Spec425|Spec426|Spec427|ProviderCapability|SourceContract|CoverageV2Readiness|CustomerOutputGate' --compactcd apps/platform && ./vendor/bin/sail bin pint --dirty --format agentgit diff --checkgit diff --name-onlygit status --short
User Scenarios & Testing
User Story 1 - Build a Safe Internal Model (Priority: P1)
As an internal TenantPilot consumer developer, I can supply a successful Spec 437 comparable result envelope and receive a deterministic internal-only model whose identity and protected values cannot be displayed exactly.
Why this priority: It establishes the only in-scope capability and the security boundary shared by all three target mappings.
Independent Test: A valid envelope for each supported target yields the fixed model contract, while invalid or blocked envelopes yield null.
Acceptance Scenarios:
- Given a valid successful Spec 437 envelope, When the builder runs, Then it returns the fixed boundary metadata, protected identity label, and target-specific sections.
- Given a blocked, malformed, wrong-workload, wrong-version, or mismatched envelope, When the builder runs, Then it returns
nulland creates no alternate model. - Given an Evidence, Resource, OperationRun, raw payload, or normalized payload object instead of the envelope, When the public API is invoked, Then the input is rejected by type/shape and no fallback occurs.
User Story 2 - Preserve Safe Material Meaning (Priority: P1)
As an internal operator-experience designer, I need safe state, enum, count, and presence semantics to change predictably without exact protected values affecting the model.
Why this priority: A presentation model is useful only if it reflects safe material changes while remaining stable across protected-value substitutions.
Independent Test: Safe booleans/enums/counts/presence change only their expected output fields; protected exact-value or unknown-key changes with identical safe semantics leave the model byte-equivalent.
Acceptance Scenarios:
- Given
enabled,require_tls, a safe enum, or a safe marker count changes, When the model is rebuilt, Then only the corresponding allowlisted output changes. - Given an upstream protected exact value changes and Spec 437 produces a successful envelope with unchanged valid marker presence/count/state, When only that resulting envelope is supplied to this builder, Then the model is identical.
- Given input keys are reordered or unknown keys are added, When the model is rebuilt, Then fixed field/section order and output bytes remain unchanged.
User Story 3 - Preserve Existing Truth Boundaries (Priority: P1)
As a platform reviewer, I need proof that deriving the internal model does not create or mutate database, coverage, OperationRun, UI, customer-output, or trigger truth.
Why this priority: The model must remain a pure adapter and must not silently become CoverageLevel::Renderable or customer proof.
Independent Test: Focused database snapshots and dependency/diff review show no writes, no forbidden dependencies, and no product-surface or customer-output files changed.
Acceptance Scenarios:
- Given existing Evidence, Resource, ResourceType, and OperationRun rows, When the builder runs on a separately supplied envelope, Then row counts and relevant attributes remain unchanged.
- Given implementation files are reviewed, When dependencies and changed paths are inspected, Then no ReadModel, old renderer, CustomerOutputGate, report, UI, route, job, listener, console, migration, enum, or catalog integration exists.
Edge Cases
- Top-level
resource_typeand nestedpayload.resource_typedisagree. comparable=trueis missing, false, or non-boolean;blockeris non-null.payloadis absent, list-shaped, or uses another workload/shape/version/input marker.comparable_hash, identity HMAC, source key, or provenance payload hash is missing or malformed.- Identity source key does not match
resource_type:field:value_hmac. - Required target sections are absent or list-shaped.
- An allowlisted safe boolean/enum/priority has the wrong type or value.
- A required redacted marker has inconsistent
redacted,present,count, andstatevalues. - Unknown keys exist beside valid target data; they are ignored and never counted or emitted.
- Empty target sections remain valid when the Spec 437 shape permits them and produce zero counts.
Input Contract
The public contract is:
public function build(array $comparableResult): ?array
The input MUST be the complete successful result returned by ExchangePowerShellComparablePayloadBuilder, not the nested comparable payload alone.
Required top-level values:
comparable === trueblocker === nullresource_typeis exactlytransportRule,remoteDomain, orinboundConnectorpayloadis an associative arraycomparable_hashis a lowercase 64-character SHA-256 value
Required nested values:
payload.workload === exchangepayload.resource_typematches top-levelresource_typepayload.comparable_payload_shape_version === exchange-powershell-comparable-payload-v1payload.compare_input === normalized_payload_onlypayload.identity.fieldis a non-empty stringpayload.identity.value_hmacis a lowercase SHA-256 valuepayload.identity.source_keyexactly matchesresource_type:field:value_hmacpayload.redaction.protected_values_use_safe_markers === truepayload.provenance.payload_hashis a lowercase SHA-256 valuepayload.provenance.evidence_state === content_backedpayload.provenance.coverage_level === content_backedpayload.provenance.capture_outcome === captured- all target-required projection sections exist as arrays; an empty section is valid, while any non-empty list-shaped section is malformed
Required target invariants:
- The canonicalized
remoteDomainidentity field (lowercase with non-alphanumeric characters removed) is notdomainname,displayname, orname, andremote_domain_class.domain_name_is_identity === false; any missing, true, or malformed identity safety marker returnsnull. - When present,
inboundConnector.routing.connector_type,routing.enabled, androuting.require_tlsMUST equal their canonicalmaterialcounterparts; disagreement returnsnull. - Recognized allowlisted
transportRulecondition/exception fields and recognized non-scalar-safe action fields MUST be valid redacted markers when present. - Recognized allowlisted
remoteDomainsetting fields other thanauto_reply_enabledMUST be valid redacted markers when present. - Recognized allowlisted
inboundConnectorrouting fields other than the three material duplicates MUST be valid redacted markers when present. - Every present entry in
protected_valuesandsensitive_values, including entries whose field name is not display-allowlisted, MUST be an exact canonical redacted marker. Raw scalars, lists, nested non-marker maps, or markers with additional keys returnnull.
Envelope integrity validation MUST recompute Spec 437's comparable_hash: remove payload.provenance, remove null-valued associative entries, recursively sort associative maps using the Spec 437 natural case-insensitive key ordering while preserving list order, JSON-encode with unescaped slashes and throwing errors, hash with SHA-256, and compare in constant time with the top-level hash. A changed key inside the hashed region is accepted only when the supplied hash is recomputed; provenance-only changes remain outside the hash by Spec 437 contract.
This hash check proves canonical payload consistency only. It is an unkeyed hash, deliberately excludes provenance, and does not authenticate origin or evidence state. The API is therefore a trusted in-process boundary: a future consumer MUST pass the direct Spec 437 service result and MUST NOT treat deserialized or user-supplied arrays as authenticated comparable provenance.
Invalid input returns null. No exception taxonomy, blocker family, or fallback output is introduced.
Forbidden Input And Fallbacks
The builder MUST NOT accept or resolve TenantConfigurationResourceEvidence, TenantConfigurationResource, TenantConfigurationResourceType, OperationRun, raw provider payload, normalized payload, stdout, stderr, transcript, provider response, credential/permission evidence, ReadModel, report, Review Pack, PDF, or customer-output models.
There is no fallback from invalid comparable input to Evidence, normalized/raw data, OperationRun context, the old Exchange/Teams renderer, generic stringification, or display/name/domain identity.
Output Contract
A successful build returns exactly this top-level order:
resource_type
workload
model_version
internal_only
customer_output
certified
restore_ready
identity_label
sections
Fixed values:
workload = exchangemodel_version = exchange-internal-render-model-v1internal_only = truecustomer_output = falsecertified = falserestore_ready = falseidentity_label = { label: "Protected stable identity", protected: true }
No hidden metadata bag is allowed.
identity_label always emits label and then protected. Every section always emits every field listed below in the listed order. An absent optional safe scalar is emitted as null; count fields are non-negative integers and presence fields are booleans.
Common Mapping Semantics
- Exact identity field, HMAC, source key, comparable hash, payload hash, source metadata, and provenance are validation inputs only and MUST NOT be emitted.
- Unknown non-allowlisted field names are ignored for output and counts and MUST NOT change the model. The only stricter exception is
protected_values/sensitive_values: unknown field names there remain undisplayed and uncounted, but every associated value MUST still pass exact marker-key-set validation. - Missing or malformed required sections and malformed allowlisted fields/markers return
null. configured_field_countcounts recognized allowlisted keys with a non-null safe scalar or a marker whosepresentis true.redacted_field_countcounts recognized allowlisted fields represented by valid redacted markers.redacted_value_countis the sum of those markers' non-negativecountvalues.- Unknown keys and the duplicate validation-only
protected_values/sensitive_valuessections MUST NOT contribute to display counts. - Priority accepts a non-negative integer or digit string and is emitted as an integer.
- A safe-enum input MUST be a non-empty string. Canonicalize it by trimming, removing every non-ASCII-alphanumeric character, and lowercasing; then use only this fixed token map:
enabled -> enabled,disabled -> disabled,enforce -> enforce,audit -> audit,auditandnotify -> audit_and_notify,wrap -> wrap,ignore -> ignore,reject -> reject,append -> append,prepend -> prepend,partner -> partner, andonpremises -> on_premises. Any other token returnsnullfor the whole model. - A marker is valid only when it is an associative array whose key set is exactly
redacted,present,count, andstate; input key order is irrelevant.redacted=true,presentis boolean,countis a non-negative integer, andstateis consistent:absentfor false/zero,presentfor true/one,present_multiplefor true/count greater than one. Additional keys make the marker malformed. - Collections are sorted only where order is non-semantic; fixed sections and fields retain the contract order defined below.
- Protected exact-value stability is compositional: the mandatory Spec 437 regression proves upstream exact-value variants collapse to the same valid safe-marker semantics, while the Spec 438 Unit case supplies only those valid comparable-envelope semantics and proves identical render output. No exact protected value is injected into or accepted by the Spec 438 API.
transportRule Mapping
Required input sections: material, conditions, actions, exceptions, protected_values, and sensitive_values.
Fixed output section order:
state:label,enabled,state; labelState, nullable boolean, and nullable enum (enabled/disabled).execution:label,mode; labelExecutionand nullable enum (enforce/audit/audit_and_notify).priority:label,value; labelPriorityand canonical non-negative integer ornull.conditions_summary:label,configured_field_count,protected_values_present,redacted_field_count,redacted_value_count; labelConditions summary.actions_summary:label,delete_message,disclaimer_fallback_action,disclaimer_location,configured_field_count,protected_values_present,redacted_field_count,redacted_value_count; labelActions summary, nullable safe scalar fields, and fixed counts.exceptions_summary:label,configured_field_count,protected_values_present,redacted_field_count,redacted_value_count; labelExceptions summary.redaction_summary:label,redacted_field_count,redacted_value_count; labelRedaction summary, with totals from the three display summary sections only.
For the three configured summaries, protected_values_present is true exactly when at least one recognized marker has present=true.
Allowlisted condition keys: from, recipient_domain_is, sender_domain_is, sent_to, subject_contains_words, subject_or_body_contains_words, body_contains_words.
Allowlisted action keys: apply_html_disclaimer_text, apply_html_disclaimer_fallback_action, apply_html_disclaimer_location, delete_message, redirect_message_to, header_contains_words, set_header_value.
Allowlisted exception keys: except_if_from and except_if_recipient_domain_is.
Rule names, display names, raw conditions/actions/exceptions, patterns, words, phrases, headers, email addresses, domains, senders, recipients, and routing values MUST NOT appear.
remoteDomain Mapping
Required input sections: remote_domain_class, settings, protected_values, and sensitive_values.
Fixed output section order:
classification:label,kind; labelClassification, with nullabledefault/customderived only fromis_default.delivery_settings:label,configured_field_count,protected_values_present,redacted_field_count,redacted_value_count; labelDelivery settings, overauto_forward_enabled,target_delivery_domain,tnef_enabled, andtrusted_mail_outbound_enabled.message_settings:label,configured_field_count,protected_values_present,redacted_field_count,redacted_value_count; labelMessage settings, overallowed_oof_type,mail_tips_access_level, andmail_tips_access_scope.feature_states:label,auto_reply_enabled; labelFeature statesand nullable exact boolean.protected_settings_summary:label,configured_field_count,protected_values_present,redacted_field_count,redacted_value_count; labelProtected settings summary, aggregated across all eight allowlisted settings without double-countingprotected_valuesorsensitive_values.redaction_summary:label,redacted_field_count,redacted_value_count; labelRedaction summary, with totals from the allowlisted settings only.
Exact domain, domain label, name, display name, exact identity, raw routing, and protected setting values MUST NOT appear. domain_name_is_identity is validation-only, MUST be exactly false, and MUST NOT be emitted.
inboundConnector Mapping
Required input sections: material, routing, protected_values, and sensitive_values.
Fixed output section order:
connector_state:label,enabled; labelConnector stateand nullable boolean.connector_type:label,value; labelConnector typeand nullable canonicalpartner/on_premises.tls_state:label,require_tls; labelTLS stateand nullable boolean.routing_summary:label,sender_domains_present,sender_domain_count,sender_ip_addresses_present,sender_ip_address_count,associated_accepted_domains_present,associated_accepted_domain_count,smart_hosts_present,smart_host_count,routing_metadata_present,routing_metadata_field_count; labelRouting summary.routing_metadata_presentis true when at least one recognized marker forrestrict_domains_to_ip_addresses,cloud_services_mail_enabled, orconnector_sourcehaspresent=true;routing_metadata_field_countis the number of those three recognized markers withpresent=true.protected_values_summary:label,tls_certificate_name_present,tls_certificate_name_count,comment_present,redacted_field_count,redacted_value_count; labelProtected values summary. Redacted totals cover the named non-duplicate routing allowlist only.redaction_summary:label,redacted_field_count,redacted_value_count; labelRedaction summary, with totals from all named non-duplicate routing allowlist fields only.
routing.connector_type, routing.enabled, and routing.require_tls are validation duplicates and MUST NOT create duplicate display truth; when present, they MUST agree with the canonicalized material values, and the display values come only from material.
Connector name, display name, raw IPs/CIDRs, domains, hosts, smart hosts, certificate names, comments, connector source values, and other routing/TLS values MUST NOT appear.
Functional Requirements
- FR-438-001: The system MUST add exactly one local final
ExchangePowerShellInternalRenderModelBuilderor repo-canonical equivalent. - FR-438-002: The public API MUST be
build(array $comparableResult): ?arrayand MUST accept only the complete successful Spec 437 result envelope by shape. - FR-438-003: Invalid, blocked, unsupported, mismatched, unsafe, or malformed input MUST return
nullwith no fallback. - FR-438-004: Supported resource types MUST be exactly
transportRule,remoteDomain, andinboundConnector. - FR-438-005: The builder MUST validate top-level success markers, nested workload/type/version/input markers, canonical comparable-hash integrity, identity anchor shape, redaction marker, provenance gates, target-required sections, remote-domain identity safety, and inbound duplicate-field consistency.
- FR-438-006: The builder MUST emit the fixed top-level output and fixed target section/field order defined above.
- FR-438-007: Every emitted field MUST be explicitly allowlisted; no recursive pass-through, unknown-key section, generic stringification, or JSON dump is allowed.
- FR-438-008: Unknown non-allowlisted keys MUST NOT affect counts, ordering, or output; values under unknown
protected_values/sensitive_valuesfield names MUST nevertheless pass canonical marker validation. - FR-438-009: Malformed allowlisted scalar values or redacted markers MUST return
null. - FR-438-010: Exact identity, identity field/HMAC/source key, comparable/payload hashes, source metadata, provenance, and raw/provider values MUST NOT be emitted.
- FR-438-011: Protected values MUST be represented only through fixed presence/count/redacted-state semantics.
- FR-438-012: Count semantics MUST follow the definitions in Common Mapping Semantics and MUST avoid duplicate protected/sensitive-section counting.
- FR-438-013: Safe material changes MUST affect only the corresponding output fields.
- FR-438-014: Protected exact-value changes with unchanged valid safe semantics MUST NOT change the model.
- FR-438-015: The same logical envelope with different key order, unknown keys, or volatile metadata MUST produce byte-equivalent canonical output.
- FR-438-016: The builder MUST NOT depend on Evidence, Resource, ResourceType, OperationRun, ReadModel, old ExchangeTeams renderer, CustomerOutputGate, report, Review Pack, PDF, route, UI, job, listener, console, migration, registry, or catalog classes.
- FR-438-017: Builder invocation MUST create or mutate no database row, latest pointer, coverage level, capture outcome, source metadata, claim state, or OperationRun state.
- FR-438-018: The implementation MUST NOT persist
CoverageLevel::Renderable, a render model/payload/cache, or any new truth. - FR-438-019: The implementation MUST add no UI, route, navigation, global search, asset, report, customer output, certification, restore, trigger, migration, tenant ownership, enum, status family, interface, registry, DTO family, or generic presentation framework.
- FR-438-020: Existing
ExchangeTeamsRenderableSummaryBuilder,CoverageV2ReadinessReadModel,CoverageV2ResourceInstancesTable,CustomerOutputGate, OperationRun types/catalog, and completed Specs 429-437 MUST remain unchanged.
Non-Functional Requirements
- NFR-438-001 - Determinism: Equal logical safe input MUST yield byte-equivalent output with fixed section/field order.
- NFR-438-002 - Data minimization: Output MUST contain only fixed boundary metadata, protected identity label, and allowlisted safe section values.
- NFR-438-003 - Fail closed: Any malformed required contract element or allowlisted value MUST yield
null. - NFR-438-004 - Purity: The builder MUST perform no I/O, remote call, database lookup/write, event dispatch, queue work, logging of payload data, caching, or session mutation.
- NFR-438-005 - Proportionality: Runtime scope is one class. Tests MUST protect leakage, determinism, and immutability without creating a broad discovery framework.
- NFR-438-006 - Provider containment: Exchange semantics remain inside the provider-owned service seam and do not become platform-core vocabulary or persistence.
- NFR-438-007 - Boundary language: Section labels and values MUST avoid customer-ready, compliance, security verdict, risk, remediation, certification, and restore guidance. Fixed false top-level boundary keys are the only permitted certification/restore wording.
Forbidden Output Corpus
Tests MUST prove output excludes supplied examples of:
- rule/display/connector names and exact stable identity
- identity HMAC, source key, comparable hash, payload hash, provenance
- patterns, words, phrases, header values, email addresses, senders, recipients, domains
- IP addresses, CIDRs, hosts, smart hosts, certificate/TLS names, comments, connector-source/routing values
- raw payload, normalized payload, stdout, stderr, transcript, provider response
- credentials, tokens, authorization headers, cookies, permission evidence
- customer-ready, compliant/non-compliant, secure/insecure, critical/risk, remediation/fix/recommended language
Evidence, Resource, And Coverage Immutability
Spec 438 MUST NOT create or mutate Evidence, Resource, ResourceType, ProviderConnection, OperationRun, EnvironmentReviewSection, Review Pack, StoredReport, report, PDF, cache, or any latest pointer. Existing evidence remains content_backed. render_model_available is an in-memory success description only and MUST NOT be persisted as CoverageLevel::Renderable or any new field/state.
Data And Migration Impact
- New entities/tables/columns/indexes: none.
- Migration: none.
- Tenant ownership changes: none.
- Environment variables: none.
- Queues/schedules/listeners/commands: none.
- Storage/cache/assets/provider permissions/external calls: none.
Assumptions
- Spec 437's result envelope and shape version remain stable for implementation.
- A successful result includes the current provenance and redaction markers produced by
ExchangePowerShellComparablePayloadBuilder. - The builder is invoked only by tests in this slice; a runtime consumer is intentionally deferred and is not implied.
- Any future invocation is trusted in-process composition from the direct Spec 437 result. Canonical hash equality is not origin authentication and cannot replace caller ownership, RBAC, evidence-currentness, or consumer-boundary proof.
- Missing optional safe scalar values are represented as
nullin their fixed output fields; missing required contract structure returnsnullfor the whole model.
Risks
- Premature layer risk: No current consumer exists. Mitigation: one local class only, no shared contract, and no consumer integration.
- Leakage risk: Exact protected values could pass through via generic traversal. Mitigation: target field allowlists, marker validation, forbidden corpus tests, and no metadata bag.
- Double-count risk: Spec 437 repeats protected/sensitive markers in validation sections. Mitigation: display counts use only explicitly named primary target sections.
- Overclaim risk: Internal model availability could be read as persisted renderability or customer readiness. Mitigation: fixed false boundary flags and no ReadModel/claim/customer integration.
- Legacy collision risk: Old renderer can expose provider-near values. Mitigation: source dependency guard and no fallback.
- Test bloat risk: Broad file discovery could enter the fast lane. Mitigation: focused Unit/Feature tests plus explicit diff/dependency close-out.
Open Questions
- Blocking product question: Which current-release internal operator workflow and real consumer requires this model? Until a consumer-owning spec answers this, the standalone candidate fails PROP-001 and Spec Approval Rubric Rule C.
The technical input envelope, null-on-invalid behavior, target allowlists, count semantics, canonical enum casing, output version, and no-product-surface boundary are otherwise fixed.
Success Criteria
Measurable Outcomes
- SC-438-001: All three supported Spec 437 success envelopes produce the exact fixed internal model contract.
- SC-438-002: Every blocked/malformed/unsupported input case returns
nulland produces no fallback output. - SC-438-003: The complete leakage corpus has zero exact-value occurrences in encoded output.
- SC-438-004: Same logical safe input, reordered input keys, unknown keys, and volatile metadata produce byte-equivalent output.
- SC-438-005: Safe booleans/enums/counts/presence changes affect only expected fields; protected exact-value substitutions with unchanged safe semantics produce identical output.
- SC-438-006: Database immutability proof shows zero created rows and zero relevant attribute changes.
- SC-438-007: Changed-file and dependency proof shows one runtime class, two focused test files, active Spec 438 artifacts, and no forbidden runtime surface.
- SC-438-008: Focused tests, required regressions, Pint, and
git diff --checkpass or exact pre-existing/environmental limitations are documented.
Acceptance Criteria
Scope And Input
- Exactly one builder and three targets.
- Complete successful Spec 437 result envelope only.
nullfor any invalid or unsafe envelope.- No Evidence/raw/normalized/OperationRun/old-renderer fallback.
Output And Safety
- Fixed deterministic top-level and target-specific section order.
- Fixed internal/customer/certification/restore boundary values.
- Generic protected identity label only.
- No exact protected values, hashes, provenance, or hidden metadata.
- Unknown fields ignored; malformed allowlisted values block.
- Count semantics and canonical enum/priority normalization proven.
Truth And Surface Boundaries
- No persistence or
CoverageLevel::Renderablepromotion. - No Evidence/Resource/ResourceType/OperationRun mutation.
- No ReadModel, UI, route, report, Review Pack, PDF, customer-output, certification, restore, job, listener, schedule, command, migration, enum, status, registry, interface, or DTO family.
- Browser and Human Product Sanity are N/A.
Validation
- Unit, focused Feature, targeted regressions, and changed-file/dependency review complete.
- Pint and
git diff --checkpass. - The consumer-owning spec's future implementation report records branch/HEAD/dirty state, files changed, prerequisite proof, adapted target/boundary matrices, tests, its browser rationale, Livewire/provider/global-search/action/asset posture, deployment impact, and deferred work; standalone Spec 438 creates no report.
Consumer-Owned Implementation Report Reference
Standalone Spec 438 MUST NOT create an implementation report while its gates remain FAIL. If a consumer-owning spec absorbs this contract and passes its own gates, that spec's implementation report SHOULD adapt and include the following evidence:
- Candidate gate, branch, HEAD, dirty state before/after, and files changed.
- Spec 437 prerequisite/envelope proof and proportionality result.
- Public API, null-on-invalid, no-fallback, target mapping, count semantics, canonicalization, leakage, determinism, material-change, and protected-value stability proof.
- Evidence/Resource/ResourceType/OperationRun immutability and no persisted Renderable proof.
- No ReadModel/old renderer/customer/report/Review Pack/PDF/UI/route/trigger/migration/tenant-id proof.
- Test/lane results, browser N/A, Human Product Sanity N/A, Livewire v4, provider registration, global search, destructive/high-impact action, asset strategy, deployment impact, visible complexity, no completed-spec rewrite assertion, and deferred work.
Required target matrix:
| Type | Spec 437 comparable prerequisite | Internal model available | Persisted renderable | Protected exact values exposed | Blocker |
|---|---|---|---|---|---|
transportRule |
Yes | implementation proof | No | No | null on invalid input |
remoteDomain |
Yes | implementation proof | No | No | null on invalid input |
inboundConnector |
Yes | implementation proof | No | No | null on invalid input |
Required boundary matrix:
| Area | State |
|---|---|
| Internal model | Derived only |
| Evidence mutation | No |
| Resource mutation | No |
| Coverage-level promotion | No |
| ReadModel integration | No |
| UI | No |
| Customer output | No |
| Reports/Review Packs/PDF | No |
| Certification | No |
| Restore | No |
| OperationRun | No new operation or input |
| Migration | No |
Follow-Up Spec Candidates
- First internal operator consumer of the model, with explicit Product Surface, RBAC, browser, and Human Product Sanity gates.
- Any customer/report/Review Pack/PDF consumption with a separate content-safety and CustomerOutputGate contract.
- Comparator/delta semantics if a real consumer requires them.
- Additional Exchange or Teams target mappings only after their comparable contracts are proven.
No follow-up is pre-approved by this spec.
Final Candidate Gate
Preparation candidate decision: FAIL.
Spec Readiness Gate: FAIL for implementation because the candidate lacks a current operator workflow and real consumer. The technical contract may be reused only as reference material: resolving the blocker requires migrating and adapting it inside a consumer-owning spec, not documenting a consumer while executing this standalone task package.
If a future consumer-owning spec resolves the blocker, implementation PASS would additionally require the local-only proportionality boundary, complete-envelope input, fixed allowlists, leakage/determinism/immutability proof, no persistence, and no customer-output posture to remain intact.
For this unchanged standalone package, a runtime consumer remains a scope violation. The consumer-owning spec that resolves the product blocker must replace these gates/tasks with its own authorized integration scope. Independently, FAIL if implementation introduces a shared presentation layer, second mapping class, interface/registry/DTO family, generic traversal/stringification, exact protected-value leakage, Evidence/raw/normalized/OperationRun fallback, old renderer reuse, persistence or Renderable promotion, unauthorized ReadModel/UI/customer/report integration, migration, new operation type, or insufficient focused safety proof.