TenantAtlas/specs/222-findings-intake-team-queue/research.md
ahmido 712576c447
Some checks failed
Main Confidence / confidence (push) Failing after 44s
feat: add findings intake queue and stabilize follow-up regressions (#260)
## Summary
- add the new admin findings intake queue at `/admin/findings/intake` with fixed `Unassigned` and `Needs triage` views, tenant-safe filtering, claim flow, and continuity into tenant finding detail and `My Findings`
- add Spec 222 artifacts (`spec`, `plan`, `tasks`, `research`, `data model`, `quickstart`, contract, checklist) and register the new admin page
- fix follow-up regressions uncovered during full-suite validation around findings action-surface declarations, findings list default columns, provider-blocked run messaging, operation catalog aliases, and workspace overview query volume

## Validation
- `cd apps/platform && ./vendor/bin/sail bin pint --dirty --format agent`
- `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Findings/FindingsIntakeQueueTest.php tests/Feature/Authorization/FindingsIntakeAuthorizationTest.php tests/Feature/Findings/FindingsClaimHandoffTest.php`
- `cd apps/platform && ./vendor/bin/sail artisan test --compact`

## Notes
- Filament remains on v5 with Livewire v4-compatible patterns
- provider registration remains unchanged in `apps/platform/bootstrap/providers.php`
- no new assets or schema changes are introduced

Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #260
2026-04-21 22:54:08 +00:00

7.0 KiB

Research: Findings Intake & Team Queue V1

Decision 1: Implement the intake queue as an admin panel page with slug findings/intake

  • Decision: Add a new Filament admin page under the existing AdminPanelProvider for the canonical route /admin/findings/intake.
  • Rationale: The feature is an admin-plane, workspace-scoped decision surface, not a tenant-local variant of the existing findings resource. A page registration keeps the same Filament middleware, route shape, session handling, and Livewire page lifecycle already used by MyFindingsInbox.
  • Alternatives considered:
    • Reuse the tenant-local FindingResource list with a preset filter. Rejected because it still forces tenant-first navigation and does not answer the cross-tenant intake question.
    • Add a standalone controller route in routes/web.php. Rejected because this is a normal admin panel surface and should stay inside Filament routing and guardrails.

Decision 2: Keep intake truth as a direct Finding query, but use Finding::openStatuses() instead of openStatusesForQuery()

  • Decision: Build the intake queue from Finding records scoped by current workspace, visible tenant IDs, assignee_user_id IS NULL, and Finding::openStatuses().
  • Rationale: Spec 222 explicitly reuses Spec 111 open statuses for intake: new, triaged, in_progress, and reopened. The broader openStatusesForQuery() helper includes acknowledged, which belongs in existing tenant-local workflows but not in this shared intake queue.
  • Alternatives considered:
    • Reuse Finding::openStatusesForQuery() exactly as MyFindingsInbox does. Rejected because it would leak acknowledged rows into intake and violate FR-003.
    • Introduce a new shared intake-query service immediately. Rejected because there is still only one concrete intake consumer and the repo guidance prefers direct implementation until reuse pressure is real.

Decision 3: Model Unassigned and Needs triage as fixed page-level views, not a new taxonomy or generic filter framework

  • Decision: Represent Unassigned and Needs triage as fixed queue-view controls in the page shell, with Needs triage implemented as the strict subset of intake rows in new or reopened status.
  • Rationale: The two views are product vocabulary, not a reusable classification framework. Page-local view state keeps the implementation explicit, honors the spec's fixed queue contract, and avoids adding new persisted preferences or a generic queue-view registry.
  • Alternatives considered:
    • Add a reusable taxonomy or enum for queue view types. Rejected because two concrete values do not justify new semantic machinery.
    • Model the views as ordinary optional filters only. Rejected because the spec treats them as fixed queue modes, not ad hoc filter combinations.

Decision 4: Reuse canonical admin tenant filter and navigation context helpers

  • Decision: Use CanonicalAdminTenantFilterState to apply the active tenant as the default prefilter and CanonicalNavigationContext to carry Back to findings intake continuity into tenant finding detail.
  • Rationale: The repo already uses these helpers for canonical admin-plane filters and cross-surface back links. Reusing them keeps intake aligned with the existing admin shell, keeps row and count disclosure tenant-safe, and avoids adding another page-specific context mechanism. If a workspace member has no currently viewable findings scope anywhere, the queue should fail with 403 instead of pretending the backlog is simply empty.
  • Alternatives considered:
    • Store active tenant and return-path state in a queue-local session structure. Rejected because the existing helpers already solve both problems and another state path would add drift.
    • Depend on browser history for return links. Rejected because it is brittle across reloads, tabs, and copied URLs.

Decision 5: Add Claim finding as a narrow row action that reuses assignment semantics, adds a lightweight preview/confirmation step, and adds a stale-row conflict guard

  • Decision: Implement Claim finding as the single inline safe shortcut on the intake row, gated by Capabilities::TENANT_FINDINGS_ASSIGN, surfaced through a lightweight preview/confirmation modal, and routed through FindingWorkflowService plus the existing finding.assigned audit action while adding a claim-specific re-check that refuses rows whose assignee is no longer null under lock.
  • Rationale: The current assignment service already owns audit logging, tenant ownership checks, and capability enforcement, but plain assign semantics would allow a stale queue click to overwrite a newer assignee and would not satisfy the repository write-flow governance. A lightweight preview/confirmation step plus a narrow guard inside the existing service seam solves both needs without inventing a second assignment system.
  • Alternatives considered:
    • Reuse the existing Assign form unchanged. Rejected because the intake surface needs a lighter self-claim preview/confirmation flow, not a broader owner/assignee form.
    • Reuse assign() without an extra guard. Rejected because it could silently overwrite another operator's successful claim, violating FR-014.

Decision 6: Keep post-claim continuity explicit through My Findings rather than auto-redirecting away from intake

  • Decision: Refresh the queue after a successful claim so the row disappears immediately, then provide a clear next step into /admin/findings/my-work while leaving row-open detail continuity available when the operator needs deeper context first.
  • Rationale: The queue is still the canonical shared backlog surface. Auto-redirecting every successful claim would interrupt batch intake review, while a clear Open my findings next step satisfies the spec without hiding the updated queue truth.
  • Alternatives considered:
    • Redirect to My Findings after every claim. Rejected because it would add navigation churn when multiple intake items are reviewed in sequence.
    • Keep success feedback to a generic toast only. Rejected because the spec requires a clear next step into the existing personal execution destination.

Decision 7: Prove the feature with three focused feature suites and rely on existing UI guard tests as ambient protection

  • Decision: Add FindingsIntakeQueueTest, FindingsIntakeAuthorizationTest, and FindingsClaimHandoffTest as the focused proving suites while allowing existing action-surface and Filament-table guards to stay in their normal CI role.
  • Rationale: The user-visible risk is queue truth, hidden-tenant isolation, claim authorization, stale-row conflict handling, and handoff continuity. Those are best proven with focused feature tests that exercise the real Livewire and Filament seams.
  • Alternatives considered:
    • Add browser coverage. Rejected because the surface is straightforward and already well served by existing feature-test patterns.
    • Add a new heavy-governance suite for this page. Rejected because the change is still one bounded feature surface, not a new cross-cutting UI family.