TenantAtlas/specs/173-tenant-dashboard-truth-alignment/data-model.md
2026-04-03 21:15:29 +02:00

267 lines
13 KiB
Markdown

# Phase 1 Data Model: Tenant Dashboard KPI & Attention Truth Alignment
## Overview
This feature does not add a table, persisted summary entity, or new runtime domain subsystem. It aligns existing persistent tenant truth and existing derived summary contracts so the tenant dashboard's KPI, attention, compare, and recency surfaces describe the same tenant reality.
## Persistent Source Truths
### Tenant
**Purpose**: Scope boundary for every dashboard query and every destination opened from the tenant dashboard.
**Key fields**:
- `id`
- `workspace_id`
- `external_id`
**Validation rules**:
- Every dashboard summary and destination must resolve for one explicit tenant scope at a time.
- Canonical admin destinations opened from the dashboard must preserve this tenant scope through filters or navigation context.
### Finding
**Purpose**: Source of drift, workflow, severity, and due-state truth used by tenant dashboard KPI and attention surfaces.
**Key fields**:
- `tenant_id`
- `workspace_id`
- `finding_type`
- `status`
- `severity`
- `due_at`
- `assignee_user_id`
- `scope_key`
- `baseline_operation_run_id`
- `current_operation_run_id`
**Validation rules**:
- Canonical active/open semantics come from `Finding::openStatusesForQuery()`.
- Canonical high-severity tenant-summary semantics use `SEVERITY_HIGH` plus `SEVERITY_CRITICAL`.
- If a dashboard metric intentionally uses a narrower subset, the label and destination must say so explicitly.
**Relevant state families**:
- `new`
- `acknowledged`
- `triaged`
- `in_progress`
- `reopened`
- `risk_accepted`
- `resolved`
- `closed`
### FindingException / Governance Validity
**Purpose**: Supplies expiring and lapsed accepted-risk governance truth used by tenant-level attention and calmness guards.
**Key fields**:
- `tenant_id`
- `workspace_id`
- `finding_id`
- `status`
- `current_validity_state`
- `review_due_at`
- `expires_at`
**Validation rules**:
- Expiring and lapsed governance must remain derived from existing validity state and timing rules.
- No dashboard-local governance state family may replace or reinterpret existing validity truth.
### OperationRun
**Purpose**: Source of tenant activity, compare execution state, and canonical operation detail navigation.
**Key fields**:
- `id`
- `tenant_id`
- `workspace_id`
- `type`
- `status`
- `outcome`
- `created_at`
- `started_at`
- `completed_at`
- `context`
**Validation rules**:
- Canonical active operations semantics come from `OperationRun::scopeActive()` and the Operations page `active` tab.
- Dashboard activity signals must remain distinct from governance posture signals.
- Attention-worthy operations follow-up is narrower than generic activity and is limited to failed, warning, or unusually long-running or stalled tenant runs that require operator review.
## Existing Runtime Source Objects
### BaselineCompareStats
**Purpose**: Existing compare- and governance-aware source object that already owns overdue, expiring, lapsed, and high-severity active findings counts alongside compare posture inputs.
**Key consumed fields**:
- `profileName`
- `state`
- `operationRunId`
- `lastComparedHuman`
- `findingsCount`
- `overdueOpenFindingsCount`
- `expiringGovernanceCount`
- `lapsedGovernanceCount`
- `activeNonNewFindingsCount`
- `highSeverityActiveFindingsCount`
**Validation rules**:
- Existing compare and governance counts remain the source of truth for compare-backed dashboard calmness guards.
- The feature must not create a second competing count path for the same compare-backed summary family.
### BaselineCompareSummaryAssessment
**Purpose**: Existing summary contract that maps compare stats into posture family, tone, headline, supporting message, and next-action intent.
**Key consumed fields**:
- `stateFamily`
- `tone`
- `headline`
- `supportingMessage`
- `reasonCode`
- `positiveClaimAllowed`
- `nextActionLabel()`
- `nextActionTarget()`
**Validation rules**:
- Dashboard compare calmness must remain sourced from this assessment path.
- If the dashboard suppresses additional calm claims beyond compare posture, it must do so by consuming existing tenant attention truth, not by inventing a new tone system.
### TenantGovernanceAggregate
**Purpose**: Existing derived tenant-scoped summary contract that already combines compare posture and governance-related counts for tenant dashboard consumers.
**Key consumed fields**:
- `headline`
- `profileName`
- `lastComparedLabel`
- `compareState`
- `summaryAssessment`
- `overdueOpenFindingsCount`
- `expiringGovernanceCount`
- `lapsedGovernanceCount`
- `highSeverityActiveFindingsCount`
- `nextActionLabel`
- `nextActionTarget`
- `stats`
**Validation rules**:
- The feature should extend or reuse this existing contract rather than creating a new dashboard-only aggregate.
- Widgets using this aggregate must not re-query the same owned summary fields locally.
## Derived Dashboard View Contracts
### Dashboard KPI Metric
**Purpose**: Compact tenant dashboard stat that names one count universe and one matching destination.
#### Fields
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `key` | string | yes | Stable metric identity such as `new_drift`, `high_severity_active`, or `active_operations` |
| `label` | string | yes | Operator-facing label that must match the actual count universe |
| `count` | integer | yes | Metric value |
| `problemFamily` | enum | yes | Shared dashboard problem family limited to `findings` or `operations` for the KPI strip in this slice |
| `findingUniverse` | enum nullable | no | `new_drift_only`, `open_drift`, or `active_findings` when applicable |
| `severityUniverse` | enum nullable | no | `high_only` or `high_and_critical` when applicable |
| `destination` | object | yes | Shared destination contract carrying kind, tenant-scoping, semantics label, any filter state needed to reproduce the same subset, and disabled/helper-text state when a visible affordance is intentionally non-clickable |
#### Validation rules
- The metric label must accurately reflect `findingUniverse` and `severityUniverse` when either is present.
- The destination must reproduce the same subset or explicitly broaden it with visible framing.
- The metric `problemFamily` must use the same shared family naming used by `AttentionItem` and the internal OpenAPI contract.
- If a KPI remains visible for an in-scope member who lacks destination capability, the shared `destination` contract must carry the disabled state and helper text instead of implying a clickable drill-through.
- In this slice, KPI destinations are limited to `tenant_findings` or `canonical_operations`, with `none` reserved only for intentionally passive reassurance states.
- A canonical operations destination must carry tenant filter state from the dashboard context.
### Needs Attention Item
**Purpose**: One dashboard attention row that describes a tenant-level problem and tells the operator where to go next.
#### Fields
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `key` | string | yes | Stable attention identity such as `overdue_findings`, `lapsed_governance`, or `baseline_compare_posture` |
| `title` | string | yes | Operator-facing problem name |
| `body` | string | yes | Short explanation of the risk or follow-up need |
| `supportingMessage` | string nullable | no | Secondary explanatory text when the item needs more context without changing its primary destination |
| `badge` | string | yes | Existing summary family label such as `Findings`, `Governance`, `Baseline`, or `Operations` |
| `tone` | string | yes | Existing tone family used by the shared contract |
| `problemFamily` | enum | yes | `findings`, `governance`, `compare`, or `operations` |
| `actionLabel` | string nullable | no | Primary follow-up verb for this item |
| `actionDisabled` | boolean nullable | no | Whether the visible follow-up state is intentionally non-clickable for an in-scope member lacking destination capability |
| `helperText` | string nullable | no | Helper text explaining why the visible follow-up state is disabled or non-clickable |
| `nextStepLabel` | string nullable | no | Secondary text when the compare assessment already defines the next step |
| `destination` | object | yes | Shared destination contract carrying the target surface semantics, including disabled/helper-text state when the visible follow-up is intentionally non-clickable |
#### Validation rules
- Central attention items must be actionable: `destination` must be present, and the item must expose either `actionLabel`, `nextStepLabel`, or a disabled explanatory state for an in-scope member who lacks the downstream capability.
- If the visible follow-up is disabled, `actionDisabled` must be `true` and `helperText` must be populated.
- Central attention item destinations are limited to `tenant_findings`, `baseline_compare_landing`, or `canonical_operations` in this slice.
- Each item may expose one primary destination only.
- Items derived from `TenantGovernanceAggregate` must reuse its count and posture fields rather than recompute them.
### Findings Destination Filter State
**Purpose**: Structured state needed to make a dashboard findings drill-through semantically recoverable on the tenant findings list.
#### Fields
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `tenant` | tenant route key | yes | Tenant route scope |
| `tab` | enum nullable | no | `needs_action`, `overdue`, `risk_accepted`, `resolved`, or `all` |
| `status` | string nullable | no | Explicit status filter when a narrower subset is intended |
| `high_severity` | boolean nullable | no | Whether the destination must enable the high-severity quick filter |
| `finding_type` | string nullable | no | `drift` when the dashboard metric is drift-only |
#### Validation rules
- A dashboard findings link must use at least one of `tab`, `status`, `high_severity`, or `finding_type` when the originating KPI or attention item names a subset narrower than the default list.
- `high_severity=true` must align with the findings list's existing `HIGH + CRITICAL` filter semantics.
### Operations Destination Filter State
**Purpose**: Structured state needed to keep `/admin/operations` tenant-safe and semantically continuous when opened from the tenant dashboard.
#### Fields
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `workspace_id` | integer | yes | Existing workspace context |
| `tenant_id` | integer or tenant route key | yes | Active tenant filter for canonical admin operations |
| `activeTab` | enum nullable | no | `active`, `failed`, `blocked`, `succeeded`, `partial`, or `all`; `blocked` reproduces warning, stalled, or unusually long-running follow-up and `failed` reproduces terminal failure follow-up |
| `navigationContext` | string nullable | no | Optional serialized canonical back-link context carried through the canonical operations destination |
#### Validation rules
- Dashboard operations links must preserve tenant filter state.
- Operations activity KPIs should use the `activeTab=active` semantics when the metric names active work.
- Operations follow-up links must use `failed` for terminal failure follow-up and `blocked` for warning, stalled, or unusually long-running follow-up so dashboard semantics stay recoverable on `/admin/operations`.
## Relationships
- One `Tenant` owns many `Finding` rows and many `OperationRun` rows.
- One `Finding` may have zero or one current effective `FindingException` governance state relevant to this slice.
- One `TenantGovernanceAggregate` summarizes one tenant using one `BaselineCompareStats` instance and one `BaselineCompareSummaryAssessment` instance.
- `DashboardKpis`, `NeedsAttention`, and `BaselineCompareNow` consume overlapping derived truth and must remain semantically aligned.
- `RecentDriftFindings` and `RecentOperations` consume the same tenant scope but are diagnostic-only consumers, not posture owners.
## Lifecycle Notes
1. Tenant dashboard loads for one current tenant.
2. Aggregate-backed summary surfaces resolve the current tenant's compare and governance truth.
3. KPI and attention surfaces expose destinations whose filter state must preserve the originating problem family.
4. Recency surfaces expose recent records for context only.
5. Canonical operations and tenant findings destinations resolve within the same tenant scope and remain subject to existing server-side authorization.
## Migration Notes
- No schema migration is required.
- No new persisted artifact is required.
- If implementation needs a new helper, it should stay local to existing `OperationRunLinks`, findings list filter handling, or the existing aggregate path rather than introducing a new dashboard framework.