Some checks failed
Main Confidence / confidence (push) Failing after 55s
## Summary - add the canonical admin-plane `My Findings` inbox at `/admin/findings/my-work` - add the workspace overview `Assigned to me` signal and inbox-to-detail continuity - add focused Pest coverage plus the full Spec 221 artifact bundle ## Validation - `cd apps/platform && ./vendor/bin/sail bin pint --dirty --format agent` - `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Findings/MyWorkInboxTest.php tests/Feature/Authorization/MyWorkInboxAuthorizationTest.php tests/Feature/Dashboard/MyFindingsSignalTest.php` - `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Filament/WorkspaceOverviewNavigationTest.php` - integrated-browser smoke completed against the browser-facing `tenantatlas` runtime, including seeded positive-path and negative-path checks plus fixture cleanup ## Filament v5 Guardrails - Livewire v4.0+ compliant - panel provider registration remains in `apps/platform/bootstrap/providers.php` - global search behavior is unchanged; `FindingResource` already has a View page and the new inbox is a custom page, not a searchable resource - no destructive actions were introduced on the inbox or overview signal - no new assets were added; the existing deploy step for `cd apps/platform && php artisan filament:assets` remains unchanged - coverage includes the new inbox page, authorization boundaries, the workspace overview signal, and the overview CTA regression Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de> Reviewed-on: #258
6.7 KiB
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 Findingsinbox at/admin/findings/my-work - one compact
Assigned to mesignal 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:
idworkspace_idtenant_idstatusseveritydue_atsubject_display_nameowner_user_idassignee_user_idreopened_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:
idworkspace_idnameexternal_idstatus
3. TenantMembership
- Purpose: Per-tenant entitlement boundary for visibility.
- Key persisted fields used by this feature:
tenant_iduser_idrole
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_idequals 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:
assigneeUserIdis fixed and cannot be cleared in v1.tenantFilteris clearable.tenantFiltervalues 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
- Restrict to the current workspace.
- Restrict to visible tenant IDs.
- Restrict to
assignee_user_id = current user. - Restrict to
Finding::openStatusesForQuery(). - Apply optional tenant/overdue/reopened/high-severity filters.
- Sort overdue work first, reopened non-overdue work next, then remaining work.
- Within each urgency bucket, rows with due dates sort by
dueAtascending, rows without due dates sort last, and remaining ties sort byfindingIddescending.
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/403semantics on the destination. - The derived signal and row model remain useful without ever revealing hidden tenant names or quantities.