TenantAtlas/specs/225-assignment-hygiene/plan.md
Ahmed Darrazi bc07e05659
Some checks failed
PR Fast Feedback / fast-feedback (pull_request) Failing after 1m27s
feat: add findings hygiene report and control catalog layering
2026-04-22 14:19:23 +02:00

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: 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)

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 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.