Some checks failed
Main Confidence / confidence (push) Failing after 44s
## 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
7.0 KiB
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
AdminPanelProviderfor 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
FindingResourcelist 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.
- Reuse the tenant-local
Decision 2: Keep intake truth as a direct Finding query, but use Finding::openStatuses() instead of openStatusesForQuery()
- Decision: Build the intake queue from
Findingrecords scoped by current workspace, visible tenant IDs,assignee_user_id IS NULL, andFinding::openStatuses(). - Rationale: Spec 222 explicitly reuses Spec 111 open statuses for intake:
new,triaged,in_progress, andreopened. The broaderopenStatusesForQuery()helper includesacknowledged, which belongs in existing tenant-local workflows but not in this shared intake queue. - Alternatives considered:
- Reuse
Finding::openStatusesForQuery()exactly asMyFindingsInboxdoes. Rejected because it would leakacknowledgedrows 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.
- Reuse
Decision 3: Model Unassigned and Needs triage as fixed page-level views, not a new taxonomy or generic filter framework
- Decision: Represent
UnassignedandNeeds triageas fixed queue-view controls in the page shell, withNeeds triageimplemented as the strict subset of intake rows inneworreopenedstatus. - 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
CanonicalAdminTenantFilterStateto apply the active tenant as the default prefilter andCanonicalNavigationContextto carryBack to findings intakecontinuity 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
403instead 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 findingas the single inline safe shortcut on the intake row, gated byCapabilities::TENANT_FINDINGS_ASSIGN, surfaced through a lightweight preview/confirmation modal, and routed throughFindingWorkflowServiceplus the existingfinding.assignedaudit 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
Assignform 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.
- Reuse the existing
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-workwhile 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 findingsnext step satisfies the spec without hiding the updated queue truth. - Alternatives considered:
- Redirect to
My Findingsafter 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.
- Redirect to
Decision 7: Prove the feature with three focused feature suites and rely on existing UI guard tests as ambient protection
- Decision: Add
FindingsIntakeQueueTest,FindingsIntakeAuthorizationTest, andFindingsClaimHandoffTestas 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.