TenantAtlas/specs/221-findings-operator-inbox/data-model.md
Ahmed Darrazi 8cc73dff71
Some checks failed
PR Fast Feedback / fast-feedback (pull_request) Failing after 4m59s
feat: add findings operator inbox
2026-04-21 10:19:14 +02:00

6.7 KiB

Data Model: Findings Operator Inbox V1

Overview

This feature does not add or modify persisted entities. It introduces two derived read models:

  • the canonical admin-plane My Findings inbox at /admin/findings/my-work
  • one compact Assigned to me signal on /admin

Both remain projections over existing finding, tenant membership, and workspace context truth.

Existing Persistent Inputs

1. Finding

  • Purpose: Tenant-owned workflow record representing current governance or execution work.
  • Key persisted fields used by this feature:
    • id
    • workspace_id
    • tenant_id
    • status
    • severity
    • due_at
    • subject_display_name
    • owner_user_id
    • assignee_user_id
    • reopened_at
  • Relationships used by this feature:
    • tenant()
    • ownerUser()
    • assigneeUser()

Relevant existing semantics:

  • Finding::openStatusesForQuery() defines inbox inclusion for open work.
  • Finding::openStatuses() and terminal statuses remain unchanged.
  • Spec 219 defines assignee inclusion and owner-only exclusion.

2. Tenant

  • Purpose: Tenant boundary for findings ownership and tenant-plane detail navigation.
  • Key persisted fields used by this feature:
    • id
    • workspace_id
    • name
    • external_id
    • status

3. TenantMembership

  • Purpose: Per-tenant entitlement boundary for visibility.
  • Key persisted fields used by this feature:
    • tenant_id
    • user_id
    • role

The inbox and overview signal must only materialize findings from tenants where the current user still has membership and findings-view entitlement.

4. Workspace Context

  • Purpose: Active workspace selection in the admin plane.
  • Source: Existing workspace session context, not a new persisted model for this feature.
  • Effect on this feature:
    • gates entry into the admin inbox
    • constrains visible tenants to the current workspace
    • feeds the workspace overview signal

Derived Presentation Entities

1. AssignedFindingInboxRow

Logical row model for /admin/findings/my-work.

Field Meaning Source
findingId Target finding identifier Finding.id
tenantId Tenant route scope for detail drilldown Finding.tenant_id
tenantLabel Tenant name visible in the queue Tenant.name
summary Operator-facing finding summary Finding.subject_display_name plus existing fallback logic
severity Severity badge value Finding.severity
status Workflow/lifecycle badge value Finding.status
dueAt Due date if present Finding.due_at
dueState Derived urgency label such as overdue or due soon existing finding due-state logic
reopened Whether reopened context should be emphasized Finding.status === reopened or existing lifecycle cues
ownerLabel Accountable owner when different from assignee ownerUser.name
detailUrl Tenant finding detail route derived from tenant finding view route
navigationContext Return-path payload back to the inbox derived from CanonicalNavigationContext

Validation rules:

  • Row inclusion requires all of the following:
    • finding belongs to the current workspace
    • finding belongs to a tenant the current user may inspect
    • finding status is in Finding::openStatusesForQuery()
    • assignee_user_id equals the current user ID
  • Owner-only findings are excluded.
  • Hidden-tenant findings produce no row, no count, and no filter option.

2. MyFindingsInboxState

Logical state model for the inbox page.

Field Meaning
workspaceId Current admin workspace scope
assigneeUserId Fixed personal-work scope
tenantFilter Optional active-tenant prefilter, defaulted from canonical admin tenant context
overdueOnly Optional urgency narrowing
reopenedOnly Optional reopened-work narrowing
highSeverityOnly Optional severity narrowing

Rules:

  • assigneeUserId is fixed and cannot be cleared in v1.
  • tenantFilter is clearable.
  • tenantFilter values may only reference entitled tenants.
  • Invalid or stale tenant filter state is discarded rather than widening visibility.
  • Inbox summary counts reflect the currently visible queue after the fixed assignee scope and any active tenant, overdue, reopened, or high-severity filters are applied.

3. MyFindingsSignal

Logical summary model for the workspace overview.

Field Meaning Source
openAssignedCount Count of visible open assigned findings derived Finding count
overdueAssignedCount Count of visible overdue assigned findings derived Finding count with due_at < now()
isCalm Whether the signal should render calm wording derived from the two counts
headline Operator-facing signal summary derived presentation copy
description Supporting copy clarifying visible-scope truth derived presentation copy
ctaLabel Explicit drill-in label fixed vocabulary Open my findings
ctaUrl Canonical inbox route derived from the new admin page URL

Validation rules:

  • Counts are workspace-scoped and tenant-entitlement scoped.
  • The signal never implies hidden or inaccessible work.
  • Calm wording is only allowed when both visible counts are zero.

State And Ordering Rules

Inbox inclusion order

  1. Restrict to the current workspace.
  2. Restrict to visible tenant IDs.
  3. Restrict to assignee_user_id = current user.
  4. Restrict to Finding::openStatusesForQuery().
  5. Apply optional tenant/overdue/reopened/high-severity filters.
  6. Sort overdue work first, reopened non-overdue work next, then remaining work.
  7. Within each urgency bucket, rows with due dates sort by dueAt ascending, rows without due dates sort last, and remaining ties sort by findingId descending.

Urgency semantics

  • Overdue work is the highest-priority urgency bucket.
  • Reopened non-overdue work is the next urgency bucket.
  • High-severity remains a filter and emphasis cue rather than a separate mandatory sort bucket.
  • Terminal findings are never part of the inbox or signal.

Empty-state semantics

  • If no visible assigned open findings exist anywhere in scope, the inbox shows a calm empty state.
  • If the active tenant prefilter causes the empty state while other visible tenants still have work, the empty state must explain the tenant boundary and provide a clear fallback CTA.

Authorization-sensitive Output

  • Tenant labels, filter values, rows, and counts are only derived from entitled tenants.
  • The inbox itself is workspace-context dependent.
  • Detail navigation remains tenant-scoped and must preserve existing 404/403 semantics on the destination.
  • The derived signal and row model remain useful without ever revealing hidden tenant names or quantities.