# Research: Evidence Domain Foundation ## Decision 1: Model evidence as immutable snapshots plus per-dimension items - **Decision**: Create a dedicated `evidence_snapshots` root record and a child `evidence_snapshot_items` table 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_packs` as the evidence domain: rejected because `ReviewPack` is 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. ## Decision 2: Snapshot generation is DB-only queued work with a new OperationRun type - **Decision**: Generate evidence snapshots through a queued `OperationRun` such as `tenant.evidence.snapshot.generate`, created and updated exclusively through `OperationRunService`. - **Rationale**: Snapshot creation aggregates existing internal artifacts and can exceed synchronous request budgets. Existing patterns in `ReviewPackService`, `GenerateReviewPackJob`, `ScanEntraAdminRolesJob`, and `OperationRunService` already 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`, `Finding` summaries, baseline/drift summaries, and recent `OperationRun` rollups. - **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 `StoredReport` and `ReviewPack` fingerprint 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 canonical `AuditLog` page 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 `/snapshots` segment. 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.