# 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<string>)

**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.