TenantAtlas/specs/131-cross-resource-navigation/data-model.md
ahmido b15d1950b4 feat: add cross-resource navigation cohesion (#160)
## Summary
- add a shared cross-resource navigation layer with canonical navigation context and related-context rendering
- wire findings, policy versions, baseline snapshots, backup sets, and canonical operations surfaces into consistent drill-down flows
- extend focused Pest coverage for canonical operations links, related navigation, and tenant-context preservation

## Testing
- focused Pest coverage for spec 131 was added and the task list marks the implementation verification and Pint steps as completed

## Follow-up
- manual QA checklist item `T036` in `specs/131-cross-resource-navigation/tasks.md` is still open and should be completed during review

Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #160
2026-03-10 16:08:14 +00:00

261 lines
11 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Data Model: Cross-Resource Navigation & Drill-Down Cohesion
**Feature**: 131-cross-resource-navigation | **Date**: 2026-03-10
## Overview
This feature introduces no new database tables. It adds a shared navigation read model and relation-mapping layer over existing workspace-owned and tenant-owned records.
The design relies on existing persisted entities plus a small set of computed presentation concepts:
1. a navigation matrix defining allowed operator journeys,
2. related-context sections on key detail pages,
3. list-level drill-down actions,
4. canonical destination context for operations and other authoritative pages,
5. explicit unavailable-state handling for missing or unauthorized relations.
## Existing Persistent Entities
### Policy
| Attribute | Type | Notes |
|-----------|------|-------|
| `id` | int | Primary internal policy identity |
| `tenant_id` | int | Tenant ownership boundary |
| `workspace_id` | int | Workspace ownership boundary |
| `display_name` | string nullable | Primary human-readable label for navigation |
| `policy_type` | string | Policy-type hint used in labels and filtered destinations |
**Relationships**:
- has many `PolicyVersion`
- may be referenced by findings, backup items, and operation runs through context or foreign keys
**Usage rules**:
- Parent policy is the default upstream destination from a policy version.
- Policy links must remain tenant-entitlement checked.
### PolicyVersion
| Attribute | Type | Notes |
|-----------|------|-------|
| `id` | int | Primary version identity |
| `policy_id` | int nullable | Parent policy relationship |
| `tenant_id` | int | Tenant ownership boundary |
| `workspace_id` | int | Workspace ownership boundary |
| `version_number` | int or string | Displayed as user-facing version context |
| `captured_at` | timestamp | Useful for evidence-related context |
**Relationships**:
- belongs to `Policy`
- may be referenced from baseline snapshot items or backup items
**Usage rules**:
- Policy version detail must expose parent policy and related snapshot evidence where resolvable.
- When the parent policy cannot be opened, the page must still preserve readable version context.
### BaselineProfile
| Attribute | Type | Notes |
|-----------|------|-------|
| `id` | int | Primary workspace-owned baseline profile identity |
| `workspace_id` | int | Workspace ownership boundary |
| `name` | string | Primary label |
| `status` | string | Badge-backed profile state |
| `capture_mode` | string | Useful supporting context on related pages |
**Relationships**:
- has many `BaselineSnapshot`
- may be indirectly referenced by findings and operation runs
**Usage rules**:
- Baseline profile is the primary upstream destination from a baseline snapshot.
- Baseline profile links are workspace-authorized, not tenant-authorized.
### BaselineSnapshot
| Attribute | Type | Notes |
|-----------|------|-------|
| `id` | int | Primary snapshot identity |
| `workspace_id` | int | Workspace ownership boundary |
| `baseline_profile_id` | int | Owning baseline profile |
| `captured_at` | timestamp | Primary temporal context |
| `summary_jsonb` | jsonb | Existing summary and fidelity metadata |
**Relationships**:
- belongs to `BaselineProfile`
- may reference policy versions and source operation runs through existing summary or evidence metadata
- may be linked to related findings
**Usage rules**:
- Snapshot detail must expose owning profile, source run, and related findings where meaningful and authorized.
- Snapshot remains workspace-owned even when it refers to tenant evidence.
### Finding
| Attribute | Type | Notes |
|-----------|------|-------|
| `id` | int | Primary finding identity |
| `tenant_id` | int | Tenant ownership boundary |
| `workspace_id` | int | Workspace ownership boundary |
| `finding_type` | string | Used for operator context and prioritization |
| `subject_display_name` | string nullable | Existing human-readable subject label |
| `baseline_operation_run_id` | int nullable | Existing related operations link |
| `current_operation_run_id` | int nullable | Existing related operations link |
| `evidence_jsonb` | jsonb | Existing source-evidence context |
**Relationships**:
- may point to baseline snapshots, policy versions, policies, inventory items, and runs through evidence metadata and explicit IDs
**Usage rules**:
- Findings are high-priority drill-down surfaces and must not degrade into raw IDs when source evidence is resolvable.
- Findings remain tenant-authorized even when they link to workspace-owned baseline records.
### BackupSet
| Attribute | Type | Notes |
|-----------|------|-------|
| `id` | int | Primary backup set identity |
| `tenant_id` | int | Tenant ownership boundary |
| `workspace_id` | int | Workspace ownership boundary |
| `name` | string | Primary operator label |
| `status` | string | Existing badge-backed lifecycle state |
| `item_count` | int | Supporting list context |
**Relationships**:
- has many backup items
- may be referenced by operation-run context and restore flows
**Usage rules**:
- Backup set pages should expose related operation runs or resulting artifacts when available.
- Backup-related navigation must stay consistent with canonical operations routing.
### OperationRun
| Attribute | Type | Notes |
|-----------|------|-------|
| `id` | int | Canonical run identity |
| `workspace_id` | int | Workspace ownership boundary |
| `tenant_id` | int nullable | Nullable for some workspace-level operations |
| `type` | string | Operation type driving related destinations |
| `status` | string | Existing run status badge |
| `outcome` | string nullable | Existing outcome badge |
| `context` | json/jsonb | Existing target-resource and workflow metadata |
**Relationships**:
- may point to policies, backup sets, restore runs, baseline compare destinations, and other domain records via context
**Usage rules**:
- `OperationRun` is the canonical operational drill-down target.
- Related domain links should be generated from run context only when target authorization can be proven.
## New Computed Read Models
### NavigationMatrixRule
| Field | Type | Description |
|------|------|-------------|
| `source_type` | string | Resource or page type that owns the navigation surface |
| `source_surface` | string enum | `detail_section`, `detail_header`, `list_row`, `canonical_list` |
| `relation_key` | string | Stable semantic relation identifier, e.g. `source_run`, `parent_policy` |
| `target_type` | string | Destination resource or page type |
| `target_mode` | string enum | `direct_record`, `filtered_list`, `canonical_page` |
| `label` | string | Shared operator-facing action or entry label |
| `priority` | int | Lower number = higher visibility priority |
| `requires_capability_check` | bool | Whether actionability depends on explicit authorization |
| `missing_state_policy` | string enum | `hide`, `show_unavailable`, `show_reference_only` |
**Rules**:
- Every in-scope relationship in the specs navigation matrix maps to one rule.
- Rules choose only one canonical destination for the same operator task.
### RelatedContextSection
| Field | Type | Description |
|------|------|-------------|
| `title` | string | Usually `Related context` or a narrow equivalent |
| `entries` | list<RelatedContextEntry> | Ordered related records for the current page |
| `primary_entry_key` | string nullable | Optional marker for the most likely next step |
| `empty_message` | string nullable | Optional empty-state copy when no related entries are available |
**Rules**:
- Detail pages should render one structured section rather than scattering relation fields.
- Entries are ordered by operator relevance, not by storage order.
### RelatedContextEntry
| Field | Type | Description |
|------|------|-------------|
| `key` | string | Stable identifier such as `source_run` or `baseline_profile` |
| `label` | string | User-facing relation label |
| `value` | string | Human-readable related object label |
| `secondary_value` | string nullable | Optional technical ID or context hint |
| `target_url` | string nullable | Drill-down destination when actionable |
| `target_kind` | string | Resource or canonical page type |
| `availability` | string enum | `available`, `missing`, `unauthorized`, `unresolved` |
| `unavailable_reason` | string nullable | User-safe explanation when not actionable |
| `context_badge` | string nullable | Optional workspace, tenant, or type hint |
**Rules**:
- `value` should favor human-readable labels over raw IDs.
- Technical identifiers may appear only as secondary supporting context.
### DrillDownAction
| Field | Type | Description |
|------|------|-------------|
| `label` | string | Shared operator-facing action label |
| `url` | string | Target destination |
| `placement` | string enum | `row_action`, `header_action`, `inline_entry`, `grouped_action` |
| `priority` | int | Governs whether it appears inline or only in grouped context |
| `target_kind` | string | Destination type |
| `visible` | bool | Whether action should be rendered at all |
| `disabled_reason` | string nullable | Optional tooltip or unavailable-state explanation |
**Rules**:
- No more than the highest-priority actions should remain visibly inline on list rows.
- Actions to canonical operations pages must resolve through the canonical helper layer.
### CanonicalNavigationContext
| Field | Type | Description |
|------|------|-------------|
| `workspace_id` | int | Active workspace scope |
| `tenant_id` | int nullable | Originating tenant context if preserved |
| `source_surface` | string | Resource or page that launched the navigation |
| `canonical_route_name` | string | Authoritative destination route name |
| `filter_payload` | array | Context-preserving query or filter state |
| `back_link_label` | string nullable | Explicit return path label |
**Rules**:
- Canonical route identity must remain stable even when tenant context is preserved.
- This model is especially important for operations and monitoring surfaces.
### UnavailableRelationState
| Field | Type | Description |
|------|------|-------------|
| `relation_key` | string | Relation that could not be resolved |
| `reference_value` | string nullable | Technical identifier or fallback reference |
| `reason` | string enum | `missing`, `deleted`, `unauthorized`, `unresolved` |
| `message` | string | User-facing explanation |
| `show_reference` | bool | Whether the secondary reference is safe to display |
**Rules**:
- Unauthorized states must not disclose more than existing access rules allow.
- Missing and unresolved states must preserve surrounding page usability and layout.
## Validation Rules
| Rule | Result |
|------|--------|
| Every in-scope relationship resolves through one explicit navigation-matrix rule | Required |
| Canonical run destinations always use the tenantless operations route family | Required |
| Non-members receive 404 semantics for target resources | Required |
| In-scope members lacking capability may see disabled or unavailable context, but target execution still fails with 403 | Required |
| Related-context entries show human-readable labels first and technical IDs second | Required |
| List-level drill-down actions remain limited to the highest-priority operator journeys | Required |
| Missing or unresolved relations render clear unavailable states without broken links | Required |
## Schema Impact
No schema migration is expected for this feature.