TenantAtlas/specs/189-portfolio-triage-review-state/spec.md
ahmido 2f45ff5a84 feat: add portfolio triage review state tracking (#220)
## Summary
- add tenant triage review-state persistence, fingerprinting, resolver logic, service layer, and migration for current affected-set tracking
- surface review-state and affected-set progress across tenant registry, tenant dashboard arrival continuity, and workspace overview
- extend RBAC, audit/badge support, specs, and test coverage for portfolio triage review-state workflows
- suppress expected hidden-page background transport failures in the global unhandled rejection logger while keeping visible-page failures logged

## Validation
- targeted Pest coverage added for tenant registry, workspace overview, arrival context, RBAC authorization, badges, fingerprinting, resolver behavior, and logger asset behavior
- code formatted with `cd apps/platform && ./vendor/bin/sail bin pint --dirty --format agent`

## Notes
- full suite was not re-run in this final step
- branch includes the spec artifacts under `specs/189-portfolio-triage-review-state/`

Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #220
2026-04-10 21:35:17 +00:00

36 KiB

Feature Specification: Portfolio Triage Review State and Operator Progress

Feature Branch: [189-portfolio-triage-review-state]
Created: 2026-04-10
Status: Draft
Input: User description: "Spec 189 - Portfolio Triage Review State / Operator Progress Tracking"

Spec Scope Fields (mandatory)

  • Scope: workspace
  • Primary Routes:
    • /admin as the workspace overview where backup-attention and recovery-attention surfaces gain current-set progress semantics
    • /admin/tenants as the canonical portfolio triage list that shows review state, supports review-state filtering, and preserves concern-family focus
    • /admin/tenants/{tenant} as the existing tenant-registry inspect route that remains the canonical registry detail path
    • /admin/t/{tenant} as the canonical tenant dashboard arrival surface where the triage continuity block shows current review state and exposes lightweight progress actions
    • /admin/t/{tenant}/backup-sets and /admin/t/{tenant}/restore-runs as deeper existing backup and recovery follow-up surfaces that remain authoritative for domain truth after the triage action is recorded
  • Data Ownership:
    • Existing backup-health and recovery-evidence posture remain derived tenant truth built from current backup, restore, and workspace overview aggregation layers
    • A new persisted triage-review record becomes the source of truth for whether one workspace has already reviewed one tenant for one concern family, because that operational truth must survive navigation and time
    • The new record is workspace-shared but tenant-owned in storage terms because it is attached to one tenant and one concern family inside one workspace; it is not a formal review artifact, evidence artifact, or policy-governance record
    • Workspace progress summaries and registry badges remain derived views over the combination of current concern truth and the persisted triage-review record; this feature does not create a second posture model or a formal review workflow layer
  • RBAC:
    • Workspace membership remains required to render /admin and /admin/tenants, and tenant membership remains required for /admin/t/{tenant} and deeper tenant surfaces
    • Review-state visibility follows the same visible-tenant and visible-concern boundaries as portfolio triage; out-of-scope tenants and concern hints remain hidden under deny-as-not-found rules
    • Members with view access may see review-state badges and progress, but only members with the canonical server-side capability to update portfolio triage review state, or an equivalent existing operator-level capability mapped through the capability registry, may mark reviewed or follow-up needed
    • Non-members remain 404; in-scope members lacking the mutation capability receive 403 on review-state mutations

UI/UX Surface Classification (mandatory when operator-facing surfaces are changed)

Surface Surface Type Primary Inspect/Open Model Row Click Secondary Actions Placement Destructive Actions Placement Canonical Collection Route Canonical Detail Route Scope Signals Canonical Noun Critical Truth Visible by Default Exception Type
Workspace overview current-set progress summary Embedded status summary / drill-in surface Explicit stat or summary CTA to a filtered tenant-registry slice forbidden Inline summary links only none /admin /admin/tenants Active workspace, concern family, visible affected-set scope Triage progress / Review state Reviewed X/Y, follow-up-needed count, and changed-since-review count for the current affected set additive summary strip
Tenant registry triage list CRUD / list-first resource Full-row click remains the canonical inspect path for registry detail, while the existing tenant-open shortcut remains the fast triage continuation required One inline safe shortcut plus More, with review-state mutations inside More to preserve the action-surface contract none /admin/tenants /admin/tenants/{tenant} Active workspace, current concern-family focus, backup posture, recovery evidence, review state Tenants / Tenant Current concern truth and current review state stay visible together without merging working-surface augmentation
Tenant dashboard arrival continuity block Embedded arrival and progress control surface Explicit inline actions inside the continuity block for review-state mutation and return-to-triage forbidden Inline actions and helper copy inside the block none /admin/t/{tenant} Existing next-step surfaces remain /admin/t/{tenant}/backup-sets or /admin/t/{tenant}/restore-runs Workspace context, tenant context, arrival source, concern family, review state Triage review / Review state Why the operator is here, whether it was already reviewed, and whether it changed since review additive continuity control

Operator Surface Contract (mandatory when operator-facing surfaces are changed)

Surface Primary Persona Surface Type Primary Operator Question Default-visible Information Diagnostics-only Information Status Dimensions Used Mutation Scope Primary Actions Dangerous Actions
Workspace overview current-set progress summary Workspace operator Embedded portfolio summary How far through the current affected set are we, and which bucket still needs attention? Concern-family label, reviewed count, affected-set total, follow-up-needed count, changed-since-review count Raw fingerprints, low-level resolver inputs, inactive historical rows concern family, current affected-set size, review state, portfolio urgency none; read-only summary surface Open filtered registry slices for not reviewed, follow-up needed, or changed since review none
Tenant registry triage list Workspace operator List-first working surface Which visible affected tenants are untouched, already checked, still needing follow-up, or changed since review? Tenant identity, backup posture, recovery evidence, review state, current slice focus, bounded reviewer or timestamp context when available Raw snapshot JSON, fingerprint ingredients, inactive state history backup posture, recovery evidence, review state, triage priority TenantPilot only for review-state mutations; read-only for posture truth Open tenant, keep triage filters, mark reviewed, mark follow-up needed none
Tenant dashboard arrival continuity block Workspace operator arriving from triage Embedded continuity and mutation surface Why was this tenant opened, has this concern already been checked, and can I close or park it now? Arrival reason, triggering concern family, current review state, last reviewed actor or time when available, return target Fingerprint delta detail, low-level concern payloads, inactive prior records arrival reason, concern family, review state, current-vs-reviewed drift TenantPilot only for review-state mutation; deeper next-step links stay read-only navigation Mark reviewed, Mark follow-up needed, Return to triage, Open next-step follow-up surface none

Proportionality Review (mandatory when structural complexity is introduced)

  • New source of truth?: yes
  • New persisted entity/table/artifact?: yes
  • New abstraction?: yes
  • New enum/state/reason family?: yes
  • New cross-domain UI framework/taxonomy?: no
  • Current operator problem: The product can already identify affected tenants and explain why one tenant was opened, but it still cannot tell the operator which affected tenants were already checked, which ones still need follow-up, or which ones became relevant again after a prior review.
  • Existing structure is insufficient because: Current posture truth and arrival context are intentionally read-first and request-scoped. They can explain concern state, but they do not persist lightweight operator progress across sessions, registry revisits, or team handoff.
  • Narrowest correct implementation: Introduce one lightweight persisted triage-review record per workspace, tenant, and concern family, derive not reviewed and changed since review around that record, and reuse existing overview, registry, and arrival surfaces rather than creating notes, assignments, review packs, or a workflow engine.
  • Ownership cost: One small persisted review-state model, a stable fingerprint rule, bounded preloading and derivation logic, additive UI badges and actions on existing surfaces, and focused regression coverage for state derivation, progress counts, and RBAC.
  • Alternative intentionally rejected: Reusing formal TenantReview or ReviewPack artifacts, introducing user-specific queues, or adding a broader case-management workflow were rejected because they overshoot the immediate portfolio-triage need and would create a heavier governance layer than this release requires.
  • Release truth: current-release portfolio-triage workflow hardening

User Scenarios & Testing (mandatory)

User Story 1 - Record That A Concern Was Checked (Priority: P1)

As a workspace operator, I want to mark the current tenant concern as reviewed or follow-up needed from the tenant context so that the portfolio queue reflects work I already did.

Why this priority: Without a persisted action at the point of review, the operator still has to track progress outside the product.

Independent Test: Can be fully tested by opening a tenant from portfolio triage, marking the current concern reviewed or follow-up needed, and verifying that the registry and overview immediately reflect the new state for the same concern family.

Acceptance Scenarios:

  1. Given a tenant is currently affected for backup health and has no active review record, When an authorized operator opens the Mark reviewed preview, confirms the TenantPilot only scope, and submits it from the arrival continuity block, Then the tenant shows Reviewed for backup health and stores the current fingerprint and review timestamp.
  2. Given a tenant is currently affected for recovery evidence and still needs additional work, When an authorized operator opens the Mark follow-up needed preview, confirms the TenantPilot only scope, and submits it, Then the tenant shows Follow-up needed for that recovery concern while the underlying posture remains unchanged.

User Story 2 - Work The Remaining Set From The Registry (Priority: P1)

As a workspace operator, I want the tenant registry to show and filter review state so that I can quickly focus on untouched or reappeared tenants.

Why this priority: Portfolio triage becomes operationally useful only if the registry can separate completed work from the remaining work.

Independent Test: Can be fully tested by seeding affected tenants with mixed review states, opening /admin/tenants, and verifying the review-state column, state filters, and concern-family focus behavior.

Acceptance Scenarios:

  1. Given the current tenant-registry slice is focused on backup health, When the operator filters for Not reviewed, Then only backup-affected tenants without an active matching review record remain.
  2. Given the current tenant-registry slice is mixed across backup and recovery concerns, When the review-state column renders, Then each row identifies which concern family its displayed review state refers to instead of showing an ambiguous shared state.

User Story 3 - Re-review Tenants When The Concern Changes (Priority: P1)

As a workspace operator, I want a previously reviewed tenant to become visibly changed since review when the relevant concern changes so that stale review state does not hide renewed risk.

Why this priority: Review progress is unsafe if an old reviewed badge can survive a materially different current concern.

Independent Test: Can be fully tested by recording a review, changing the current concern fingerprint while the tenant remains affected, and verifying that the registry and dashboard show Changed since review until a new review is recorded.

Acceptance Scenarios:

  1. Given a tenant was reviewed when backup health was stale, When the current backup concern changes to a different stable reason or state while the tenant remains affected, Then the system shows Changed since review instead of Reviewed.
  2. Given a tenant was marked Follow-up needed for recovery evidence, When the current recovery fingerprint changes, Then the changed-since-review state overrides the prior manual state until the operator reviews the new situation.

User Story 4 - See Honest Progress For The Current Affected Set (Priority: P2)

As a workspace operator, I want the workspace overview to show progress only for the tenants that are currently affected so that the product tells me what is left right now rather than what was reviewed historically.

Why this priority: Progress is misleading if calm or resolved tenants keep inflating reviewed counts or if reviewed badges are mistaken for fixed posture.

Independent Test: Can be fully tested by mixing currently affected tenants, resolved tenants, and stale-review tenants, then verifying that the overview summary counts only current affected tenants and keeps posture separate from review state.

Acceptance Scenarios:

  1. Given a workspace has nine currently affected tenants, When the overview progress summary renders, Then the reviewed total and bucket counts are derived only from those nine tenants and exclude calm or resolved tenants.
  2. Given a tenant remains weak but is marked Reviewed, When the registry or overview renders, Then the product still shows the weak posture separately and does not imply that the issue is fixed.

Edge Cases

  • A tenant can be affected in both backup_health and recovery_evidence at the same time; the product must keep one review state per concern family and must not let one family's review mark the other family reviewed.
  • The registry can be opened without a single concern-family focus; in that mixed slice the displayed review state must name the concern family it refers to instead of pretending there is one global tenant review state, and it must choose that family by reusing the existing worst-first portfolio concern priority rules already used for mixed registry triage.
  • A concern can disappear after review and later reappear; prior resolved or inactive review records must not make the tenant count as reviewed for the new current affected set unless the current fingerprint matches an active record.
  • Two operators may update the same workspace-shared review state close together; V1 uses one coherent active state with last-write-wins semantics and does not introduce per-change collaboration workflows.
  • Cosmetic text or volatile timestamps may change while the material concern remains the same; fingerprinting must not mark the tenant changed since review unless stable concern inputs changed.
  • A user may be allowed to view the registry and dashboard but not mutate review state; review-state truth remains visible while mutation actions degrade safely or fail with 403.

Requirements (mandatory)

Constitution alignment (required): This feature adds lightweight TenantPilot-only DB mutations and one new persisted review-state truth, but it introduces no Microsoft Graph calls, no remote writes, and no long-running work. Review-state updates are internal triage-progress writes only, so they do not require an OperationRun. Each mutation still uses a bounded pre-execution preview plus explicit confirmation that shows the concern family, current review state, target manual state, and TenantPilot only scope. V1 also does not introduce a dedicated per-change audit timeline for these updates because the product decision is to stay lightweight and non-governance-oriented.

Constitution alignment (PROP-001 / ABSTR-001 / PERSIST-001 / STATE-001 / BLOAT-001): New persistence is justified because current operator workflow now requires progress that survives navigation and time, while request-scoped posture truth and arrival context do not. The persisted addition remains narrow: one active triage-review record per workspace, tenant, and concern family plus derived not reviewed and changed since review states. No review-pack layer, no generic workflow engine, and no formal evidence model are introduced.

Constitution alignment (OPS-UX): No OperationRun, progress widget for execution, or terminal operation notification is introduced. These are quick internal DB writes whose effect is immediately reflected in existing portfolio surfaces.

Constitution alignment (Read/Write Separation): Because these writes are local, reversible, and sub-2-second, the required preview or dry-run is implemented as a bounded pre-execution summary plus explicit confirmation on the existing Filament actions rather than as a separate remote dry-run pipeline.

Constitution alignment (RBAC-UX): Authorization spans the workspace plane at /admin and /admin/tenants plus the tenant plane at /admin/t/{tenant}. Non-members or actors outside workspace or tenant scope remain 404. Established members without the review-state mutation capability receive 403 on Mark reviewed and Mark follow-up needed. Server-side authorization remains the source of truth for every mutation. Existing tenant resource global search behavior remains tenant-safe and unchanged; review state is not added as a new cross-tenant search hint.

Constitution alignment (OPS-EX-AUTH-001): Not applicable. No auth-handshake behavior changes.

Constitution alignment (BADGE-001): The new status-like labels Not reviewed, Reviewed, Follow-up needed, and Changed since review must use centralized badge and label semantics rather than page-local color decisions. Tests must cover any new centralized mappings so review-state meaning stays consistent across overview, registry, and arrival surfaces.

Constitution alignment (UI-FIL-001): The feature reuses existing Filament tables, filters, actions, stats, sections, and badge-like shared primitives. It must avoid local replacement markup for review-state badges or action blocks. Semantic emphasis comes from existing Filament primitives and central label mappings rather than page-specific border or color language.

Constitution alignment (UI-NAMING-001): Operator-facing vocabulary stays explicit and narrow: Review state, Mark reviewed, Mark follow-up needed, and Changed since review. The wording must not borrow formal-governance language such as Tenant review complete, approved, resolved, or evidence accepted.

Constitution alignment (UI-CONST-001 / UI-SURF-001 / UI-HARD-001 / UI-EX-001 / UI-REVIEW-001): The tenant registry remains the canonical collection route and keeps full-row click as its one primary inspect model. Review-state mutations on the registry live in overflow, not as competing primary opens. The tenant dashboard arrival continuity block is an additive mutation surface with explicit inline actions. No redundant view affordance, no destructive control, and no new cross-domain review page are introduced.

Constitution alignment (OPSURF-001): Default-visible content on /admin and /admin/tenants remains operator-first: current concern truth, current review state, and remaining work. Diagnostics such as fingerprints and snapshots stay secondary. Every review-state mutation must communicate that it changes TenantPilot-only portfolio progress, not Microsoft tenant posture or configuration.

Constitution alignment (UI-SEM-001 / LAYER-001 / TEST-TRUTH-001): Direct mapping from current posture alone to operator progress is insufficient because posture truth answers what is wrong, not whether someone already checked it. The new triage-review layer is therefore allowed, but it must stay narrow, concern-family-bound, and clearly secondary to posture truth. Tests must focus on business consequences such as wrong counts, stale reviewed badges, family conflation, and false fixed semantics.

Constitution alignment (Filament Action Surfaces): The Action Surface Contract remains satisfied. TenantResource keeps exactly one primary inspect model, retains its existing safe tenant-open shortcut, and places the new review-state mutations in overflow. TenantDashboard gets additive inline actions inside the existing continuity block, not redundant header actions. No empty action groups or destructive placements are introduced. UI-FIL-001 remains satisfied with no exception required.

Constitution alignment (UX-001 - Layout & Information Architecture): No new create or edit forms are introduced. The tenant registry continues to provide search, sort, filters, and meaningful empty states for core dimensions. The tenant dashboard uses existing cards or sections for the continuity block rather than introducing a new standalone screen. View or detail surfaces remain operator-first and do not turn review state into a full workflow board.

Functional Requirements

  • FR-189-001: The system MUST persist one active triage-review record per workspace, tenant, and concern family and MUST allow prior records to become inactive instead of overwriting historical continuity in place.
  • FR-189-002: Persisted manual review states MUST be limited to reviewed and follow_up_needed; not_reviewed and changed_since_review MUST remain derived states.
  • FR-189-003: When a currently affected tenant has no active triage-review record for the current concern family, the system MUST derive not_reviewed.
  • FR-189-004: Authorized operators MUST be able to mark a currently affected tenant concern as reviewed from portfolio triage context.
  • FR-189-005: Authorized operators MUST be able to mark a currently affected tenant concern as follow_up_needed from portfolio triage context.
  • FR-189-006: Setting either manual state MUST store the current concern family, the current stable concern fingerprint, the review timestamp, the reviewing actor when available, and a lightweight bounded snapshot sufficient to explain what was reviewed without becoming a formal evidence artifact.
  • FR-189-007: The stored fingerprint MUST be based only on stable concern inputs such as concern family, concern state, stable reason code, and stable supporting keys; it MUST NOT depend on cosmetic labels, free-text notes, or volatile timestamps.
  • FR-189-008: When the current concern still exists but the current fingerprint differs from the stored review fingerprint, the system MUST derive changed_since_review (stale_review) for that workspace, tenant, and concern family.
  • FR-189-009: changed_since_review MUST override displayed reviewed or follow_up_needed until an authorized operator records a new review against the current fingerprint.
  • FR-189-010: Review state MUST never overwrite, hide, or downgrade the separately displayed backup posture or recovery evidence truth; a tenant may remain weak and reviewed at the same time.
  • FR-189-011: Backup-health and recovery-evidence review states MUST be stored and resolved independently. Reviewing one concern family MUST NOT mark the other family reviewed.
  • FR-189-012: The tenant registry MUST display the resolved review state for each currently affected row and MUST identify the concern family that state refers to whenever the current view is not already family-specific.
  • FR-189-013: When /admin/tenants is in a family-specific triage slice, whether from workspace drilldown or explicit filters, the review-state column and review-state mutations MUST operate on that same concern family.
  • FR-189-014: When /admin/tenants is in a mixed slice with more than one active concern family, the review-state presentation MUST follow the row's highest-priority active concern family according to the existing worst-first portfolio concern priority rules and MUST label that family explicitly.
  • FR-189-015: The tenant registry MUST support filtering the current visible affected set by not_reviewed, reviewed, follow_up_needed, and changed_since_review.
  • FR-189-016: Workspace overview surfaces MUST show minimal progress summary for each current concern family or active triage slice, including at least Reviewed X/Y, Follow-up needed, and Changed since review, and those counts MUST be derived only from the current visible affected set.
  • FR-189-017: Tenants whose relevant concern no longer exists MUST NOT remain counted inside current-set progress or current review-state buckets for that concern family.
  • FR-189-018: The tenant dashboard arrival continuity block MUST show the current review state for the triggering concern family and MUST offer Mark reviewed and Mark follow-up needed actions when the current session arrived from portfolio triage with valid concern context.
  • FR-189-019: Before any review-state mutation executes, the product MUST show a bounded pre-execution preview and explicit confirmation describing the concern family, current review state, target manual state, and TenantPilot only mutation scope.
  • FR-189-020: Generic tenant browsing sessions without portfolio-triage context MUST NOT show triage-review actions, mutation affordances, or progress language that implies a queue.
  • FR-189-021: When an active triage-review record exists, the product MUST expose the last reviewed time and, when known, the last reviewing actor in a bounded way that supports workspace-shared progress without turning into a comment thread or audit timeline.
  • FR-189-022: Review-state visibility MUST remain available to entitled viewers even when they lack mutation capability; mutation attempts from non-members MUST resolve as 404, and mutation attempts from in-scope members without capability MUST resolve as 403.
  • FR-189-023: Review-state loading for the registry and workspace progress summaries MUST remain query-bounded and MUST avoid uncontrolled per-row resolver fanout or per-row fingerprint recomputation that would cause list-level N+1 behavior.
  • FR-189-024: Review-state mutation MUST be a TenantPilot-only workflow mutation that does not start an OperationRun, does not call Microsoft Graph, and does not create a formal TenantReview, ReviewPack, comment thread, assignment, or workflow ticket.
  • FR-189-025: The feature MUST ship without person-specific queues, collaboration conflict resolution, notes, due dates, SLAs, or per-episode review history beyond the minimal active and inactive triage-review record needed to keep current state honest.
  • FR-189-026: Regression coverage MUST prove persistence semantics, fingerprint stability, changed-since-review derivation, family separation, registry badging, registry filtering, workspace progress counts, arrival-block actions, generic-session suppression, preview-and-confirmation semantics, honest posture-plus-review coexistence, and RBAC-safe view-versus-mutate behavior.

Review-State Semantics

  • Active triage-review record: The current workspace-shared progress record for one tenant and one concern family. It stores the operator's last manual state plus the fingerprint that was current when the review was recorded.
  • Inactive triage-review record: A prior record whose concern no longer matches the current affected set or was intentionally superseded. It exists only to keep continuity lightweight and does not create an operator-visible review timeline.
  • Current affected set: The visible tenants whose current backup-health or recovery-evidence posture still matches the concern-family attention rules used by workspace overview and tenant registry triage.
  • Derived state precedence: changed_since_review overrides follow_up_needed, which overrides reviewed, which overrides not_reviewed for currently affected tenants.
  • Current-set progress: The overview summary over the current visible affected set for one concern family or active triage slice; calm or resolved tenants are excluded.

Fingerprint Boundaries

  • The fingerprint must stay stable across repeated reads of the same material concern situation.
  • The fingerprint must change when the current concern family, material concern state, stable reason code, or stable supporting posture keys change.
  • The fingerprint must ignore cosmetic wording, translated copy, timestamps, and other volatile display-only values.
  • Backup-health fingerprints may include stable posture values such as absent, stale, degraded, healthy, schedule-follow-up family, or freshness family.
  • Recovery-evidence fingerprints may include stable posture values such as weakened, unvalidated, no_recent_issues_visible, primary concern reason, or restore-evidence concern family.

UI Action Matrix (mandatory when Filament is changed)

Surface Location Header Actions Inspect Affordance (List/Table) Row Actions (max 2 visible) Bulk Actions (grouped) Empty-State CTA(s) View Header Actions Create/Edit Save+Cancel Audit log? Notes / Exemptions
Workspace overview progress summary app/Filament/Pages/WorkspaceOverview.php, app/Filament/Widgets/Workspace/WorkspaceSummaryStats.php, app/Filament/Widgets/Workspace/WorkspaceNeedsAttention.php none added Explicit stat or summary CTA to a filtered registry slice none none Existing calm-state CTA behavior remains unchanged n/a n/a no dedicated triage-review audit requirement Read-only summary only. No destructive action or new action-group exception is introduced.
Tenant registry triage list app/Filament/Resources/TenantResource.php, app/Filament/Resources/TenantResource/Pages/ListTenants.php Existing header actions remain unchanged Full-row click remains the canonical inspect affordance Existing openTenant safe shortcut remains visible; Mark reviewed and Mark follow-up needed live in More or equivalent overflow No new bulk mutation required in V1 Existing empty-state CTA remains unchanged Existing tenant detail header actions remain unchanged Existing create or edit flows remain unchanged no dedicated triage-review audit requirement Action Surface Contract remains satisfied because review-state mutations do not compete with the primary open model, are not destructive, and open a bounded preview-and-confirmation step before the write executes.
Tenant dashboard arrival continuity block app/Filament/Pages/TenantDashboard.php plus the existing arrival continuity surface introduced by Spec 187 none added Explicit inline Return to triage and next-step links only n/a n/a none n/a n/a no dedicated triage-review audit requirement Mark reviewed and Mark follow-up needed live inside the continuity block as additive inline actions, open a bounded preview-and-confirmation step before write, and stay suppressed for generic browsing sessions without valid triage context.

Key Entities (include if feature involves data)

  • Triage-review record: The persisted workspace-shared progress record for one tenant and one concern family, including manual state, review fingerprint, bounded snapshot, and active or inactive lifecycle.
  • Concern fingerprint: The stable representation of the current material concern situation used to decide whether a prior review still applies.
  • Concern-family focus: The backup-health or recovery-evidence slice that tells the registry and tenant arrival block which review state they are resolving or mutating.
  • Current affected set: The visible tenant population currently matching one concern family's triage rules.
  • Progress summary: The derived count view over the current affected set showing how many items are reviewed, need follow-up, or changed since review.

Assumptions

  • Existing workspace overview, tenant registry triage, and tenant dashboard arrival-context slices already expose enough stable concern-family and reason context to support bounded fingerprint generation without inventing a second posture model.
  • V1 review-state scope is limited to the current portfolio-triage concern families backup_health and recovery_evidence. Extending the concept to other domains requires a follow-up spec.
  • Workspace-shared review state uses one coherent active record with last-write-wins behavior rather than a multi-operator merge or conflict-resolution layer.
  • A lightweight bounded snapshot is sufficient for V1; rich notes, comments, evidence attachments, and long-form review rationale remain intentionally out of scope.

Dependencies

  • Existing workspace overview backup-attention and recovery-attention semantics from the current portfolio-triage work
  • Existing tenant registry triage filters, worst-first ordering, and tenant-open continuity
  • Existing tenant dashboard arrival continuity block introduced by Spec 187
  • Existing backup-health and recovery-evidence truth used by workspace overview and tenant registry triage
  • Existing RBAC helpers, capability registry, and tenant-safe route resolution patterns

Out of Scope and Follow-up

  • No formal TenantReview, ReviewPack, or evidence-artifact workflow
  • No notes, comments, or rich-text rationale on review-state records
  • No assignments, owner queues, or team delegation workflow
  • No due dates, reminders, SLAs, or escalation mechanics
  • No automatic ticket creation or external task synchronization
  • No multi-operator conflict-resolution layer or per-episode review history beyond active and inactive continuity records
  • No broader workflow engine with in progress, blocked, done, or other cross-domain states

Risks

  • If review state is rendered more prominently than posture truth, operators may misread Reviewed as Fixed.
  • If fingerprint rules are too sensitive, tenants will churn into Changed since review because of cosmetic differences instead of meaningful posture change.
  • If fingerprint rules are too weak, materially different concern situations will keep stale Reviewed or Follow-up needed badges.
  • If workspace-shared state lacks reviewer and timestamp context, team handoff can become confusing even in a lightweight model.
  • If registry loading resolves review state per row without batching, portfolio triage performance can regress under moderate affected-set sizes.

Definition of Done

This feature is complete when:

  • a persisted portfolio triage review state exists for workspace, tenant, and concern-family combinations,
  • the tenant registry shows review state beside posture truth without conflating the two,
  • the tenant registry can filter by review state for the current visible affected set,
  • authorized operators can mark Reviewed and Follow-up needed from tenant triage context,
  • fingerprint-based Changed since review derivation works and overrides stale prior manual states,
  • workspace overview surfaces show lightweight progress for the current affected set,
  • backup-health and recovery-evidence review state remain separate,
  • formal review, notes, assignments, and workflow-engine behavior remain absent,
  • targeted regression coverage is green, and
  • the shipped UI remains honest that Reviewed does not mean Fixed.

Success Criteria (mandatory)

Measurable Outcomes

  • SC-189-001: In acceptance review, a workspace operator can determine within 10 seconds from /admin/tenants which currently affected tenants are Not reviewed, Reviewed, Follow-up needed, or Changed since review.
  • SC-189-002: In 100% of covered mutation scenarios, marking Reviewed or Follow-up needed from tenant triage context updates the matching registry row and current-set progress summary without requiring an external note-taking step.
  • SC-189-003: In 100% of covered fingerprint-change scenarios, a previously reviewed or follow-up-needed tenant shows Changed since review until a new review is recorded against the current fingerprint.
  • SC-189-004: In 100% of covered overview and registry scenarios, current-set progress counts include only currently affected visible tenants and exclude calm or resolved tenants.
  • SC-189-005: In 100% of covered truthfulness scenarios, posture truth and review state appear together without any label or layout implying that review state means the tenant is fixed, approved, or recovery-proven.
  • SC-189-006: In RBAC regression coverage, entitled viewers can see review-state truth while non-members receive 404 and in-scope members without mutation capability receive 403 for review-state mutations.
  • SC-189-007: Targeted query-bounded regression coverage shows that representative affected-set registry rendering does not degrade into uncontrolled per-row review-state query fanout.