TenantAtlas/specs/221-findings-operator-inbox/plan.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

21 KiB

Implementation Plan: Findings Operator Inbox V1

Branch: 221-findings-operator-inbox | Date: 2026-04-20 | Spec: /Users/ahmeddarrazi/Documents/projects/wt-plattform/specs/221-findings-operator-inbox/spec.md Input: Feature specification from /Users/ahmeddarrazi/Documents/projects/wt-plattform/specs/221-findings-operator-inbox/spec.md

Note: This plan keeps the work inside the existing admin workspace shell, tenant-owned Finding truth, and current tenant finding detail surface. The intended implementation is one new canonical /admin page plus one small workspace overview signal. It does not add persistence, capabilities, workflow states, queue automation, or a second mutation lane.

Summary

Add one canonical admin-plane inbox at /admin/findings/my-work that shows the current user's open assigned findings across visible, capability-eligible tenants, keeps urgency first with a deterministic overdue-then-reopened ordering, and drills into the existing tenant finding detail with a preserved return path. Reuse existing Finding lifecycle and responsibility semantics, CanonicalAdminTenantFilterState for active-tenant prefiltering and applied-scope metadata, CanonicalNavigationContext for queue-to-detail continuity, and extend WorkspaceOverviewBuilder plus the workspace overview Blade view with one compact Assigned to me signal that exposes open and overdue counts with a single CTA into the inbox.

Technical Context

Language/Version: PHP 8.4.15, Laravel 12, Filament v5, Livewire v4, Blade
Primary Dependencies: Filament admin panel pages, Finding, FindingResource, WorkspaceOverviewBuilder, WorkspaceContext, WorkspaceCapabilityResolver, CapabilityResolver, CanonicalAdminTenantFilterState, and CanonicalNavigationContext
Storage: PostgreSQL via existing findings, tenants, tenant_memberships, and workspace context session state; no schema changes planned
Testing: Pest v4 feature tests with Livewire/Filament page assertions
Validation Lanes: fast-feedback, confidence Target Platform: Dockerized Laravel web application via Sail locally and Linux containers in deployment
Project Type: Laravel monolith inside the wt-plattform monorepo
Performance Goals: Keep inbox and overview rendering DB-only, eager-load tenant/owner/assignee display context, avoid N+1 queries, and keep the first operator scan within the 10-second acceptance target
Constraints: No Graph calls, no new OperationRun, no new workflow states or owner semantics, no new capabilities, no new queue mutations, no hidden-tenant leakage, no duplicate personal-work truth between inbox and overview, and no new assets
Scale/Scope: One admin page, one workspace overview signal, one derived cross-tenant personal-assignment query, three focused feature suites, and one focused extension to existing route-alignment coverage

UI / Surface Guardrail Plan

  • Guardrail scope: changed surfaces
  • Native vs custom classification summary: native Filament page, table, empty-state, badge, and Blade summary primitives only
  • Shared-family relevance: findings workflow family and workspace overview summary family
  • State layers in scope: shell, page, detail, URL-query
  • Handling modes by drift class or surface: review-mandatory
  • Repository-signal treatment: review-mandatory
  • Special surface test profiles: global-context-shell
  • Required tests or manual smoke: functional-core, state-contract
  • Exception path and spread control: none; the inbox remains a standard read-first admin page and the overview signal stays an embedded summary, not a second queue
  • Active feature PR close-out entry: Guardrail

Constitution Check

GATE: Passed before Phase 0 research. Re-check after Phase 1 design.

