TenantAtlas/specs/187-portfolio-triage-arrival-context/data-model.md
ahmido 28e62bd22c feat: preserve portfolio triage arrival context (#218)
## Summary
- preserve portfolio triage arrival context from workspace overview and tenant registry drill-throughs
- add a tenant dashboard continuity widget plus bounded arrival token and resolver support
- add focused Pest coverage for arrival routing, return flow, RBAC degradation, and request-local performance
- include the Spec 187 spec, plan, research, data model, quickstart, contract, and tasks artifacts

## Validation
- integrated browser smoke: workspace overview -> tenant dashboard arrival -> backup sets CTA
- integrated browser smoke: tenant registry triage -> tenant dashboard arrival -> return to tenant triage
- branch includes focused automated test coverage for the new arrival-context surfaces

Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #218
2026-04-09 21:38:31 +00:00

6.7 KiB

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 Sanitized through TenantResource::sanitizeBackupPostures()
recovery_evidence array 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.