## Summary - add the Evidence Snapshot domain with immutable tenant-scoped snapshots, per-dimension items, queued generation, audit actions, badge mappings, and Filament list/detail surfaces - add the workspace evidence overview, capability and policy wiring, Livewire update-path hardening, and review-pack integration through explicit evidence snapshot resolution - add spec 153 artifacts, migrations, factories, and focused Pest coverage for evidence, review-pack reuse, authorization, action-surface regressions, and audit behavior ## Testing - `vendor/bin/sail artisan test --compact --stop-on-failure` - `CI=1 vendor/bin/sail artisan test --compact` - `vendor/bin/sail bin pint --dirty --format agent` ## Notes - branch: `153-evidence-domain-foundation` - commit: `b7dfa279` - spec: `specs/153-evidence-domain-foundation/` Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de> Reviewed-on: #183
5.6 KiB
5.6 KiB
Research: Evidence Domain Foundation
Decision 1: Model evidence as immutable snapshots plus per-dimension items
- Decision: Create a dedicated
evidence_snapshotsroot record and a childevidence_snapshot_itemstable keyed by evidence dimension. - Rationale: The candidate is a curation and completeness layer, not a stakeholder export. A normalized snapshot root plus dimension items supports immutable capture, completeness per dimension, tenant/workspace-safe filtering, and downstream reuse without forcing every consumer to parse a single monolithic blob.
- Alternatives considered:
- Reuse
review_packsas the evidence domain: rejected becauseReviewPackis export-centric, binary-file oriented, and already downstream-facing. - Store one large JSON blob per snapshot: rejected because it weakens queryability, completeness filtering, and per-dimension provenance.
- Reuse
Decision 2: Snapshot generation is DB-only queued work with a new OperationRun type
- Decision: Generate evidence snapshots through a queued
OperationRunsuch astenant.evidence.snapshot.generate, created and updated exclusively throughOperationRunService. - Rationale: Snapshot creation aggregates existing internal artifacts and can exceed synchronous request budgets. Existing patterns in
ReviewPackService,GenerateReviewPackJob,ScanEntraAdminRolesJob, andOperationRunServicealready solve dedupe, active-run visibility, and canonical operator feedback. - Alternatives considered:
- Inline synchronous snapshot creation: rejected because it would violate existing operations UX expectations and create unpredictable request latency.
- DB-only action with no
OperationRun: rejected because snapshot generation is operationally relevant and must remain observable and auditable.
Decision 3: Reuse existing internal artifacts as source references, not as copied raw payload dumps
- Decision: Snapshot items store curated summaries plus source references and source fingerprints to
StoredReport,Findingsummaries, baseline/drift summaries, and recentOperationRunrollups. - Rationale: The foundation should preserve reproducible evidence composition while minimizing duplication. Existing artifacts already carry domain truth; the snapshot needs to record what was included, how fresh it was, and how complete it was.
- Alternatives considered:
- Deep-copy every source payload in full: rejected because it increases storage cost, duplicates secrets/redaction risks, and blurs the line between curated evidence and archival dump.
- Store only foreign keys with no captured summary: rejected because snapshots would become unintelligible if source artifacts later changed or expired.
Decision 4: Dedupe identical evidence state by fingerprint, but supersede changed active snapshots
- Decision: Use a deterministic fingerprint over the included evidence state. If the fingerprint matches an existing non-expired snapshot for the same tenant scope, reuse it. If the fingerprint changes, create a new snapshot and mark the previously active snapshot as
superseded. - Rationale: This preserves immutability and prevents duplicate noise while still producing a historical chain when evidence actually changes. The pattern aligns with existing
StoredReportandReviewPackfingerprint behavior. - Alternatives considered:
- Always create a new snapshot on every request: rejected because it creates operator noise and unnecessary storage churn.
- Update the existing snapshot in place: rejected because it destroys the foundation's core immutability guarantee.
Decision 5: Downstream consumers must resolve explicit snapshots, not silently fall back to live data
- Decision: Introduce a resolver contract that returns an explicit eligible snapshot or an explicit “no snapshot available” result. Downstream consumers in the first slice use this contract instead of reconstructing the same evidence set from raw live records.
- Rationale: The foundation only creates real architectural leverage if downstream consumers depend on it intentionally. Silent fallback to live sources would hide evidence gaps and undermine reproducibility.
- Alternatives considered:
- Let downstream consumers fall back to live assembly when no snapshot exists: rejected because it preserves the current ad hoc architecture and weakens trust.
- Force all consumers to always require a snapshot immediately: rejected because first-slice adoption needs staged rollout and explicit absence handling.
Decision 6: Use a tenant-context resource plus a workspace overview page for first-slice visibility
- Decision: Provide a tenant-scoped evidence snapshot list/detail surface and a workspace-scoped overview that summarizes evidence completeness across authorized tenants.
- Rationale: The spec requires both immutable capture and pre-report completeness visibility. Existing patterns in
ReviewPackResource,TenantReviewPackCard, and the canonicalAuditLogpage show how tenant detail and workspace-wide review can coexist safely. - Implementation note: The implemented canonical overview lives at
/admin/evidence/overview, while the tenant-context Filament resource uses the evidence slug directly rather than a nested/snapshotssegment. Optional overview prefilters are carried only for entitled tenant ids. - Alternatives considered:
- No UI until downstream consumers exist: rejected because completeness visibility is a first-class user story, not a later polish layer.
- Tenant-only UI with no workspace overview: rejected because MSP/workspace operators need safe aggregate visibility before tenant-by-tenant drill-down.