130 lines
6.7 KiB
Markdown
130 lines
6.7 KiB
Markdown
# Data Model: Portfolio Triage Arrival Context
|
|
|
|
## Overview
|
|
|
|
This feature introduces no new persisted tables or stored entities. The model impact is a small set of request-scoped, derived runtime contracts that connect existing workspace triage truth to tenant-dashboard arrival rendering.
|
|
|
|
## Existing Source Truths
|
|
|
|
### Workspace overview attention item
|
|
|
|
**Type**: Existing derived workspace summary payload
|
|
**Source**: `WorkspaceOverviewBuilder`
|
|
|
|
| Field | Type | Notes |
|
|
|------|------|-------|
|
|
| `tenant_route_key` | string | Tenant route identifier already used in workspace attention items |
|
|
| `family` | string | Existing concern family, such as `backup_health` or `recovery_evidence` |
|
|
| `title` | string | Operator-facing concern headline |
|
|
| `body` | string | Operator-facing bounded summary |
|
|
| `supporting_message` | string or null | Existing claim-boundary or next-step phrasing |
|
|
| `reason_context.family` | enum | `backup_health` or `recovery_evidence` |
|
|
| `reason_context.state` | enum | Existing posture state emitted from workspace triage |
|
|
| `reason_context.reason` | string or null | Existing bounded reason code |
|
|
| `destination.kind` | string | Existing destination type |
|
|
| `destination.url` | string or null | Existing destination URL |
|
|
| `destination.disabled` | bool | Existing capability-aware destination availability |
|
|
| `destination.helper_text` | string or null | Existing degraded-access explanation |
|
|
|
|
### Tenant-registry triage state
|
|
|
|
**Type**: Existing query-driven list state
|
|
**Source**: `ListTenants::applyRequestedTriageIntent()`
|
|
|
|
| Field | Type | Validation |
|
|
|------|------|------------|
|
|
| `backup_posture` | array<string> | Sanitized through `TenantResource::sanitizeBackupPostures()` |
|
|
| `recovery_evidence` | array<string> | Sanitized through `TenantResource::sanitizeRecoveryEvidenceStates()` |
|
|
| `triage_sort` | string or null | Sanitized through `TenantResource::sanitizeTriageSort()` |
|
|
|
|
### Existing destination continuity inputs
|
|
|
|
**Type**: Existing route-level continuity params
|
|
|
|
| Surface | Param | Purpose |
|
|
|--------|-------|---------|
|
|
| Backup-set list | `backup_health_reason` | Explains why backup detail or backup list was opened |
|
|
| Restore-run list | `recovery_posture_reason` | Explains why restore history was opened |
|
|
| Restore-run detail | `recovery_posture_reason` | Explains why a specific restore run was opened |
|
|
|
|
## New Derived Runtime Contracts
|
|
|
|
### PortfolioArrivalContext
|
|
|
|
**Type**: Request-scoped value object
|
|
**Lifecycle**: Decoded from a tenant-dashboard query token, validated against current scope, discarded after the request completes
|
|
|
|
| Field | Type | Validation |
|
|
|------|------|------------|
|
|
| `version` | integer | Must match the current token version |
|
|
| `sourceSurface` | enum | `workspace_overview` or `tenant_registry` |
|
|
| `tenantRouteKey` | string | Must match the current tenant route or resolve to the same tenant |
|
|
| `workspaceId` | integer or null | Optional scope binding; when present, must match current workspace context |
|
|
| `concernFamily` | enum | `backup_health` or `recovery_evidence` |
|
|
| `concernState` | enum | `absent`, `stale`, `degraded`, `unvalidated`, or `weakened` |
|
|
| `concernReason` | string or null | Allowlisted per concern family |
|
|
| `arrivalSummary` | string | Derived bounded explanation of why the operator arrived |
|
|
| `claimBoundary` | string or null | Derived reminder that arrival reason is not a stronger truth than current evidence supports |
|
|
| `nextStep` | `NextStepTarget` | Capability-aware navigation target |
|
|
| `returnTarget` | `ReturnTarget` | Portfolio return link with bounded route and query state |
|
|
| `currentTruthDelta` | string or null | Optional note when current tenant truth differs from the arrival reason |
|
|
|
|
### NextStepTarget
|
|
|
|
**Type**: Request-scoped navigation descriptor
|
|
|
|
| Field | Type | Notes |
|
|
|------|------|-------|
|
|
| `kind` | enum | `tenant_dashboard`, `backup_sets`, `restore_runs`, `restore_run_detail` |
|
|
| `label` | string | Operator-facing next-step verb + object label |
|
|
| `url` | string or null | Null when unavailable under current RBAC |
|
|
| `disabled` | bool | True when the user can see the arrival block but cannot access the target |
|
|
| `helperText` | string or null | Truthful reason the next step cannot be opened |
|
|
| `reasonParam` | array<string, scalar> | Existing `backup_health_reason` or `recovery_posture_reason` continuation payload when needed |
|
|
|
|
### ReturnTarget
|
|
|
|
**Type**: Request-scoped navigation descriptor
|
|
|
|
| Field | Type | Notes |
|
|
|------|------|-------|
|
|
| `kind` | enum | `workspace_overview` or `tenant_registry` |
|
|
| `label` | string | Operator-facing return label |
|
|
| `url` | string | Canonical return URL |
|
|
| `filters` | array<string, mixed> | Allowlisted triage filters and sort state for registry returns |
|
|
|
|
## Validation Rules
|
|
|
|
### Concern family and state compatibility
|
|
|
|
| Concern Family | Allowed States | Allowed Reasons |
|
|
|---------------|----------------|-----------------|
|
|
| `backup_health` | `absent`, `stale`, `degraded` | Existing `TenantBackupHealthAssessment` reason constants that justify tenant-opening triage |
|
|
| `recovery_evidence` | `unvalidated`, `weakened` | Existing restore-safety or recovery-triage reason codes such as `no_history`, `failed`, `partial`, or `completed_with_follow_up` |
|
|
|
|
### Token decode rules
|
|
|
|
- Empty, malformed, or unsupported-version tokens decode to `null`.
|
|
- Unknown families, states, reasons, or return kinds decode to `null`.
|
|
- Tokens bound to a different tenant route or incompatible workspace context decode to `null`.
|
|
- Successful decode does not replace current tenant truth; it only authorizes rendering of the continuity block.
|
|
|
|
### Return filter rules
|
|
|
|
- Registry return filters may include only the existing triage keys: `backup_posture`, `recovery_evidence`, and `triage_sort`.
|
|
- Filter values must pass the same sanitizers used by `ListTenants` on mount.
|
|
- Unknown or empty filters are dropped from the return target.
|
|
|
|
## Relationships
|
|
|
|
- One workspace attention item may generate one `PortfolioArrivalContext` when its destination is a tenant-level surface.
|
|
- One filtered tenant-registry session may generate one `PortfolioArrivalContext` per tenant-open action.
|
|
- One `PortfolioArrivalContext` owns exactly one `NextStepTarget` and one `ReturnTarget`.
|
|
- One tenant dashboard request may resolve zero or one valid `PortfolioArrivalContext`.
|
|
|
|
## Rendering Rules
|
|
|
|
- No valid `PortfolioArrivalContext` means the tenant dashboard renders normally with no continuity block.
|
|
- A valid `PortfolioArrivalContext` may render even if current tenant truth has changed, but the block must differentiate arrival reason from current truth.
|
|
- `NextStepTarget.disabled = true` still allows the block to render, but the CTA must be absent or disabled with helper text.
|
|
- `ReturnTarget` renders only when the origin surface can be reconstituted safely. |