TenantAtlas/specs/153-evidence-domain-foundation/data-model.md
2026-03-19 14:27:51 +01: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