23 KiB
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:
Featurefor 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.phpcd 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-filamentfor the canonical report andglobal-context-shellfor 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_ator genericupdated_atinstead 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)
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)
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 Findingsonly 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/hygieneinstead of stretchingMy Findingsor intake beyond their current decision role. - Reuse the existing workspace overview signal pattern in
WorkspaceOverviewBuilderinstead of creating a new widget family. - Add one narrow
FindingAssignmentHygieneServicebecause 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_ator genericupdated_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 decisionsdata-model.md: existing entities plus the derived hygiene issue and overview-signal modelscontracts/assignment-hygiene.logical.openapi.yaml: internal logical contract for the canonical report, overview signal, and finding drilldownquickstart.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 Findingsand 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.phpwith a matching Blade view, fixed reason views, row-click drilldown only, and tenant-prefilter recovery. - Added
apps/platform/app/Services/Findings/FindingAssignmentHygieneService.phpas the single derived-query and count truth for report rows plus workspace overview signal counts. - Extended
apps/platform/app/Services/Findings/FindingWorkflowService.phpto expose shared meaningful workflow activity anchors. - Wired
/admin/findings/hygieneintoapps/platform/app/Providers/Filament/AdminPanelProvider.php,apps/platform/app/Support/Middleware/EnsureFilamentTenantSelected.php,apps/platform/app/Support/Workspaces/WorkspaceOverviewBuilder.php, andapps/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, andapps/platform/tests/Feature/Findings/FindingsAssignmentHygieneOverviewSignalTest.php.
- Added the canonical admin-plane report at
- 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.phpcd apps/platform && ./vendor/bin/sail bin pint --dirty --format agentcd 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.