TenantAtlas/specs/361-report-evidence-reconciliation/plan.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

224 lines
15 KiB
Markdown

# Implementation Plan: Spec 361 - Report and Evidence Reconciliation Adapters
**Branch**: `361-report-evidence-reconciliation` | **Date**: 2026-06-06 | **Spec**: `/Users/ahmeddarrazi/Documents/projects/wt-plattform/specs/361-report-evidence-reconciliation/spec.md`
**Input**: Feature specification from `/Users/ahmeddarrazi/Documents/projects/wt-plattform/specs/361-report-evidence-reconciliation/spec.md`
**Note**: This plan is repo-aware and preparation-only. No application implementation is performed in this step.
## Summary
Extend the current `OperationRun` reconciliation registry with artifact-backed, read-only adapters for `EvidenceSnapshot` and `ReviewPack`, so queued/running/stale runs can finalize only when current repo-real artifact truth already proves success. Keep generic `StoredReport` families fail-closed and explicitly deferred; Spec 361 does not allow any `StoredReport`-backed success path.
## Technical Context
**Language/Version**: PHP 8.4.15
**Primary Dependencies**: Laravel 12.52, Filament 5.2.1, Livewire 4.1.4, Pest 4.3.1
**Storage**: PostgreSQL 16 (`operation_runs`, `evidence_snapshots`, `review_packs`, `stored_reports`)
**Testing**: Pest Unit + Feature + one bounded Browser smoke
**Validation Lanes**: fast-feedback, confidence, browser
**Target Platform**: Laravel monolith in Sail / Dokploy container workflow
**Project Type**: single web application (`apps/platform`)
**Performance Goals**: no new polling or background family; reconciliation stays bounded to current stale/queued scan paths
**Constraints**: no new schema, no new operation type, no new panel/provider, no new Filament asset strategy, no generic stored-report heuristics
**Scale/Scope**: narrow `OperationRun` artifact reconciliation extension over two existing artifact families plus explicit unsupported-family handling
## UI / Surface Guardrail Plan
- **Guardrail scope**: changed surfaces
- **Affected routes/pages/actions/states/navigation/panel/provider surfaces**:
- `apps/platform/app/Filament/Pages/Monitoring/Operations.php`
- `apps/platform/app/Filament/Pages/Operations/TenantlessOperationRunViewer.php`
- `apps/platform/app/Filament/Resources/EvidenceSnapshotResource.php`
- `apps/platform/app/Filament/Resources/ReviewPackResource.php`
- **No-impact class, if applicable**: N/A
- **Native vs custom classification summary**: native Filament surfaces with shared `OperationRun` and artifact-truth helpers
- **Shared-family relevance**: `OperationRun` monitoring family plus artifact-truth detail family
- **State layers in scope**: page, detail, URL-query
- **Audience modes in scope**: operator-MSP, support-platform
- **Decision/diagnostic/raw hierarchy plan**: decision-first on operations surfaces, diagnostics-second on run detail, artifact proof on demand through existing detail pages
- **Raw/support gating plan**: raw context stays in existing diagnostic sections only
- **One-primary-action / duplicate-truth control**: keep current inspect/open paths; do not add a second success summary on artifact pages
- **Handling modes by drift class or surface**: review-mandatory
- **Repository-signal treatment**: review-mandatory for any attempt to widen scope into generic stored-report success or new artifact lifecycle semantics
- **Special surface test profiles**: monitoring-state-page
- **Required tests or manual smoke**: functional-core plus one bounded browser smoke
- **Exception path and spread control**: none
- **Active feature PR close-out entry**: Guardrail / Smoke Coverage
- **UI/Productization coverage decision**: no new coverage artifact required; existing monitoring and artifact page families remain sufficient
- **Coverage artifacts to update**: none by default
- **No-impact rationale**: existing reachable surfaces only; no new route family or navigation entry
- **Navigation / Filament provider-panel handling**: no panel or navigation change
- **Screenshot or page-report need**: no by default; use bounded browser smoke unless implementation proves a material hierarchy shift
## Shared Pattern & System Fit
- **Cross-cutting feature marker**: yes
- **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`
- **Shared abstractions reused**: current adapter contract, current service-owned reconciliation metadata, current monitoring/detail presenters, current artifact-truth presenters and links
- **New abstraction introduced? why?**: at most one small proof/helper path if duplicated scope/completeness checks across Evidence Snapshot and Review Pack become noisy; no generic report framework is allowed
- **Why the existing abstraction was sufficient or insufficient**: the write seam and presenters already exist, but the registry lacks artifact-backed success for evidence and review-pack runs
- **Bounded deviation / spread control**: keep proof logic local to `App\Support\Operations\Reconciliation\`; do not add new persistence or a generalized artifact orchestration engine
## OperationRun UX Impact
- **Touches OperationRun start/completion/link UX?**: yes
- **Central contract reused**: current `OperationRunService`, registry-backed reconciliation, `OperationRunLinks`, and existing monitoring/detail presenters
- **Delegated UX behaviors**: existing queued toast, run link, artifact link, run-enqueued browser event, and terminal notification paths remain unchanged
- **Surface-owned behavior kept local**: wording and placement only
- **Queued DB-notification policy**: unchanged
- **Terminal notification path**: unchanged central lifecycle mechanism
- **Exception path**: none
## Provider Boundary & Portability Fit
- **Shared provider/platform boundary touched?**: no
- **Provider-owned seams**: N/A
- **Platform-core seams**: `OperationRun`, artifact truth, current scope-safe links
- **Neutral platform terms / contracts preserved**: `operation`, `artifact`, `review pack`, `evidence snapshot`, `reconciliation`
- **Retained provider-specific semantics and why**: none
- **Bounded extraction or follow-up path**: generic `StoredReport` causality remains a separate future follow-up if ever needed
## Constitution Check
*GATE: Must pass before implementation starts. Re-check if scope changes.*
- Inventory-first: PASS. The slice reads current artifact truth; it does not create new snapshot truth.
- Read/write separation: PASS. Adapters read artifacts only and finalize runs only through `OperationRunService`.
- Graph contract path: PASS. No Graph surface change is planned.
- Deterministic capabilities: PASS. No new capability derivation is introduced.
- Workspace and tenant isolation: PASS. Adapters must use run-owned workspace/environment scope and existing resource policies.
- Run observability: PASS. `OperationRun` remains the only lifecycle owner; no shadow truth is introduced.
- TEST-GOV-001: PASS. Unit + Feature + bounded Browser are the narrowest honest proof.
- PROP-001 / ABSTR-001: PASS only if any new helper stays bounded to the two in-scope artifact families and does not become a generic report framework.
- PERSIST-001 / STATE-001: PASS. No new persisted truth or lifecycle family is planned.
- XCUT-001 / LAYER-001: PASS. Extend current registry and presenters; do not create parallel operator language.
- UI-SEM-001 / UI-FIL-001 / UI-COV-001: PASS. Existing native surfaces only; no new page family or asset strategy.
- BADGE-001: PASS. Existing badge and artifact-truth semantics remain authoritative.
## Test Governance Check
- **Test purpose / classification by changed surface**: Unit for adapter resolution, proof logic, reconciliation-metadata shape, and idempotency; Feature for run finalization, scope-safe links, and disclosure-layer behavior; Browser for existing Operations or artifact surface wording if it materially changes
- **Affected validation lanes**: fast-feedback, confidence, browser
- **Why this lane mix is the narrowest sufficient proof**: the feature is primarily service and model coordination with a small amount of operator-visible copy and link fallout; no PGSQL-only schema behavior is introduced
- **Narrowest proving command(s)**:
- `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`
- **Fixture / helper / factory / seed / context cost risks**: existing workspace/environment and artifact factories only; no new global defaults should be introduced
- **Expensive defaults or shared helper growth introduced?**: no
- **Heavy-family additions, promotions, or visibility changes**: one explicit browser smoke only
- **Surface-class relief / special coverage rule**: monitoring-state-page
- **Closing validation and reviewer handoff**: reviewers should verify that `StoredReport` families stay unsupported in Spec 361, that adapters never mutate artifacts, that `context.reconciliation` stays canonical and idempotent, and that no new operation type or asset strategy slips into scope
- **Budget / baseline / trend follow-up**: none expected
- **Review-stop questions**: does the feature overclaim generic report success, add persistence, or invent a new artifact lifecycle model?
- **Escalation path**: document-in-feature if stored-report proof remains insufficient; reject-or-split if implementation tries to widen scope
- **Active feature PR close-out entry**: Guardrail / Smoke Coverage
- **Why no dedicated follow-up spec is needed**: Evidence Snapshot and Review Pack already have strong repo-real proof surfaces. Only generic stored-report causality is deferred explicitly to a future follow-up spec if the repo ever gains a direct proof contract.
## Repo-Verified Runtime Surfaces Likely Affected
- `apps/platform/app/Services/AdapterRunReconciler.php`
- `apps/platform/app/Services/OperationRunService.php`
- `apps/platform/app/Models/OperationRun.php`
- `apps/platform/app/Models/EvidenceSnapshot.php`
- `apps/platform/app/Models/ReviewPack.php`
- `apps/platform/app/Models/StoredReport.php` (context only unless proof is stronger than expected)
- `apps/platform/app/Support/Operations/Reconciliation/OperationRunReconciliationRegistry.php`
- `apps/platform/app/Support/Operations/Reconciliation/OperationRunReconciliationAdapter.php`
- `apps/platform/app/Support/OperationRunLinks.php`
- `apps/platform/app/Support/Ui/GovernanceArtifactTruth/ArtifactTruthPresenter.php`
- `apps/platform/app/Support/OpsUx/GovernanceRunDiagnosticSummaryBuilder.php`
- `apps/platform/app/Support/OperationCatalog.php`
- `apps/platform/app/Support/OperationRunType.php`
- `apps/platform/app/Services/Evidence/EvidenceSnapshotService.php`
- `apps/platform/app/Services/ReviewPackService.php`
- `apps/platform/app/Filament/Pages/Monitoring/Operations.php`
- `apps/platform/app/Filament/Pages/Operations/TenantlessOperationRunViewer.php`
- `apps/platform/app/Filament/Resources/EvidenceSnapshotResource.php`
- `apps/platform/app/Filament/Resources/ReviewPackResource.php`
- `apps/platform/tests/Feature/Operations/*`
- `apps/platform/tests/Feature/Evidence/*`
- `apps/platform/tests/Feature/ReviewPack/*`
- `apps/platform/tests/Browser/Spec360OperationRunCanonicalCutoverSmokeTest.php` and new Spec 361 smoke coverage as needed
## Technical Approach
1. Verify the exact current repo-real operation types, artifact models, and status/fingerprint/scope fields before runtime edits.
2. Reuse the current reconciliation registry and service-owned write seam.
3. Add bounded, read-only adapters for `EvidenceSnapshot` and `ReviewPack` only.
4. Keep generic `StoredReport` families fail-closed in Spec 361 and record any future direct-proof need as a separate follow-up spec instead of widening this package.
5. Reuse current operations and artifact-truth presentation seams for operator-facing fallout.
## Risk Controls
- Fail closed on ambiguous, expired, partial, missing, or cross-scope artifacts.
- No adapter may write to artifact models or regenerate output.
- No new `stored_report.generate` or similar operation type may be introduced.
- If generic stored-report proof is still insufficient, the implementation must record an explicit defer note instead of widening scope.
- No new panel/provider, global search, asset registration, or destructive action may be added in this slice.
## Implementation Phases
### Phase 1: Baseline and Repo-Truth Inventory
Confirm current operation types, adapter seams, and artifact models. Explicitly verify that `StoredReport` is still weaker than `EvidenceSnapshot` and `ReviewPack`.
### Phase 2: Canonical Evidence Snapshot Reconciliation
Add the evidence adapter, bounded proof checks, and focused operations/evidence tests.
### Phase 3: Canonical Review Pack Reconciliation
Add the review-pack adapter, bounded proof checks, current operations/detail fallout, and focused review-pack tests.
### Phase 4: Unsupported Generic Report-Family Handling
Keep `permission.posture.check` and `entra.admin_roles.scan` honest and diagnostic-first. Record defer/follow-up instead of inventing new truth.
### Phase 5: Validation and Close-Out
Run scoped Pest and Browser validation, confirm no migration/assets/panel drift, and record which artifact families were reconciled versus explicitly deferred.
## Project Structure
### Documentation (this feature)
```text
specs/361-report-evidence-reconciliation/
├── spec.md
├── plan.md
├── tasks.md
└── checklists/requirements.md
```
### Source Code (repository root)
```text
apps/platform/app/Services/
apps/platform/app/Support/Operations/Reconciliation/
apps/platform/app/Support/OpsUx/
apps/platform/app/Support/Ui/GovernanceArtifactTruth/
apps/platform/app/Filament/Pages/
apps/platform/app/Filament/Resources/
apps/platform/tests/Unit/
apps/platform/tests/Feature/
apps/platform/tests/Browser/
```
## Assumptions
- The merged Spec 359 baseline and current `context.dispatch` seam remain in `platform-dev`.
- `EvidenceSnapshot` and `ReviewPack` retain their current status, fingerprint, and scope semantics.
- No production-compatibility requirement exists that would justify weak generic stored-report heuristics.
## Open Preparation Decision
Generic `StoredReport` reconciliation is intentionally excluded from the initial safe implementation slice because the current model lacks direct `operation_run_id` and readiness truth. This is not a blocker: the spec is explicitly narrowed around `EvidenceSnapshot` and `ReviewPack`, and unsupported report families must remain fail-closed.