## 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
261 lines
11 KiB
Markdown
261 lines
11 KiB
Markdown
# 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 spec’s 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. |