TenantAtlas/specs/361-report-evidence-reconciliation/spec.md
ahmido 252cd4513d feat: implement report evidence reconciliation (#432)
Implemented report evidence reconciliation.

Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #432
2026-06-06 22:40:59 +00:00

362 lines
39 KiB
Markdown

# 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.