# Implementation Plan: Assignment Hygiene & Stale Work Detection **Branch**: `225-assignment-hygiene` | **Date**: 2026-04-22 | **Spec**: `/Users/ahmeddarrazi/Documents/projects/wt-plattform/specs/225-assignment-hygiene/spec.md` **Input**: Feature specification from `/Users/ahmeddarrazi/Documents/projects/wt-plattform/specs/225-assignment-hygiene/spec.md` **Note**: This plan keeps the work inside the existing findings workflow, canonical admin-plane findings pages, workspace overview builder, and current tenant finding detail repair actions. The intended implementation adds one new read-first findings hygiene page, one small overview signal, and one narrow derived-query service. It does not add a new table, a new workflow state, a notification channel, an automation engine, or a second repair surface. ## Summary Introduce a canonical `/admin/findings/hygiene` report plus a matching workspace overview signal that surface unhealthy findings workflow items, including broken assignments and stale in-progress work. Back both surfaces with one narrow `FindingAssignmentHygieneService` that derives `broken assignment` and `stale in progress` from existing `Finding` lifecycle fields, current tenant entitlement, user soft-delete truth, and existing finding workflow audit timestamps. Reuse the current tenant finding detail route for repair, preserve `My Findings` and intake as the healthy-work and unassigned-work surfaces, and keep the new hygiene slice strictly read-first. ## Technical Context **Language/Version**: PHP 8.4.15, Laravel 12, Filament v5, Livewire v4, Blade **Primary Dependencies**: `Finding`, `FindingResource`, `MyFindingsInbox`, `FindingsIntakeQueue`, `WorkspaceOverviewBuilder`, `EnsureFilamentTenantSelected`, `FindingWorkflowService`, `AuditLog`, `TenantMembership`, Filament page and table primitives **Storage**: PostgreSQL via existing `findings`, `audit_logs`, `tenant_memberships`, and `users`; no schema changes planned **Testing**: Pest v4 feature tests with Filament/Livewire 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 report and overview queries scoped to visible tenants, avoid N+1 owner or assignee resolution, and keep summary counts cheap enough for the existing `/admin` overview render path **Constraints**: No new persistence, no hidden-tenant leakage, no automatic reassignment, no new findings lifecycle state, no new assets, and no inline repair mutation on the hygiene report **Scale/Scope**: One new canonical admin-plane report, one new overview signal, one narrow findings service, one Blade view, one middleware route allow-list update, and three focused feature suites ## UI / Surface Guardrail Plan - **Guardrail scope**: changed surfaces - **Native vs custom classification summary**: native Filament page, table, empty-state, and workspace summary primitives only - **Shared-family relevance**: findings workflow family (`My Findings`, intake, finding detail) and workspace overview signal 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**: standard-native-filament, global-context-shell - **Required tests or manual smoke**: functional-core, state-contract - **Exception path and spread control**: none; the feature adds one bounded findings page and one overview signal rather than a new shared UI family - **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 | Hygiene remains derived from live finding workflow truth, not from snapshots or exports | | Read/write separation | PASS | PASS | The report and overview signal are read-only; repair stays on the existing finding detail actions and audit path | | Graph contract path | PASS | PASS | No Microsoft Graph call path or contract-registry change is introduced | | Deterministic capabilities / RBAC-UX | PASS | PASS | Admin-plane report rows and counts remain tenant-safe, non-members stay `404`, and current finding-view capability stays authoritative for disclosure | | Workspace / tenant isolation | PASS | PASS | `/admin/findings/hygiene` is tenantless by URL but every row, count, and filter remains tenant-entitlement checked | | Run observability / Ops-UX | PASS | PASS | No new `OperationRun`, queued workflow, or notification family is introduced | | Proportionality / no premature abstraction | PASS | PASS | One narrow service is justified because both the report and overview signal need the same derived issue truth and counting rules | | Persisted truth / few layers | PASS | PASS | No table, materialized hygiene state, or UI-only lifecycle is introduced | | Behavioral state discipline | PASS | PASS | `broken assignment` and `stale in progress` remain derived hygiene reasons, not new persisted finding statuses | | Filament-native UI (UI-FIL-001) | PASS | PASS | The feature uses a native Filament page, native table filters, native empty-state affordances, and existing workspace-summary primitives | | Decision-first / action-surface contract | PASS | PASS | The report is the one primary repair surface, the overview signal is a secondary drill-in, and repair still happens on the existing finding detail | | Test governance (TEST-GOV-001) | PASS | PASS | Proof stays in focused feature suites for hygiene classification, visibility, overview counts, and drilldown safety | | Filament v5 / Livewire v4 compliance | PASS | PASS | The feature adds a Filament v5 page and stays inside Livewire v4-compatible patterns | | Provider registration / global search / assets | PASS | PASS | Panel providers remain in `apps/platform/bootstrap/providers.php`; no new globally searchable resource or asset family is added | ## Test Governance Check - **Test purpose / classification by changed surface**: `Feature` for the hygiene report, derived classification, overview signal consistency, and tenant-safe disclosure behavior - **Affected validation lanes**: `fast-feedback`, `confidence` - **Why this lane mix is the narrowest sufficient proof**: The primary risks are integrated operator outcomes: which findings appear, why they appear, whether hidden tenants leak into counts, whether the overview matches the canonical report, and whether row drilldown preserves the existing authorization contract. Focused feature tests prove that without adding browser or heavy-governance breadth. - **Narrowest proving command(s)**: - `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Findings/FindingsAssignmentHygieneReportTest.php` - `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Findings/FindingsAssignmentHygieneClassificationTest.php tests/Feature/Findings/FindingsAssignmentHygieneOverviewSignalTest.php` - **Fixture / helper / factory / seed / context cost risks**: Moderate. Tests need mixed visible versus hidden tenant memberships, soft-deleted or entitlement-lost assignees, in-progress timestamps, existing finding workflow audit rows, and active tenant prefilter state. - **Expensive defaults or shared helper growth introduced?**: no; any hygiene-specific fixtures should stay local to the new tests and reuse existing findings workflow helpers - **Heavy-family additions, promotions, or visibility changes**: none - **Surface-class relief / special coverage rule**: `standard-native-filament` for the canonical report and `global-context-shell` for the workspace overview signal plus tenantless route safety - **Closing validation and reviewer handoff**: Reviewers should rely on the exact commands above and verify that healthy assigned work stays out of the hygiene report, ordinary intake backlog stays out, one finding with two reasons counts once, the active tenant prefilter can be cleared back to all visible tenants, and the overview signal uses the same unique issue truth as the report. - **Budget / baseline / trend follow-up**: none - **Review-stop questions**: Did the implementation introduce a persisted hygiene flag or table? Did stale detection rely on observation fields such as `last_seen_at` or generic `updated_at` instead of workflow activity? Did any route or count leak hidden-tenant findings? Did the report gain inline repair actions or other mutation drift? - **Escalation path**: document-in-feature unless a new persistence model, automation engine, or broad availability 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**: This feature remains bounded to one report, one signal, and one narrow derived-query seam over current findings workflow truth ## Project Structure ### Documentation (this feature) ```text specs/225-assignment-hygiene/ ├── plan.md ├── research.md ├── data-model.md ├── quickstart.md ├── contracts/ │ └── assignment-hygiene.logical.openapi.yaml ├── checklists/ │ └── requirements.md └── tasks.md ``` ### Source Code (repository root) ```text apps/platform/ ├── app/ │ ├── Filament/ │ │ └── Pages/ │ │ └── Findings/ │ │ ├── FindingsHygieneReport.php │ │ ├── FindingsIntakeQueue.php │ │ └── MyFindingsInbox.php │ ├── Models/ │ │ ├── AuditLog.php │ │ ├── Finding.php │ │ └── User.php │ ├── Services/ │ │ └── Findings/ │ │ ├── FindingAssignmentHygieneService.php │ │ └── FindingWorkflowService.php │ ├── Support/ │ │ ├── Middleware/ │ │ │ └── EnsureFilamentTenantSelected.php │ │ └── Workspaces/ │ │ └── WorkspaceOverviewBuilder.php ├── resources/ │ └── views/ │ └── filament/ │ └── pages/ │ └── findings/ │ └── findings-hygiene-report.blade.php └── tests/ └── Feature/ └── Findings/ ├── FindingsAssignmentHygieneClassificationTest.php ├── FindingsAssignmentHygieneOverviewSignalTest.php └── FindingsAssignmentHygieneReportTest.php ``` **Structure Decision**: Standard Laravel monolith. The feature stays inside the existing findings page family, workspace overview builder, and findings workflow service seams. No new base directory, panel, or persisted model is required. ## Complexity Tracking | Violation | Why Needed | Simpler Alternative Rejected Because | |-----------|------------|-------------------------------------| | none | — | — | ## Proportionality Review - **Current operator problem**: Assigned findings can silently become unworkable or stale after assignment, and no existing cross-tenant surface exposes those failures in one repair context. - **Existing structure is insufficient because**: `My Findings` only answers healthy assigned work for the current user, intake only answers unassigned backlog, and the overview today has no workflow-hygiene signal. Duplicating derived issue logic in both a new page and the overview would drift quickly. - **Narrowest correct implementation**: Add one new report page and one summary signal backed by one narrow findings hygiene service that centralizes classification and count rules while keeping repair on the existing finding detail page. - **Ownership cost created**: One small service, one page, one Blade view, one overview builder extension, one middleware allow-list update, and three focused feature suites. - **Alternative intentionally rejected**: A generic workflow health engine or a persisted hygiene table. Both add more machinery than current release truth needs because only the findings workflow consumes this derived issue model today. - **Release truth**: Current-release truth. The feature repairs an active workflow trust gap now rather than preparing a later autonomous work-routing platform. ## Phase 0 Research Research outcomes are captured in `/Users/ahmeddarrazi/Documents/projects/wt-plattform/specs/225-assignment-hygiene/research.md`. Key decisions: - Add a dedicated canonical report at `/admin/findings/hygiene` instead of stretching `My Findings` or intake beyond their current decision role. - Reuse the existing workspace overview signal pattern in `WorkspaceOverviewBuilder` instead of creating a new widget family. - Add one narrow `FindingAssignmentHygieneService` because both the report and overview signal need the same classification and counting truth. - Derive broken assignment from current tenant entitlement plus existing user soft-delete truth rather than introducing a new availability model. - Derive stale work from finding workflow timestamps plus existing finding workflow audit events, not from observation fields such as `last_seen_at` or generic `updated_at`. - Keep repair on the existing tenant finding detail route and current assignment actions; the hygiene report stays read-first. ## Phase 1 Design Design artifacts are created under `/Users/ahmeddarrazi/Documents/projects/wt-plattform/specs/225-assignment-hygiene/`: - `research.md`: report-shape, classification-source, and reuse decisions - `data-model.md`: existing entities plus the derived hygiene issue and overview-signal models - `contracts/assignment-hygiene.logical.openapi.yaml`: internal logical contract for the canonical report, overview signal, and finding drilldown - `quickstart.md`: focused validation workflow for implementation and review Design decisions: - No schema migration is required; hygiene remains derived from current `Finding`, `AuditLog`, user, and membership truth. - The canonical new seam is one narrow `FindingAssignmentHygieneService`, not a broad workflow-health framework. - Stale detection uses workflow activity anchors, not observation freshness fields. - The canonical repair surface remains the existing tenant finding detail route. - Existing `My Findings` and intake inclusion rules remain unchanged. ## Phase 1 Agent Context Update Run: - `.specify/scripts/bash/update-agent-context.sh copilot` ## Constitution Check — Post-Design Re-evaluation - PASS — the design stays inside current findings, audit, workspace overview, and Filament page seams with no new persistence, no Graph work, 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`, no globally searchable resource behavior changes, and no destructive action path is added on the new report. ## Implementation Strategy ### Phase A — Add the canonical hygiene report shell and route treatment **Goal**: Create the new admin-plane report without disturbing the current findings page family. | Step | File | Change | |------|------|--------| | A.1 | `apps/platform/app/Filament/Pages/Findings/FindingsHygieneReport.php` | Add the new Filament page with slug `findings/hygiene`, canonical row drilldown into `FindingResource`, fixed reason filters, active-tenant prefilter behavior, and read-first table semantics | | A.2 | `apps/platform/resources/views/filament/pages/findings/findings-hygiene-report.blade.php` | Add the page shell using the same findings page family layout patterns as `MyFindingsInbox` and `FindingsIntakeQueue` | | A.3 | `apps/platform/app/Support/Middleware/EnsureFilamentTenantSelected.php` | Extend the tenant-selection bypass and workspace-scoped navigation handling so `/admin/findings/hygiene` behaves like the existing tenantless canonical findings pages | ### Phase B — Add one narrow derived hygiene service over existing findings truth **Goal**: Centralize hygiene classification and count truth for both the report and the overview signal. | Step | File | Change | |------|------|--------| | B.1 | `apps/platform/app/Services/Findings/FindingAssignmentHygieneService.php` | Add a focused service that scopes findings to visible tenants, derives hygiene reasons, returns the canonical report query, and computes unique summary counts | | B.2 | `apps/platform/app/Models/Finding.php` | Reuse existing open-status and responsibility fields; add only narrow query helpers if they materially simplify the hygiene service without introducing a new semantic layer | | B.3 | `apps/platform/app/Models/AuditLog.php` or existing audit query seam | Reuse existing finding workflow audit rows as one stale-activity input rather than persisting a new timestamp | ### Phase C — Implement broken-assignment and stale-work classification on real existing truth **Goal**: Make the derived reasons honest without inventing new persistence or generic availability models. | Step | File | Change | |------|------|--------| | C.1 | `apps/platform/app/Services/Findings/FindingAssignmentHygieneService.php` | Classify `broken assignment` for non-terminal findings whose assignee no longer has current tenant entitlement or whose user record is soft-deleted | | C.2 | `apps/platform/app/Services/Findings/FindingAssignmentHygieneService.php` | Classify `stale in progress` for non-terminal findings in `in_progress` whose last meaningful workflow activity is older than seven days | | C.3 | `apps/platform/app/Services/Findings/FindingWorkflowService.php` and existing audit action ids | Reuse `finding.assigned`, `finding.in_progress`, and `finding.reopened` audit history plus `in_progress_at` and `reopened_at` as activity anchors, explicitly avoiding `last_seen_at` or generic `updated_at` as the stale-work source | ### Phase D — Reuse the workspace overview summary pattern without adding a second work surface **Goal**: Make hygiene issues discoverable from `/admin` while keeping the report canonical. | Step | File | Change | |------|------|--------| | D.1 | `apps/platform/app/Support/Workspaces/WorkspaceOverviewBuilder.php` | Add one findings hygiene summary signal with unique issue counts, calm-state copy, and a CTA into `/admin/findings/hygiene` | | D.2 | `apps/platform/app/Support/Workspaces/WorkspaceOverviewBuilder.php` | Reuse the same visible-tenant scope rules and counting truth as the hygiene service so the overview never drifts from the canonical report | ### Phase E — Preserve report filtering, empty-state truth, and existing repair drilldown **Goal**: Keep the new report decision-first and tenant-safe. | Step | File | Change | |------|------|--------| | E.1 | `apps/platform/app/Filament/Pages/Findings/FindingsHygieneReport.php` | Implement fixed filters for `All issues`, `Broken assignment`, and `Stale in progress` without exposing a free-form workflow taxonomy | | E.2 | `apps/platform/app/Filament/Pages/Findings/FindingsHygieneReport.php` and Blade view | Add the filtered-empty-state explanation and one `Clear tenant filter` CTA when tenant prefiltering hides otherwise visible hygiene issues | | E.3 | `apps/platform/app/Filament/Pages/Findings/FindingsHygieneReport.php` | Keep row click as the sole inspect affordance and point it to the existing tenant finding detail repair flow | ### Phase F — Lock down classification, visibility, and overview consistency with focused regression coverage **Goal**: Prove the report stays honest, tenant-safe, and aligned with the overview signal. | Step | File | Change | |------|------|--------| | F.1 | `apps/platform/tests/Feature/Findings/FindingsAssignmentHygieneReportTest.php` | Cover report visibility, tenant-prefilter behavior, hidden-tenant suppression, and row drilldown behavior | | F.2 | `apps/platform/tests/Feature/Findings/FindingsAssignmentHygieneClassificationTest.php` | Cover broken-assignment classification, stale-work classification, exclusion of healthy assigned and ordinary intake backlog, and one-row multi-reason behavior | | F.3 | `apps/platform/tests/Feature/Findings/FindingsAssignmentHygieneOverviewSignalTest.php` | Cover overview signal calm versus attention states, unique count alignment with the canonical report, and CTA routing | | F.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 | ## Implementation Close-out - **Status**: Implemented and verified on 2026-04-22. - **Implemented scope**: - Added the canonical admin-plane report at `apps/platform/app/Filament/Pages/Findings/FindingsHygieneReport.php` with a matching Blade view, fixed reason views, row-click drilldown only, and tenant-prefilter recovery. - Added `apps/platform/app/Services/Findings/FindingAssignmentHygieneService.php` as the single derived-query and count truth for report rows plus workspace overview signal counts. - Extended `apps/platform/app/Services/Findings/FindingWorkflowService.php` to expose shared meaningful workflow activity anchors. - Wired `/admin/findings/hygiene` into `apps/platform/app/Providers/Filament/AdminPanelProvider.php`, `apps/platform/app/Support/Middleware/EnsureFilamentTenantSelected.php`, `apps/platform/app/Support/Workspaces/WorkspaceOverviewBuilder.php`, and `apps/platform/resources/views/filament/pages/workspace-overview.blade.php`. - Added focused Pest coverage in `apps/platform/tests/Feature/Findings/FindingsAssignmentHygieneClassificationTest.php`, `apps/platform/tests/Feature/Findings/FindingsAssignmentHygieneReportTest.php`, and `apps/platform/tests/Feature/Findings/FindingsAssignmentHygieneOverviewSignalTest.php`. - **Proof commands run**: - `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Findings/FindingsAssignmentHygieneClassificationTest.php tests/Feature/Findings/FindingsAssignmentHygieneReportTest.php tests/Feature/Findings/FindingsAssignmentHygieneOverviewSignalTest.php` - `cd apps/platform && ./vendor/bin/sail bin pint --dirty --format agent` - `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Findings/FindingsAssignmentHygieneClassificationTest.php tests/Feature/Findings/FindingsAssignmentHygieneReportTest.php tests/Feature/Findings/FindingsAssignmentHygieneOverviewSignalTest.php` - **Guardrail outcome**: - Livewire v4.0+ / Filament v5 compliance remains intact. - Provider registration location remains unchanged in `bootstrap/providers.php`. - No global-search resource behavior changed. - No destructive action was added on the new report. - No new asset family was introduced, so existing deployment behavior stays sufficient.