TenantAtlas/specs/153-evidence-domain-foundation/data-model.md
ahmido a74ab12f04 feat: implement evidence domain foundation (#183)
## 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
2026-03-19 13:32:52 +00:00

114 lines
3.7 KiB
Markdown

# Data Model: Evidence Domain Foundation
## 1. EvidenceSnapshot
- **Purpose**: Immutable, tenant-scoped evidence package captured at a specific point in time and reused by downstream consumers.
- **Ownership**: Tenant-owned (`workspace_id` + `tenant_id` NOT NULL)
- **Fields**:
- `id`
- `workspace_id`
- `tenant_id`
- `operation_run_id` nullable
- `initiated_by_user_id` nullable
- `fingerprint` nullable, 64-char SHA-256
- `previous_fingerprint` nullable
- `status` enum: `queued`, `generating`, `active`, `superseded`, `expired`, `failed`
- `completeness_state` enum: `complete`, `partial`, `missing`, `stale`
- `summary` JSONB
- `generated_at` timestampTz nullable
- `expires_at` timestampTz nullable
- `created_at`, `updated_at`
- **Relationships**:
- belongs to `Workspace`
- belongs to `Tenant`
- belongs to `OperationRun`
- belongs to initiator `User`
- has many `EvidenceSnapshotItem`
- **Validation / invariants**:
- `workspace_id` and `tenant_id` required on every row
- successful snapshots are immutable in content after `status` becomes `active`
- one active snapshot per tenant evidence scope in v1
- identical fingerprint for the same tenant scope reuses the existing non-expired snapshot
## 2. EvidenceSnapshotItem
- **Purpose**: One curated evidence dimension inside an evidence snapshot.
- **Ownership**: Tenant-owned (`workspace_id` + `tenant_id` NOT NULL)
- **Fields**:
- `id`
- `evidence_snapshot_id`
- `workspace_id`
- `tenant_id`
- `dimension_key` string
- `state` enum: `complete`, `partial`, `missing`, `stale`
- `required` boolean
- `source_kind` string
- `source_record_type` nullable string
- `source_record_id` nullable string
- `source_fingerprint` nullable string
- `measured_at` timestampTz nullable
- `freshness_at` timestampTz nullable
- `summary_payload` JSONB
- `sort_order` integer default 0
- `created_at`, `updated_at`
- **Relationships**:
- belongs to `EvidenceSnapshot`
- **Validation / invariants**:
- unique per snapshot: `(evidence_snapshot_id, dimension_key)`
- `workspace_id`/`tenant_id` must match the parent snapshot
- `summary_payload` stores curated summary/reference data only; no secrets, tokens, or raw Graph dumps
## 3. Evidence dimensions in first rollout
- `findings_summary`
- `permission_posture`
- `entra_admin_roles`
- `baseline_drift_posture`
- `operations_summary`
Each dimension stores:
- inclusion state (`complete|partial|missing|stale`)
- source provenance (record type, identifier, fingerprint)
- freshness timestamp
- dimension-specific summary payload
## 4. Derived completeness rules
- **Snapshot overall completeness precedence**:
- `missing` if any required dimension is missing
- `stale` if no required dimensions are missing and at least one required dimension is stale
- `partial` if no required dimensions are missing or stale and at least one required dimension is partial
- `complete` only when all required dimensions are complete
## 5. Lifecycle transitions
### EvidenceSnapshot status
- `queued -> generating`
- `generating -> active`
- `generating -> failed`
- `active -> superseded`
- `active -> expired`
- `superseded -> expired`
No transition may mutate snapshot items after `active` is reached.
## 6. Downstream resolution contract
### EvidenceResolutionRequest
- **Purpose**: Explicit request from a downstream consumer for a tenant evidence package.
- **Fields**:
- `workspace_id`
- `tenant_id`
- `snapshot_id` nullable explicit selection
- `required_dimensions` array
- `allow_stale` boolean default `false`
### EvidenceResolutionResult
- **Outcomes**:
- `resolved` with snapshot id and eligible dimensions
- `missing_snapshot`
- `snapshot_ineligible` with missing/stale dimension reasons