Principle Pre-Research Post-Design Notes
Inventory-first / snapshots-second PASS PASS The feature reads live Finding assignment and lifecycle truth only; no new snapshot or backup semantics are introduced
Read/write separation PASS PASS The inbox and overview signal are read-only; existing finding lifecycle mutations remain on tenant finding detail
Graph contract path PASS PASS No Graph client, contract-registry, or external API work is added
Deterministic capabilities / RBAC-UX PASS PASS Workspace membership gates the admin page, missing workspace context follows the existing chooser or resume flow, tenant entitlement plus existing findings capability gate row visibility, filter values, summary counts, overview counts, and detail drilldown, and non-members remain 404
Workspace / tenant isolation PASS PASS Admin-plane inbox is workspace-scoped while drilldown stays on /admin/t/{tenant}/findings/{finding} with tenant-safe continuity
Run observability / Ops-UX PASS PASS No long-running work, no OperationRun, and no notification or progress-surface changes are introduced
Proportionality / no premature abstraction PASS PASS The plan keeps the query logic inside the new page and existing workspace builder seams; no new shared registry or durable abstraction is required
Persisted truth / few layers PASS PASS Inbox rows and overview counts remain direct derivations of existing Finding, tenant membership, and workspace context data
Badge semantics (BADGE-001) PASS PASS Severity, lifecycle, and urgency cues reuse existing findings badge semantics rather than introducing local status language
Filament-native UI (UI-FIL-001) PASS PASS Implementation stays within Filament page, table, header action, empty-state, and existing Blade summary composition
Action surface / inspect model PASS PASS The inbox keeps one inspect model, full-row open to finding detail, no redundant View, and no dangerous queue actions
Decision-first / OPSURF PASS PASS The inbox is the primary decision surface and the overview signal stays secondary with one CTA
Test governance (TEST-GOV-001) PASS PASS Proof remains in three focused feature suites plus one focused extension to existing route-alignment coverage, with no browser or heavy-governance promotion
Filament v5 / Livewire v4 compliance PASS PASS The design uses existing Filament v5 page patterns and Livewire v4-compatible page/table behavior only
Provider registration / global search / assets PASS PASS Panel providers already live in apps/platform/bootstrap/providers.php; the new page extends AdminPanelProvider only, FindingResource global search behavior is unchanged and already has a View page, and no new assets are required

Test Governance Check

  • Test purpose / classification by changed surface: Feature for the admin inbox page, authorization boundary behavior, and workspace overview signal
  • Affected validation lanes: fast-feedback, confidence
  • Why this lane mix is the narrowest sufficient proof: The feature is proven by visible operator behavior, capability-safe tenant filtering, continuity into existing tenant detail, empty-state fallback behavior, and overview-to-inbox truth alignment. Focused feature tests cover that without adding unit seams, browser automation, or heavy-governance breadth.
  • Narrowest proving command(s):
    • cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Findings/MyWorkInboxTest.php tests/Feature/Authorization/MyWorkInboxAuthorizationTest.php
    • cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Dashboard/MyFindingsSignalTest.php
    • cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Filament/WorkspaceOverviewNavigationTest.php
  • Fixture / helper / factory / seed / context cost risks: Moderate. The tests need one workspace, multiple visible and hidden tenants, owner-versus-assignee combinations, open and terminal findings, and explicit workspace plus active-tenant session context.
  • Expensive defaults or shared helper growth introduced?: no; the new suites should reuse existing createUserWithTenant(...) and Finding::factory() flows and keep any inbox-specific fixture helper local to the new tests
  • Heavy-family additions, promotions, or visibility changes: none
  • Surface-class relief / special coverage rule: named special profile global-context-shell is required because the inbox depends on workspace context, active tenant continuity, and admin-to-tenant drilldown behavior
  • Closing validation and reviewer handoff: Reviewers should rely on the exact commands above and verify that hidden-tenant or capability-blocked findings never leak into rows, filter options, or counts, owner-only findings stay out of the queue, the active tenant prefilter is clearable without removing personal scope, the empty state offers the correct fallback CTA, and the detail page exposes a working return path to the inbox.
  • Budget / baseline / trend follow-up: none
  • Review-stop questions: Did the implementation stay read-only? Did the overview signal remain a signal instead of a second queue? Did any shared query abstraction appear without clear reuse pressure beyond this feature? Did row drilldown preserve tenant-safe continuity?
  • Escalation path: document-in-feature unless a second cross-tenant findings surface or a new shared query framework is proposed, in which case split or follow up with a dedicated spec
  • Active feature PR close-out entry: Guardrail
  • Why no dedicated follow-up spec is needed: The feature remains bounded to one queue surface and one overview signal, with no new persistence, workflow family, or reusable platform abstraction

Project Structure

Documentation (this feature)

specs/221-findings-operator-inbox/
├── plan.md
├── research.md
├── data-model.md
├── quickstart.md
├── contracts/
│   └── findings-operator-inbox.logical.openapi.yaml
├── checklists/
│   └── requirements.md
└── tasks.md

Source Code (repository root)

