# Feature Specification: Spec 361 - Report and Evidence Reconciliation Adapters **Feature Branch**: `361-report-evidence-reconciliation` **Created**: 2026-06-06 **Status**: Draft **Input**: User-provided Spec 361 draft from `/Users/ahmeddarrazi/.codex/attachments/a0817e2c-be58-40c1-9108-1e1880e423d4/pasted-text.txt`, reconciled against current repo truth ## Spec Candidate Check *(mandatory - SPEC-GATE-001)* - **Problem**: current `OperationRun` monitoring can still leave report-family or evidence-family generation runs queued, running, or stale even after the repo already has a scope-safe artifact that proves the intended output exists. - **Today's failure**: operators can be forced to compare Operations, Review Pack, Evidence Snapshot, and report surfaces manually to decide whether a run is genuinely unfinished or whether the artifact already exists and the run is now ghost state. - **User-visible improvement**: existing operations surfaces, related-artifact links, and current artifact truth surfaces will show one honest execution story: reconcile only when a safe artifact already proves success, otherwise stay pending or attention-required without false calm. - **Smallest enterprise-capable version**: extend the current `OperationRun` reconciliation registry for repo-verifiable artifact families only: `tenant.evidence.snapshot.generate` and `environment.review_pack.generate`. Keep generic `StoredReport` reconciliation fail-closed unless current repo truth already proves direct causal linkage without new persistence. - **Explicit non-goals**: no new table, no new `OperationRun` status column, no new queue family, no new report renderer, no new PDF/HTML work, no new `stored_report.generate` operation type, no sync/backup/restore adapter expansion, no customer portal work, no destructive action changes, and no legacy compatibility layer. - **Permanent complexity imported**: two bounded reconciliation adapters, at most one small derived proof/helper path, focused Unit/Feature/Browser tests, and small operator-facing copy or link adjustments on existing monitoring and artifact surfaces. - **Why now**: Spec 359 merged the review-compose adapter baseline, and current `platform-dev` already contains the shared registry, `context.reconciliation`, and `context.dispatch` seams. The next repo-real trust gap is artifact-backed reconciliation for evidence and review-pack outputs. - **Why not local**: fixing one banner, one presenter, or one detail page would not repair the shared run lifecycle truth across Operations, run detail, Review Pack, and Evidence Snapshot surfaces. - **Approval class**: Core Enterprise - **Red flags triggered**: new abstraction/helper pressure and shared operator-facing status semantics. Defense: the repo already has real adapter-backed reconciliation consumers (`restore.execute`, `environment.review.compose`), and queue correctness plus audit-visible truth justify a bounded extension instead of more special cases. - **Score**: Nutzen: 2 | Dringlichkeit: 2 | Scope: 2 | Komplexitaet: 1 | Produktnaehe: 1 | Wiederverwendung: 2 | **Gesamt: 10/12** - **Decision**: approve ## Repo Truth Reconciliation The user draft is directionally correct, but current repo truth narrows the safe implementation slice: 1. `docs/product/spec-candidates.md` still records `no safe automatic next-best-prep target`. This package is therefore an explicit manual promotion from the user-provided draft, not an auto-selected queue item. 2. `platform-dev` already contains the merged Spec 359 runtime baseline at `3a750726 feat: implement review compose reconciliation adapter (spec 359) (#430)`, plus the shared `OperationRunReconciliationRegistry`, `OperationRunCorrelationResolver`, and `context.dispatch` write path in `OperationRunService`. 3. `EvidenceSnapshot` and `ReviewPack` are safe primary artifact families for this slice because they already carry workspace/environment scope, lifecycle status, fingerprint data, and direct `operation_run_id` linkage. 4. Generic `StoredReport` truth is weaker than the draft assumes: the model has no direct `operation_run_id`, no status enum, and no canonical readiness lifecycle. Spec 361 therefore treats generic report reconciliation for `permission.posture.check` and `entra.admin_roles.scan` as explicitly unsupported. This package must not add any `StoredReport`-backed success path; any future direct report-artifact proof contract belongs in a separate follow-up spec. 5. Existing review-pack productization and report-output semantics from Specs 347, 349, 351, 356, and 357 are context only. This spec must reuse those seams and must not reopen rendered-report or disclosure-policy scope. 6. Related existing specs are context only and must not be rewritten: - Spec 358 - generic queue-truth baseline - Spec 359 - merged review-compose adapter baseline - Spec 360 - canonical cutover follow-through context - Specs 347/349/351/356/357 - review-pack and rendered-report productization context ## Spec Scope Fields *(mandatory)* - **Scope**: workspace, tenant, canonical-view - **Primary Routes**: - `/admin/workspaces/{workspace}/operations` - `/admin/workspaces/{workspace}/operations/{run}` - existing environment-scoped Evidence Snapshot routes via `App\Filament\Resources\EvidenceSnapshotResource` - existing environment-scoped Review Pack routes via `App\Filament\Resources\ReviewPackResource` - **Data Ownership**: - `operation_runs` remain the only execution and reconciliation truth - `evidence_snapshots` remain the only persisted evidence-generation truth - `review_packs` remain the only persisted review-pack output truth - `stored_reports` remain supporting artifact context only unless current repo truth proves safe direct reconciliation without new persistence - **RBAC**: - existing workspace-first `OperationRun` access rules remain authoritative - existing environment-scoped `EvidenceSnapshot` and `ReviewPack` policies remain authoritative - non-members and out-of-scope actors remain `404` - no new capability strings are introduced For canonical-view specs, the spec MUST define: - **Default filter behavior when tenant-context is active**: the Operations hub remains workspace-scoped with explicit environment filters; adapter decisions must rely on the run's recorded workspace/environment scope and never on remembered environment state or current page filters. - **Explicit entitlement checks preventing cross-tenant leakage**: no adapter may reconcile to an artifact outside the run's workspace and managed environment, and any related-artifact link must resolve only through current-scope canonical routes. ## UI Surface Impact *(mandatory - UI-COV-001)* Does this spec add, remove, rename, or materially change any reachable UI surface? - [ ] No UI surface impact - [x] 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 - [x] Status/evidence/review presentation changed - [ ] Workspace/environment context presentation changed ## UI/Productization Coverage *(mandatory when UI Surface Impact is not "No UI surface impact")* - **Route/page/surface**: - workspace operations hub (`App\Filament\Pages\Monitoring\Operations`) - canonical run detail (`App\Filament\Pages\Operations\TenantlessOperationRunViewer`) - existing Evidence Snapshot related-context and detail surfaces - existing Review Pack related-context and detail surfaces - **Current or new page archetype**: existing monitoring/detail family plus existing governance-artifact detail family - **Design depth**: Domain Pattern Surface - **Repo-truth level**: repo-verified - **Existing pattern reused**: current `OperationRun` monitoring family, current artifact-truth/detail family, current related-artifact link conventions - **New pattern required**: none; this is a run-truth follow-through inside existing pattern families - **Screenshot required**: no; one bounded browser smoke is sufficient unless implementation produces a materially new visible hierarchy - **Page audit required**: no new page-report identity is required; existing monitoring and artifact page reports remain the anchors - **Customer-safe review required**: no; touched copy remains operator-facing and diagnostic - **Dangerous-action review required**: no; this spec adds no new mutation surface and no destructive action - **Coverage files updated or explicitly not needed**: - [ ] `docs/ui-ux-enterprise-audit/route-inventory.md` - [ ] `docs/ui-ux-enterprise-audit/design-coverage-matrix.md` - [ ] `docs/ui-ux-enterprise-audit/page-reports/...` - [ ] `docs/ui-ux-enterprise-audit/strategic-surfaces.md` - [ ] `docs/ui-ux-enterprise-audit/grouped-follow-up-candidates.md` - [ ] `docs/ui-ux-enterprise-audit/unresolved-pages.md` - [x] `N/A - existing monitoring and artifact page families already cover these reachable surfaces` - **No-impact rationale when applicable**: no new page family, route family, or navigation entry is added; the slice stays inside existing operations, review-pack, and evidence artifact surfaces ## Cross-Cutting / Shared Pattern Reuse *(mandatory when the feature touches notifications, status messaging, action links, header actions, dashboard signals/cards, alerts, navigation entry points, evidence/report viewers, or any other existing shared operator interaction family; otherwise write `N/A - no shared interaction family touched`)* - **Cross-cutting feature?**: yes - **Interaction class(es)**: status messaging, action links, evidence/report viewers, related-artifact diagnostics - **Systems touched**: - `App\Services\AdapterRunReconciler` - `App\Services\OperationRunService` - `App\Support\Operations\Reconciliation\OperationRunReconciliationRegistry` - `App\Support\OperationRunLinks` - `App\Support\Ui\GovernanceArtifactTruth\ArtifactTruthPresenter` - `App\Support\OpsUx\GovernanceRunDiagnosticSummaryBuilder` - existing Review Pack and Evidence Snapshot resource/detail surfaces - **Existing pattern(s) to extend**: existing reconciliation metadata path, existing operations monitoring/detail language, current artifact-truth/detail family, and current canonical artifact links - **Shared contract / presenter / builder / renderer to reuse**: `OperationRunService::updateRunWithReconciliation()`, `OperationRun::reconciliation()`, `OperationRunLinks`, `ArtifactTruthPresenter`, `GovernanceRunDiagnosticSummaryBuilder`, and current Evidence Snapshot / Review Pack resources - **Why the existing shared path is sufficient or insufficient**: the repo already has the right write seam and related-artifact presentation family, but the registry does not yet cover repo-verifiable artifact-backed success for evidence or review-pack outputs. - **Allowed deviation and why**: one bounded helper or proof object is allowed only if it removes duplicated scope/completeness checks across the two in-scope adapters. It must stay derived-only and local to current reconciliation work. - **Consistency impact**: reconciled evidence or review-pack outcomes must read consistently across start feedback, operations list/detail, and related-artifact views, while unsupported generic stored-report families must stay attention-first instead of silently appearing successful. - **Review focus**: no second reconciliation write path, no new stored-report lifecycle model, no artifact mutation from adapters, and no false success from "latest artifact exists" heuristics alone. ## OperationRun UX Impact *(mandatory when the feature creates, queues, deduplicates, resumes, blocks, completes, or deep-links to an `OperationRun`; otherwise write `N/A - no OperationRun start or link semantics touched`)* - **Touches OperationRun start/completion/link UX?**: yes, reuse-only - **Shared OperationRun UX contract/layer reused**: current `OperationRunService`, `OperationRunLinks`, registry-backed reconciliation, and existing operations monitoring/detail presenters - **Delegated start/completion UX behaviors**: existing queued toasts, canonical run links, canonical artifact links, current run-enqueued browser events, and current terminal notification path remain unchanged - **Local surface-owned behavior that remains**: placement and explanation text only - **Queued DB-notification policy**: unchanged - **Terminal notification path**: unchanged central lifecycle mechanism - **Exception required?**: none ## Provider Boundary / Platform Core Check *(mandatory when the feature changes shared provider/platform seams, identity scope, governed-subject taxonomy, compare strategy selection, provider connection descriptors, or operator vocabulary that may leak provider-specific semantics into platform-core truth; otherwise write `N/A - no shared provider/platform boundary touched`)* - **Shared provider/platform boundary touched?**: no - **Boundary classification**: N/A - **Seams affected**: N/A - **Neutral platform terms preserved or introduced**: `operation`, `artifact`, `review pack`, `evidence snapshot`, `reconciliation` - **Provider-specific semantics retained and why**: none - **Why this does not deepen provider coupling accidentally**: the slice uses platform-owned artifact and operation truth only - **Follow-up path**: none ## UI / Surface Guardrail Impact *(mandatory when operator-facing surfaces are changed; otherwise write `N/A`)* | Surface / Change | Operator-facing surface change? | Native vs Custom | Shared-Family Relevance | State Layers Touched | Exception Needed? | Low-Impact / `N/A` Note | |---|---|---|---|---|---|---| | Operations hub reconciliation copy and related-artifact state | yes | Native Filament + shared presenters | `OperationRun` monitoring family | page, detail, URL-query | no | no new action hierarchy | | Run detail reconciliation diagnostics and related links | yes | Native Filament + shared presenters | `OperationRun` monitoring family | detail | no | existing link model stays canonical | | Evidence Snapshot / Review Pack related-artifact surfaces | yes | Native Filament + shared artifact-truth helpers | artifact detail family | detail | no | no new route family or mutation | ## Decision-First Surface Role *(mandatory when operator-facing surfaces are changed)* | Surface | Decision Role | Human-in-the-loop Moment | Immediately Visible for First Decision | On-Demand Detail / Evidence | Why This Is Primary or Why Not | Workflow Alignment | Attention-load Reduction | |---|---|---|---|---|---|---|---| | Operations hub | Primary Decision Surface | Decide whether a run still needs follow-up | lifecycle, outcome, safe related-artifact state, next action | raw failures, reconciliation evidence, artifact details | primary because operators decide whether to wait, inspect, or move on here | stays aligned to monitoring workflow | removes manual cross-checking between run and artifact lists | | Run detail | Secondary Context Surface | Verify why a run was reconciled or left pending | reconciled reason, related artifact, scope-safe link | full context payload, existing diagnostic sections | secondary because it confirms a decision already hinted in the hub | stays aligned to detail workflow | keeps proof in one canonical place | | Evidence Snapshot / Review Pack detail | Tertiary Evidence / Diagnostics | Inspect the artifact that justified or blocked reconciliation | current artifact state | payloads, summary metadata, existing artifact diagnostics | tertiary because artifact pages prove evidence after the run decision | stays aligned to current artifact detail workflow | avoids duplicating full artifact truth on the run list | ## Audience-Aware Disclosure *(mandatory when operator-facing surfaces are changed)* | Surface | Audience Modes In Scope | Decision-First Default-Visible Content | Operator Diagnostics | Support / Raw Evidence | One Dominant Next Action | Hidden / Gated By Default | Duplicate-Truth Prevention | |---|---|---|---|---|---|---|---| | Operations hub | operator-MSP, support-platform | run status, honest reconciliation state, safe related artifact, next action | failures, related metadata, timestamps after explicit reveal by an actor already entitled to view the run | raw `context.reconciliation` evidence remains secondary and stays limited to the existing support/platform diagnostics path on the run host | `Open operation` or related artifact link | raw context stays diagnostic-only and is never default-visible | the hub states the outcome once; later sections add proof only | | Run detail | operator-MSP, support-platform | reconciled or not reconciled state with safe reason | full reconciliation metadata and existing failure detail after explicit reveal by an actor already entitled to view the run | raw payload inspection stays secondary and remains available only through the existing support/platform diagnostics path on the run host | `Open related artifact` when safe | low-level payloads remain subordinate and capability-gated where the host already distinguishes support/platform access | no second "success" summary beyond the canonical outcome | | Evidence Snapshot / Review Pack detail | operator-MSP, support-platform | current artifact state, scope-safe relation to the run, and concise artifact outcome summary | existing artifact diagnostics and related run context after explicit reveal by an actor already entitled to view the artifact | raw/support artifact evidence remains behind the existing host-specific diagnostics or payload affordance and never becomes the default first-read layer | `Open related operation` when safe | payloads, fingerprints, and raw evidence stay secondary and host-gated | artifact pages prove or explain one outcome only; they do not restate a competing default-visible success summary | Layer unlocks for changed surfaces: - **Decision content**: any actor who already passes the current workspace/environment and run/artifact view entitlement for the host surface. - **Operator diagnostics**: the same entitled actor, but only after an explicit reveal affordance on the host surface. - **Support / raw evidence**: only the existing support/platform diagnostics path on the current host surface; this spec introduces no new capability string and no widened raw-evidence audience. ## UI/UX Surface Classification *(mandatory when operator-facing surfaces are changed)* | Surface | Action Surface Class | Surface Type | Likely Next Operator Action | Primary Inspect/Open Model | Row Click | Secondary Actions Placement | Destructive Actions Placement | Canonical Collection Route | Canonical Detail Route | Scope Signals | Canonical Noun | Critical Truth Visible by Default | Exception Type / Justification | |---|---|---|---|---|---|---|---|---|---|---|---|---|---| | Operations hub | Monitoring / Queue / Workbench | Read-only Registry / Report Surface | Open the run or its proven artifact | row click to run detail | required | existing filters and contextual links only | unchanged | `/admin/workspaces/{workspace}/operations` | `/admin/workspaces/{workspace}/operations/{run}` | workspace route plus explicit environment filter | Operations / Operation | honest lifecycle plus safe artifact state | none | | Run detail | Record / Detail / Edit | Detail-first Operational Surface | Inspect related artifact proof | canonical detail page | N/A | existing navigation and related links only | unchanged | `/admin/workspaces/{workspace}/operations` | `/admin/workspaces/{workspace}/operations/{run}` | run-owned workspace/environment scope | Operation | reconciliation outcome and reason | none | | Evidence Snapshot / Review Pack detail | Record / Detail / Edit | Detail-first Operational Surface | Inspect the artifact proof and, if needed, follow the safe link back to the operation | canonical detail page | N/A | existing related links and host-owned secondary actions only | unchanged | existing Evidence Snapshot / Review Pack collection routes | existing Evidence Snapshot / Review Pack detail routes | artifact-owned workspace/environment scope plus current host context | Evidence Snapshot / Review Pack | current artifact truth and safe relation to the reconciled run | none | ## Operator Surface Contract *(mandatory when operator-facing surfaces are changed)* | Surface | Primary Persona | Decision / Operator Action Supported | Surface Type | Primary Operator Question | Default-visible Information | Diagnostics-only Information | Status Dimensions Used | Mutation Scope | Primary Actions | Dangerous Actions | |---|---|---|---|---|---|---|---|---|---|---| | Operations hub | MSP operator | Decide whether the run still needs attention | list/workbench | Is the artifact already proven, or is this run still unfinished? | lifecycle state, reconciled outcome, safe artifact state, next step | raw failures, detailed metadata, timestamps | lifecycle, artifact completeness, scope safety | none | existing inspect/open actions only | none | | Run detail | MSP operator or support | Confirm why the run reconciled or stayed pending | detail | What exact artifact truth did the run resolve against? | reconciled reason, related artifact, summary counts | raw `context.reconciliation`, raw context payload, deep diagnostic sections | lifecycle, artifact completeness, support state | none | open related artifact when safe | none | | Evidence Snapshot / Review Pack detail | MSP operator or support | Confirm whether the artifact really justifies or blocks reconciliation | detail | Does this artifact safely prove the run outcome for the current scope? | artifact readiness, scope-safe linkage, concise supporting summary | payloads, fingerprints, deeper host diagnostics, and related run context | artifact readiness, completeness/expiration, scope safety | none | open related operation when safe | none | ## Proportionality Review *(mandatory when structural complexity is introduced)* - **New source of truth?**: no - **New persisted entity/table/artifact?**: no - **New abstraction?**: yes, bounded adapter classes and at most one small derived proof/helper path - **New enum/state/reason family?**: no new family; only current reconciliation reason semantics may be reused or narrowly extended if behaviorally required - **New cross-domain UI framework/taxonomy?**: no - **Current operator problem**: artifact-producing runs can remain queued/running/stale even after the repo already has a safe artifact, forcing manual cross-checks and risking false calm or false alarm. - **Existing structure is insufficient because**: the current registry only covers restore and review-compose truth, while evidence and review-pack runs still lack artifact-backed reconciliation even though their artifact models already expose scope, lifecycle, and fingerprint proof. - **Narrowest correct implementation**: add only evidence snapshot and review-pack adapters over existing models and write seams; keep generic stored-report reconciliation deferred until direct causal proof exists. - **Ownership cost**: adapter maintenance, focused tests, and some monitoring/detail copy review. No migration, new artifact table, or new operator workflow family is introduced. - **Alternative intentionally rejected**: a generic "latest artifact exists => mark success" heuristic was rejected because it would overclaim success for unsupported or ambiguous report families, especially `StoredReport`. - **Release truth**: current-release truth ### Compatibility posture This feature assumes a pre-production environment. Backward compatibility, legacy aliases, migration shims, historical fixtures, and compatibility-specific tests are out of scope unless explicitly required by this spec. Canonical replacement is preferred over preservation. ## Testing / Lane / Runtime Impact *(mandatory for runtime behavior changes)* - **Test purpose / classification**: Unit, Feature, Browser - **Validation lane(s)**: fast-feedback, confidence, browser - **Why this classification and these lanes are sufficient**: adapter selection, proof evaluation, and metadata formatting are cheapest in Unit coverage; run finalization, scope safety, and artifact-link behavior require Feature coverage; one bounded Browser smoke is enough for existing Operations and artifact surfaces if visible operator wording changes. - **New or expanded test families**: one explicit Spec 361 Unit/Feature family and one bounded Browser smoke - **Fixture / helper cost impact**: moderate existing factory usage for `OperationRun`, `EvidenceSnapshot`, `ReviewPack`, `EnvironmentReview`, and workspace/environment membership; no new expensive global defaults should be introduced - **Heavy-family visibility / justification**: no heavy-governance family is added - **Special surface test profile**: monitoring-state-page - **Standard-native relief or required special coverage**: ordinary Feature coverage for existing Filament surfaces plus one bounded Browser smoke when UI wording changes - **Reviewer handoff**: reviewers must confirm generic `StoredReport` families stay fail-closed unless safe proof is demonstrated, that adapters do not mutate artifacts, and that proof commands stay scoped to Spec 361 plus contextual artifact regressions only - **Budget / baseline / trend impact**: none expected beyond one bounded browser smoke - **Escalation needed**: document-in-feature if generic stored-report proof remains insufficient; follow-up-spec only if the repo later needs a dedicated stored-report causality contract - **Active feature PR close-out entry**: Guardrail / Smoke Coverage - **Planned validation commands**: - `cd apps/platform && ./vendor/bin/sail php vendor/bin/pest tests/Unit/Support/Operations/Reconciliation tests/Feature/Operations/Spec361*` - `cd apps/platform && ./vendor/bin/sail php vendor/bin/pest tests/Feature/Evidence/GenerateEvidenceSnapshotJobTest.php tests/Feature/Evidence/EvidenceSnapshotResourceTest.php` - `cd apps/platform && ./vendor/bin/sail php vendor/bin/pest tests/Feature/ReviewPack/EnvironmentReviewDerivedReviewPackTest.php tests/Feature/ReviewPack/ReviewPackDownloadTest.php tests/Feature/ReviewPack/Spec347ReviewPackOutputContractTest.php` - `cd apps/platform && ./vendor/bin/sail php vendor/bin/pest tests/Browser/Spec361ArtifactReconciliationSmokeTest.php` - `cd apps/platform && ./vendor/bin/pint --dirty` - `git diff --check` ## User Scenarios & Testing *(mandatory)* ### User Story 1 - Reconcile evidence generation against an existing snapshot (Priority: P1) An operator sees a queued, running, or stale evidence-generation run and needs the run to finalize honestly when the matching `EvidenceSnapshot` already exists and is scope-safe, complete, and active. **Why this priority**: `EvidenceSnapshot` already has direct `operation_run_id`, lifecycle status, completeness state, and fingerprint signals, so this is the safest and highest-confidence artifact-backed reconciliation extension. **Independent Test**: seed a queued or stale `tenant.evidence.snapshot.generate` run plus a matching active, complete snapshot in the same workspace/environment, run reconciliation, and verify the run finalizes successfully with canonical related-artifact metadata. **Acceptance Scenarios**: 1. **Given** a queued or stale evidence-generation run and a matching active, complete snapshot with the same scope and fingerprint, **When** adapter reconciliation runs, **Then** the run becomes completed/succeeded and links to the snapshot safely. 2. **Given** a queued or stale evidence-generation run and a partial, failed, expired, or wrong-scope snapshot, **When** adapter reconciliation runs, **Then** the run does not claim success and remains pending or attention-required. --- ### User Story 2 - Reconcile review-pack generation against an existing pack (Priority: P1) An operator sees a queued, running, or stale review-pack generation run and needs the run to finalize honestly when the matching `ReviewPack` already exists, is ready, non-expired, and belongs to the same workspace/environment/review scope. **Why this priority**: review-pack output is already customer-facing and productized; ghost run state here directly damages trust in delivered governance artifacts. **Independent Test**: seed a queued or stale `environment.review_pack.generate` run plus a matching ready pack with canonical scope and fingerprint signals, run reconciliation, and verify the run finalizes successfully with canonical related-artifact metadata. **Acceptance Scenarios**: 1. **Given** a queued or stale review-pack run and a matching ready, non-expired pack for the same scope, **When** adapter reconciliation runs, **Then** the run becomes completed/succeeded and exposes the safe pack link through current operations surfaces. 2. **Given** a queued or stale review-pack run and a generating, failed, expired, incomplete, or wrong-scope pack, **When** adapter reconciliation runs, **Then** the run does not claim success and the operator sees honest follow-up state instead. --- ### User Story 3 - Keep unsupported generic report families honest (Priority: P2) An operator sees a queued or stale run for a report-producing operation such as `permission.posture.check` or `entra.admin_roles.scan` and must not see false success merely because a latest `StoredReport` exists without direct causal proof. **Why this priority**: the current `StoredReport` model is not strong enough to justify blind success. Preserving fail-closed behavior is a product-truth requirement and prevents overclaiming. **Independent Test**: seed queued or stale report-producing runs plus retained `StoredReport` rows and verify the run stays unresolved or diagnostic-first because Spec 361 does not allow any `StoredReport`-backed success path. **Acceptance Scenarios**: 1. **Given** a report-producing run and only a latest retained `StoredReport` with no direct causal proof, **When** reconciliation runs, **Then** the run does not finalize as succeeded from that report alone. 2. **Given** a future repo-real direct report-artifact proof path is absent, **When** implementation reaches that branch, **Then** the feature records a defer/follow-up note instead of inventing a new persistence or lifecycle model. ### Edge Cases - Multiple matching artifacts exist and no single current-scope winner is provable. - An artifact exists but belongs to another workspace or managed environment. - A snapshot is `active` but `partial`, `missing`, or otherwise incomplete. - A review pack is `ready` but expired or no longer the current export for the review context. - A run already has reconciliation metadata from another source and the adapter should not overwrite terminal truth incorrectly. - A generic `StoredReport` looks current but still cannot prove causal linkage to the run that is being reconciled. ## Requirements *(mandatory)* ### Functional Requirements - **FR-361-001**: New artifact-backed reconciliation MUST reuse the current `OperationRunReconciliationRegistry`, `OperationRunReconciliationAdapter` contract, `AdapterRunReconciler`, and `OperationRunService::updateRunWithReconciliation()` write seam. No second registry or direct model write path is allowed. - **FR-361-002**: The feature MUST implement bounded reconciliation for `tenant.evidence.snapshot.generate` using existing `EvidenceSnapshot` scope, status, fingerprint, completeness, and `operation_run_id` truth. - **FR-361-003**: The feature MUST implement bounded reconciliation for `environment.review_pack.generate` using existing `ReviewPack` scope, status, fingerprint, expiration, and `operation_run_id` truth. - **FR-361-004**: Unsupported generic report families, especially `permission.posture.check` and `entra.admin_roles.scan`, MUST remain fail-closed in Spec 361 and MUST NOT gain any `StoredReport`-backed success path in this package. - **FR-361-005**: Any shared proof helper introduced for this feature MUST remain derived-only, read-only, and local to current reconciliation scope; it MUST NOT create new persisted truth or a generic artifact workflow engine. - **FR-361-006**: Evidence and review-pack adapters MUST validate workspace/environment scope before success. If present, they SHOULD also validate direct `operation_run_id`, fingerprint, review scope, or other current repo-real identity signals. - **FR-361-007**: Evidence reconciliation MUST require a snapshot state that is good enough to prove success: `active` plus non-missing completeness. Partial, failed, expired, stale, or wrong-scope states must not succeed silently. - **FR-361-008**: Review-pack reconciliation MUST require a pack state that is good enough to prove success: `ready`, non-expired, current-scope, and tied to the same review/evidence context when current repo truth exposes those links. - **FR-361-009**: All successful artifact-backed reconciliation MUST persist canonical metadata only through `context.reconciliation`, including reason, adapter key, previous lifecycle state, and related artifact reference. - **FR-361-010**: Adapters MUST be read-only toward `EvidenceSnapshot`, `ReviewPack`, and `StoredReport`. They may read and compare; they may not mutate artifact lifecycle or regenerate output. - **FR-361-011**: Existing operations surfaces MUST explain reconciled or unsupported states calmly and truthfully without adding a new route family, a new page family, or raw storage/debug data as the primary visible message. - **FR-361-012**: The feature MUST NOT widen restore, backup, sync, or review-compose behavior beyond the current registry-owned contract. ### Non-Functional Requirements - **NFR-361-001**: The feature MUST fail closed on ambiguous, partial, expired, stale, or cross-scope artifact states. - **NFR-361-002**: The feature MUST preserve auditability by keeping reconciliation writes inside the current `OperationRun` lifecycle path. - **NFR-361-003**: The feature MUST be idempotent; re-running reconciliation over the same eligible artifact must not create duplicate lifecycle churn. - **NFR-361-004**: The feature SHOULD avoid migrations. If implementation somehow proves a migration is required, work must stop and spec/plan must be updated before coding further. - **NFR-361-005**: The feature MUST not add compatibility shims for pre-production historical report aliases or old fixture shapes. - **NFR-361-006**: The feature adds no destructive action, so no new confirmation flows are expected. - **NFR-361-007**: Filament and Livewire posture remains current repo truth: Filament v5 on Livewire v4, with provider registration in `apps/platform/bootstrap/providers.php`. - **NFR-361-008**: No new Filament asset registration is allowed; `filament:assets` remains unchanged. - **NFR-361-009**: Changed operator-facing surfaces MUST keep default-visible decision content above diagnostics, keep raw/support evidence behind existing host-gated reveal paths, and preserve exactly one dominant default next action. ## UI Action Matrix *(mandatory when Filament is changed)* | Surface | Location | Header Actions | Inspect Affordance (List/Table) | Row Actions (max 2 visible) | Bulk Actions (grouped) | Empty-State CTA(s) | View Header Actions | Create/Edit Save+Cancel | Audit log? | Notes / Exemptions | |---|---|---|---|---|---|---|---|---|---|---| | Operations hub | `apps/platform/app/Filament/Pages/Monitoring/Operations.php` | existing only | row click to canonical detail | existing only | existing only | unchanged | N/A | N/A | existing run lifecycle audit only | no new action | | Run detail | `apps/platform/app/Filament/Pages/Operations/TenantlessOperationRunViewer.php` | existing only | N/A | N/A | N/A | unchanged | existing navigation only | N/A | existing run lifecycle audit only | no new action | | Evidence Snapshot detail | `apps/platform/app/Filament/Resources/EvidenceSnapshotResource.php` | existing only | row click remains canonical | existing only | existing only | unchanged | existing only | N/A | existing audit only | related run or artifact copy only | | Review Pack detail | `apps/platform/app/Filament/Resources/ReviewPackResource.php` | existing only | row click remains canonical | existing only | existing only | unchanged | existing only | N/A | existing audit only | no new mutation surface | ### Key Entities *(include if feature involves data)* - **OperationRun**: persisted execution and reconciliation truth; owns `type`, `status`, `outcome`, `summary_counts`, and `context.reconciliation` - **EvidenceSnapshot**: persisted evidence-generation truth with `operation_run_id`, status, completeness state, scope, and fingerprint - **ReviewPack**: persisted review-pack artifact truth with `operation_run_id`, status, scope, expiration, and fingerprint - **StoredReport**: retained report artifact context only; currently lacks direct causal linkage strong enough for generic reconciliation - **ReconciliationResult / proof helper**: bounded derived decision output only; no new persistence ## Success Criteria *(mandatory)* ### Measurable Outcomes - **SC-361-001**: queued, running, or stale `tenant.evidence.snapshot.generate` runs can be reconciled to an existing active, current-scope snapshot without adding new persistence or new operation types. - **SC-361-002**: queued, running, or stale `environment.review_pack.generate` runs can be reconciled to an existing ready, non-expired, current-scope pack without adding new persistence or rendered-report scope. - **SC-361-003**: operations surfaces show one canonical artifact-backed success or non-success story, and unsupported generic `StoredReport` families do not silently appear successful. - **SC-361-004**: focused Unit, Feature, and bounded Browser validation passes with no new asset strategy, no migration, and no new destructive action. ## Assumptions - Spec 359 runtime is already merged and remains the baseline adapter consumer next to restore reconciliation. - Current `platform-dev` already contains the shared `context.dispatch` seam and `OperationRunCorrelationResolver`. - `EvidenceSnapshot` and `ReviewPack` lifecycle fields remain the current repo-real truth for artifact readiness. - Generic `StoredReport` causality is intentionally out of scope unless implementation can prove it without new persistence or compatibility logic. ## Risks - A helper introduced for shared proof checks could drift into a generic artifact framework if review is not strict about the two-family boundary. - Review-pack or evidence detail copy could overclaim success if it exposes "artifact exists" without retaining completeness or scope checks. - Implementers may be tempted to rescue generic `StoredReport` families with weak heuristics; this must be treated as out-of-scope creep unless the spec is updated first. ## Open Questions - None blocking prep. The main repo-truth deviation is already resolved in this spec: generic `StoredReport` reconciliation is not part of the initial safe implementation slice. ## Follow-up Spec Candidates - **Stored-report causal reconciliation follow-through**: if a future repo slice introduces direct `StoredReport` causal linkage, readiness lifecycle, or canonical report-output proof strong enough for safe success reconciliation, promote a separate follow-up instead of widening Spec 361 silently.