# Data Model: Structured Snapshot Rendering & Type-Agnostic Item Browser **Feature**: 130-structured-snapshot-rendering | **Date**: 2026-03-09 ## Overview This feature introduces no database schema changes. It adds a normalized read model over existing workspace-owned baseline snapshot records so the snapshot detail page can render every captured policy type through a shared summary and grouped-browser abstraction. The design relies on: 1. existing persistent snapshot records, 2. existing persistent snapshot-item records, 3. a new normalized page presentation model, 4. renderer-specific enrichment layered on top of a shared fallback contract. ## Existing Persistent Entities ### BaselineSnapshot | Attribute | Type | Notes | |-----------|------|-------| | `id` | int | Snapshot identity shown on the page | | `workspace_id` | int | Workspace isolation boundary | | `baseline_profile_id` | int | Source baseline profile reference | | `summary_jsonb` | array/jsonb | Snapshot-level counts, fidelity, policy-type totals, and gap summaries | | `snapshot_identity_hash` | string | Stable snapshot fingerprint shown as technical metadata | | `captured_at` | datetime | Snapshot capture timestamp | **Relationships**: - belongs to `Workspace` - belongs to `BaselineProfile` - has many `BaselineSnapshotItem` **Usage rules**: - The snapshot is immutable and read-only in this feature. - Snapshot detail can render only within the active workspace scope. - `summary_jsonb` remains the page’s aggregation source but not the primary raw UI payload. ### BaselineSnapshotItem | Attribute | Type | Notes | |-----------|------|-------| | `id` | int | Item identity for rendering and ordering | | `baseline_snapshot_id` | int | Parent snapshot reference | | `policy_type` | string | Primary grouping key for the browser | | `subject_type` | string | Stable subject class hint, typically policy | | `subject_key` | string | Stable identity hint for operator inspection | | `subject_external_id` | string | Workspace-safe external identity hint | | `baseline_hash` | string | Captured evidence hash | | `meta_jsonb` | array/jsonb | Best-available display, evidence, version-reference, and optional type-specific metadata | **Usage rules**: - Items are grouped by `policy_type` for summary and browser rendering. - Items must remain visible even when type-specific enrichment is unavailable. - `meta_jsonb` is the canonical source for fallback rendering. ### BaselineProfile | Attribute | Type | Notes | |-----------|------|-------| | `id` | int | Reference-only profile link | | `workspace_id` | int | Workspace ownership boundary | | `name` | string | Profile label shown in snapshot metadata | **Usage rules**: - Used only as metadata context in this feature. - No baseline-profile lifecycle changes are introduced. ## Existing Snapshot Metadata Shape ### Snapshot summary payload `summary_jsonb` is expected to provide enough information for snapshot-level aggregation: | Key | Type | Purpose | |-----|------|---------| | `total_items` | int | Total item count for snapshot-level overview | | `policy_type_counts` | map | Count by policy type for summary rows | | `fidelity_counts` | object | Aggregate fidelity counts already tracked at snapshot level | | `gaps` | object | Aggregate gap count and by-reason summaries | ### Snapshot item metadata payload `meta_jsonb` is expected to provide enough information for the minimum rendering contract: | Key | Type | Purpose | |-----|------|---------| | `display_name` | string nullable | Best available item label | | `category` | string nullable | Optional contextual attribute | | `platform` | string nullable | Optional contextual attribute | | `evidence.fidelity` | string | Item-level fidelity source | | `evidence.source` | string | Capture or reference provenance | | `evidence.observed_at` | date-time nullable | Best available observed timestamp | | `identity.strategy` | string nullable | Identity interpretation hint | | `version_reference.policy_version_id` | int nullable | Source reference hint | | `version_reference.capture_purpose` | string nullable | Optional provenance hint | | `rbac.*` | type-specific object | Optional RBAC enrichment fields | ## New Computed Read Models ### RenderedSnapshot Page-level presentation model built from one snapshot and its items. | Field | Type | Description | |------|------|-------------| | `snapshot` | SnapshotMeta | Top-of-page metadata summary | | `summaryRows` | list | One row per policy type | | `groups` | list | Grouped item browser content | | `technicalDetail` | RenderedTechnicalDetail | Secondary disclosure payload | | `hasItems` | bool | Empty-state signal | ### SnapshotMeta | Field | Type | Description | |------|------|-------------| | `snapshotId` | int | Visible snapshot identifier | | `baselineProfileName` | string nullable | Visible baseline label | | `capturedAt` | date-time nullable | Capture timestamp | | `snapshotIdentityHash` | string nullable | Technical identity hint | | `overallFidelity` | FidelityState | Overall fidelity status for the page | | `overallGapCount` | int | Total known gaps | ### RenderedSnapshotSummaryRow | Field | Type | Description | |------|------|-------------| | `policyType` | string | Canonical type key | | `label` | string | Human-readable type label | | `itemCount` | int | Number of items in this group | | `fidelity` | FidelityState | Group-level fidelity summary | | `gapCount` | int | Group-level gap count | | `capturedAt` | date-time nullable | Most relevant timing summary for the group | | `coverageHint` | string nullable | Optional summary hint | ### RenderedSnapshotGroup | Field | Type | Description | |------|------|-------------| | `policyType` | string | Canonical type key | | `label` | string | Group heading | | `itemCount` | int | Count shown in group header | | `fidelity` | FidelityState | Group-level fidelity badge state | | `gapSummary` | GapSummary | Group-level degraded-state explanation | | `initiallyCollapsed` | bool | Default UI state | | `items` | list | Visible item rows | | `renderingError` | string nullable | Per-group error message when renderer fails | ### RenderedSnapshotItem | Field | Type | Description | |------|------|-------------| | `label` | string | Best available human-readable title | | `typeLabel` | string | Human-readable policy type label | | `identityHint` | string | Stable inspection identity | | `referenceStatus` | string | Capture or reference status label | | `fidelity` | FidelityState | Item-level fidelity state | | `gapSummary` | GapSummary | Item-level degraded-state summary | | `observedAt` | date-time nullable | Best available timestamp | | `sourceReference` | string nullable | PolicyVersion or equivalent reference hint | | `structuredAttributes` | list | Shared and type-specific key-value attributes | ### RenderedAttribute | Field | Type | Description | |------|------|-------------| | `label` | string | Operator-readable field label | | `value` | scalar or string | Display value | | `priority` | string enum | `primary` or `secondary` | ### FidelityState | Value | Meaning | |-------|---------| | `full` | Structured rendering is complete and content-backed | | `partial` | Structured rendering exists but some expected detail is missing | | `reference_only` | Rendering is based on metadata or reference-only evidence | | `unsupported` | Only the minimum fallback path is available | ### GapSummary | Field | Type | Description | |------|------|-------------| | `count` | int | Number of known gaps | | `hasGaps` | bool | Shortcut for UI conditions | | `messages` | list | Human-readable explanations or translated reason labels | ### RenderedTechnicalDetail | Field | Type | Description | |------|------|-------------| | `defaultCollapsed` | bool | Always true for initial render | | `summaryPayload` | array | Raw summary payload for debugging | | `groupPayloads` | map | Optional renderer debug payloads where safe | ## Relationships and Aggregation Rules ### Snapshot to group aggregation ```text BaselineSnapshot -> many BaselineSnapshotItem -> grouped by policy_type -> one RenderedSnapshotSummaryRow per group -> one RenderedSnapshotGroup per group ``` ### Group derivation rules ```text policy_type group -> item count = number of items in the group -> fidelity = worst effective fidelity across item set or unsupported if fallback only -> gap count = derived from item gaps plus any renderer-level degraded-state signals -> captured/observed timing = most relevant available timing summary ``` ### Renderer resolution rules ```text Known supported type with specific renderer -> render shared item contract + type-specific enrichments Known type with no specific renderer -> fallback renderer Unknown type -> fallback renderer Specific renderer throws or returns invalid data -> group-level rendering error + fallback group shell ``` ## Validation Rules | Rule | Result | |------|--------| | Every captured policy type yields a summary row | Required | | Every captured policy type yields a grouped browser section | Required | | Every item yields at least the minimum rendering contract | Required | | Unknown types never disappear from the page | Required | | Technical payload is secondary and collapsed by default | Required | | One renderer failure cannot break the rest of the snapshot page | Required | ## Schema Impact No schema migration is expected for this feature.