apps/platform/
├── app/
│   ├── Filament/
│   │   ├── Pages/
│   │   │   └── Findings/
│   │   │       └── MyFindingsInbox.php
│   │   └── Resources/
│   │       └── FindingResource.php
│   ├── Providers/
│   │   └── Filament/
│   │       └── AdminPanelProvider.php
│   └── Support/
│       ├── Filament/
│       │   └── CanonicalAdminTenantFilterState.php
│       ├── Navigation/
│       │   └── CanonicalNavigationContext.php
│       └── Workspaces/
│           └── WorkspaceOverviewBuilder.php
├── resources/
│   └── views/
│       └── filament/
│           └── pages/
│               ├── findings/
│               │   └── my-findings-inbox.blade.php
│               └── workspace-overview.blade.php
└── tests/
    └── Feature/
        ├── Authorization/
        │   └── MyWorkInboxAuthorizationTest.php
        ├── Dashboard/
        │   └── MyFindingsSignalTest.php
    ├── Filament/
    │   └── WorkspaceOverviewNavigationTest.php
        └── Findings/
            └── MyWorkInboxTest.php

Structure Decision: Standard Laravel monolith. The feature stays inside the existing admin panel provider, finding domain model, workspace overview builder, and focused Pest feature suites. No new base directory, package, or persistent model is required.

Complexity Tracking

Violation Why Needed Simpler Alternative Rejected Because
none

Proportionality Review

  • Current operator problem: Assignee-based finding work exists in the data model but not as a trustworthy cross-tenant start-of-day surface.
  • Existing structure is insufficient because: The tenant-local findings list can filter to My assigned work, but it still forces users to guess the correct tenant first and does not create one canonical admin-plane queue.
  • Narrowest correct implementation: Add one admin page and one small workspace overview signal, both derived directly from existing finding assignment, lifecycle, due-date, severity, and entitlement truth.
  • Ownership cost created: One new page/view pair, one small overview payload addition, three focused feature suites, and one focused extension to existing route-alignment coverage.
  • Alternative intentionally rejected: A broader cross-tenant findings register, owner-plus-assignee mixed queue, or new shared findings query service. Those would increase semantics and maintenance cost beyond the current release need.
  • Release truth: Current-release truth. The work operationalizes the already-shipped assignee concept now rather than preparing future queue or notification systems.

Phase 0 Research

Research outcomes are captured in /Users/ahmeddarrazi/Documents/projects/wt-plattform/specs/221-findings-operator-inbox/research.md.

Key decisions:

  • Implement the inbox as an admin panel page with slug findings/my-work under the existing AdminPanelProvider, not as a tenant resource variant and not as a standalone controller route.
  • Keep the cross-tenant personal queue fully derived from Finding plus existing workspace and tenant entitlements, using Finding::openStatusesForQuery() and eager-loaded tenant/owner/assignee relationships.
  • Reuse CanonicalAdminTenantFilterState to apply the active tenant as the default prefilter, expose the applied-scope state, and preserve the clear-filter behavior without inventing a second context mechanism.
  • Use CanonicalNavigationContext when building row drilldowns so tenant finding detail can expose Back to my findings with tenant-safe continuity.
  • Add the workspace signal as a dedicated overview payload rendered in the existing /admin Blade page rather than overloading generic summary metrics, because the signal must show open count, overdue count, and one explicit CTA.
  • Prove the feature with three focused Pest feature suites plus one focused extension to existing route-alignment coverage.

Phase 1 Design

Design artifacts are created under /Users/ahmeddarrazi/Documents/projects/wt-plattform/specs/221-findings-operator-inbox/:

  • research.md: routing, query, continuity, and workspace-signal decisions
  • data-model.md: existing entities plus derived inbox row and workspace signal projections
  • contracts/findings-operator-inbox.logical.openapi.yaml: internal logical contract for the inbox, overview signal, and detail continuity inputs
  • quickstart.md: focused validation workflow for implementation and review

Design decisions:

  • No schema migration is required; the inbox and overview signal remain fully derived.
  • The canonical implementation seam is one new admin page plus one existing workspace overview builder/view extension, not a new shared findings-query subsystem.
  • Active tenant context stays canonical through CanonicalAdminTenantFilterState, while detail continuity stays canonical through CanonicalNavigationContext.
  • Existing tenant finding detail remains the only mutation surface for assignment and workflow actions.

Phase 1 Agent Context Update

Run:

  • .specify/scripts/bash/update-agent-context.sh copilot

Constitution Check — Post-Design Re-evaluation

  • PASS — the design remains read-surface only, adds no writes, no Graph calls, no OperationRun, no new capability family, and no new assets.
  • PASS — Livewire v4.0+ and Filament v5 constraints remain satisfied, panel provider registration stays in apps/platform/bootstrap/providers.php, and no global-search or destructive-action behavior changes are introduced.

Implementation Strategy

Phase A — Add The Canonical Admin Inbox Surface

Goal: Create one workspace-scoped personal findings queue under /admin/findings/my-work.

