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:
Featurefor 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.phpcd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Dashboard/MyFindingsSignalTest.phpcd 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(...)andFinding::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-shellis 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-workunder the existingAdminPanelProvider, not as a tenant resource variant and not as a standalone controller route. - Keep the cross-tenant personal queue fully derived from
Findingplus existing workspace and tenant entitlements, usingFinding::openStatusesForQuery()and eager-loaded tenant/owner/assignee relationships. - Reuse
CanonicalAdminTenantFilterStateto 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
CanonicalNavigationContextwhen building row drilldowns so tenant finding detail can exposeBack to my findingswith tenant-safe continuity. - Add the workspace signal as a dedicated overview payload rendered in the existing
/adminBlade 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 decisionsdata-model.md: existing entities plus derived inbox row and workspace signal projectionscontracts/findings-operator-inbox.logical.openapi.yaml: internal logical contract for the inbox, overview signal, and detail continuity inputsquickstart.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 throughCanonicalNavigationContext. - 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 |