# Data Model: Governance Artifact Truthful Outcomes & Fidelity Semantics ## Overview This feature introduces a shared read-model for operator-facing artifact truth across existing governance artifacts. The first slice does not require a new primary table. Instead, it standardizes how existing persisted artifact records are projected into one truth envelope. ## Entities ### ArtifactTruthEnvelope Operator-facing read model derived from one governance artifact or artifact-targeted run. **Fields**: - `artifactFamily` (enum): `baseline_snapshot`, `evidence_snapshot`, `tenant_review`, `review_pack`, `artifact_run` - `artifactKey` (string): stable identifier in the form `{family}:{id}` - `workspaceId` (int) - `tenantId` (int|null): `null` for workspace-owned baseline snapshots and workspace-level runs - `executionOutcome` (enum|null): `pending`, `succeeded`, `partially_succeeded`, `blocked`, `failed` - `artifactExistence` (enum): `not_created`, `historical_only`, `created`, `created_but_not_usable` - `contentState` (enum): `trusted`, `partial`, `missing_input`, `metadata_only`, `reference_only`, `empty`, `unsupported` - `freshnessState` (enum): `current`, `stale`, `unknown` - `publicationReadiness` (enum|null): `not_applicable`, `internal_only`, `publishable`, `blocked` - `supportState` (enum): `normal`, `limited_support` - `actionability` (enum): `none`, `optional`, `required` - `primaryReasonCode` (string|null) - `primaryLabel` (string): top-level operator-facing state summary - `primaryExplanation` (string|null): concise explanation of the main degraded dimension - `diagnosticLabel` (string|null): renderer or implementation-oriented label shown only secondarily - `nextActionLabel` (string|null) - `nextActionUrl` (string|null) - `relatedRunId` (int|null) - `relatedArtifactUrl` (string|null) **Validation rules**: - `artifactExistence = not_created` MUST NOT coexist with `contentState = trusted`. - `publicationReadiness` MAY be non-null only for review and review-pack families. - `supportState = limited_support` MUST NOT be used as the primary failure dimension if another truth dimension explains operator impact. - `actionability = required` MUST include either `nextActionLabel` or a stable reason explanation. ### ArtifactTruthDimension Normalized dimension entry for badges, helper text, summaries, and canonical filter values. **Fields**: - `axis` (enum): `execution_outcome`, `artifact_existence`, `content_fidelity`, `data_freshness`, `publication_readiness`, `support_maturity`, `operator_actionability` - `state` (string) - `label` (string) - `classification` (enum): `primary`, `secondary`, `diagnostic` - `badgeDomain` (string|null) - `badgeState` (string|null) **Validation rules**: - At most one `primary` dimension may drive the top-level alert state at a time. - Diagnostic dimensions MUST still be preserved for detail pages even when not rendered on list pages. ### ArtifactTruthCause Stable explanation payload for degraded states. **Fields**: - `reasonCode` (string|null) - `translationArtifact` (string|null): `provider_reason_codes`, `execution_denial_reason_code`, `tenant_operability_reason_code`, `rbac_reason`, or a bounded baseline/review artifact - `operatorLabel` (string|null) - `shortExplanation` (string|null) - `diagnosticCode` (string|null) - `nextSteps` (list) **Validation rules**: - If `reasonCode` is translated, `operatorLabel` SHOULD come from the centralized translator. - Unknown codes MAY fall back to persisted message text, but raw codes remain diagnostics-only. ## Source Projections ### BaselineSnapshotProjection Existing workspace-owned source record: `BaselineSnapshot`. **Source fields used**: - `id`, `workspace_id`, `captured_at` - `summary_jsonb.fidelity_counts` - `summary_jsonb.gaps` - derived fidelity from `FidelityState::fromSummary(...)` **Derived truth rules**: - Full artifact trust requires usable captured content and no misleading gap interpretation. - `unsupported` fidelity remains diagnostic-only unless it also implies that no trustworthy comparison artifact exists. - Zero-subject or unusable-upstream cases should be explained from related run context where available. ### EvidenceSnapshotProjection Existing tenant-owned source record: `EvidenceSnapshot`. **Source fields used**: - `id`, `workspace_id`, `tenant_id`, `status`, `completeness_state`, `generated_at`, `expires_at` - `summary.missing_dimensions`, `summary.stale_dimensions` - child `items.state`, `items.source_kind`, `items.freshness_at` - `operation_run_id` **Derived truth rules**: - `status` models lifecycle; `completeness_state` models coverage/freshness and must not be collapsed into lifecycle. - `missing_dimensions > 0` indicates incomplete coverage, not run failure. - `stale_dimensions > 0` indicates freshness follow-up, not absence. ### TenantReviewProjection Existing tenant-owned source record: `TenantReview`. **Source fields used**: - `id`, `workspace_id`, `tenant_id`, `status`, `completeness_state`, `generated_at`, `published_at`, `archived_at` - `summary.publish_blockers` - `summary.section_state_counts` - `evidence_snapshot_id`, `current_export_review_pack_id`, `operation_run_id` - child `sections.completeness_state`, `sections.measured_at` **Derived truth rules**: - `status` answers lifecycle (`draft`, `ready`, `published`, etc.), not evidence completeness by itself. - Publish blockers control `publicationReadiness = blocked` even when the review artifact exists. - A review may be internally useful while still not publishable. ### ReviewPackProjection Existing tenant-owned source record: `ReviewPack`. **Source fields used**: - `id`, `workspace_id`, `tenant_id`, `status`, `generated_at`, `expires_at`, `file_size` - `summary.review_status` - `summary.evidence_resolution.*` - `tenant_review_id`, `evidence_snapshot_id`, `operation_run_id` **Derived truth rules**: - `status = ready` means a file exists, not necessarily that the source review is still publishable. - Provenance must consider linked review state and evidence completeness at generation time. - `expired` is historical-only rather than a runtime failure. ### ArtifactRunProjection Existing source record: `OperationRun` limited to artifact-targeted families. **Source fields used**: - `id`, `workspace_id`, `tenant_id`, `type`, `status`, `outcome`, `summary_counts`, `failure_summary`, `context` - translated reason via `ReasonPresenter` - related links via `OperationRunLinks` **Derived truth rules**: - Run lifecycle and run outcome remain distinct from whether an artifact was actually produced. - Artifact-targeted families in this slice: `baseline_capture`, `tenant.evidence.snapshot.generate`, `tenant.review.compose`, `tenant.review_pack.generate` - The run truth section may reuse persisted `context` or summary enrichment to state whether the intended artifact exists and is usable. ## State Transitions ### Truth-envelope transitions These are read-model transitions, not persistence transitions: 1. `not_created` → `created` - Trigger: artifact record becomes available and usable. 2. `created` → `created_but_not_usable` - Trigger: artifact exists but content, freshness, or readiness makes it unsafe for the primary operator task. 3. `created` or `created_but_not_usable` → `historical_only` - Trigger: artifact remains intelligible for history, but is expired, superseded, or no longer current for primary use. 4. `publicationReadiness = blocked` → `publishable` - Trigger: review blockers clear or a derived pack is generated from a publishable review. ### Existing persisted lifecycle references - `EvidenceSnapshot.status`: `queued` → `generating` → `active` → `superseded|expired|failed` - `TenantReview.status`: `draft` → `ready` → `published|archived|superseded|failed` - `ReviewPack.status`: `queued` → `generating` → `ready|failed|expired` - `OperationRun.status/outcome`: service-owned lifecycle that remains unchanged by this feature ## No New Primary Persistence in First Slice - No new top-level table is required. - Backward-compatible enrichment of existing JSON payloads is allowed if a family cannot otherwise satisfy truthful artifact provenance. - Any enrichment must remain optional for historical records and degrade gracefully when absent.