Step File Change
A.1 apps/platform/app/Filament/Pages/Findings/MyFindingsInbox.php Add a new Filament admin page with slug findings/my-work, HasTable, workspace membership access checks, and fixed assignee/open-status scope
A.2 apps/platform/resources/views/filament/pages/findings/my-findings-inbox.blade.php Render the page shell, page description, native Filament table, and branch-specific calm empty-state CTAs: clear the tenant prefilter when it alone excludes rows, otherwise open tenant findings for the active tenant or /admin/choose-tenant when no tenant context exists
A.3 apps/platform/app/Providers/Filament/AdminPanelProvider.php Register the new page in the existing admin panel; do not move provider registration because it already lives in apps/platform/bootstrap/providers.php

Phase B — Derive Queue Truth From Existing Findings Semantics

Goal: Keep inclusion, urgency, and owner-versus-assignee behavior aligned with Specs 111 and 219.

Step File Change
B.1 apps/platform/app/Filament/Pages/Findings/MyFindingsInbox.php Build the table query and derived inbox counts from Finding, restricted to the current workspace, visible capability-eligible tenants, assignee_user_id = auth()->id(), and Finding::openStatusesForQuery()
B.2 apps/platform/app/Filament/Pages/Findings/MyFindingsInbox.php and apps/platform/app/Filament/Resources/FindingResource.php Reuse or mirror existing severity, lifecycle, overdue, reopened, and owner context presentation helpers without inventing new badge semantics
B.3 apps/platform/app/Filament/Pages/Findings/MyFindingsInbox.php Add the available-filters contract for fixed assignee scope, tenant, overdue, reopened, and high-severity filters, capability-safe tenant filter options, applied-scope metadata, summary counts, and deterministic urgency sorting with due-date and finding-ID tie-breakers while keeping personal assignment scope fixed and non-removable

Phase C — Honor Active Tenant Context And Queue-to-Detail Continuity

Goal: Make the inbox feel canonical inside the existing admin shell instead of a detached register.

Step File Change
C.1 apps/platform/app/Filament/Pages/Findings/MyFindingsInbox.php Reuse CanonicalAdminTenantFilterState so the active tenant becomes the default prefilter and can be cleared via one header action
C.2 apps/platform/app/Filament/Pages/Findings/MyFindingsInbox.php Build row URLs to tenant finding detail with CanonicalNavigationContext carrying Back to my findings continuity
C.3 apps/platform/app/Filament/Resources/FindingResource.php or existing detail page seam Ensure the tenant detail page consumes the navigation context and exposes the inbox return link without changing mutation semantics

Phase D — Add The Workspace Overview Signal Without Creating A Second Queue

Goal: Make /admin show whether personal findings work exists and link into the inbox in one click.

Step File Change
D.1 apps/platform/app/Support/Workspaces/WorkspaceOverviewBuilder.php Add a small my_findings_signal payload with visible capability-safe open assigned count, overdue count, calm state, and CTA URL
D.2 apps/platform/resources/views/filament/pages/workspace-overview.blade.php Render one compact assigned-to-me summary block ahead of the broader workspace metrics
D.3 Existing workspace navigation helpers Keep the CTA pointed at the canonical inbox route and avoid duplicating queue logic or urgency semantics in the overview itself

Phase E — Protect The Feature With Focused Regression Coverage

Goal: Lock down visibility, prioritization, tenant safety, and overview-to-inbox truth alignment.

Step File Change
E.1 apps/platform/tests/Feature/Findings/MyWorkInboxTest.php Cover capability-safe visible rows only, owner-context rendering when owner differs from assignee, the full available-filters contract truth for fixed assignee scope plus tenant, overdue, reopened, and high-severity filters, tenant filter option truth, owner-only exclusion, deterministic urgency ordering with due-date and finding-ID tie-breakers, clearable tenant prefilter, applied-scope and summary-count truth, zero-visible-work and tenant-prefilter empty-state branches, and drilldown continuity
E.2 apps/platform/tests/Feature/Authorization/MyWorkInboxAuthorizationTest.php Cover admin-plane workspace recovery behavior for missing workspace context, member-without-capability disclosure boundaries, protected-destination 403, and deny-as-not-found boundaries for non-members
E.3 apps/platform/tests/Feature/Dashboard/MyFindingsSignalTest.php Cover overview signal counts, calm state, capability-safe suppression, and CTA alignment with inbox truth
E.4 cd apps/platform && ./vendor/bin/sail bin pint --dirty --format agent plus the focused Pest commands above Run formatting and the narrowest proving suites before closing implementation