12 KiB
Implementation Plan: Findings Workflow Enforcement and Audit Backstop
Branch: 151-findings-workflow-backstop | Date: 2026-03-18 | Spec: specs/151-findings-workflow-backstop/spec.md
Input: Feature specification from specs/151-findings-workflow-backstop/spec.md
Summary
Harden findings lifecycle truth by routing all meaningful finding mutations through one service-owned workflow gateway, eliminating known bypass paths, and guaranteeing durable audit history for both human-driven and system-driven lifecycle changes.
The implementation keeps Spec 111 as the source of lifecycle semantics and Spec 134 as the source of audit semantics. The design centers on one canonical mutation gateway for findings workflow changes, removes or neutralizes direct model mutation shortcuts, adopts the same gateway for automated reopen and auto-resolve flows, normalizes finding audit action taxonomy, and adds regression guards that fail when covered code paths bypass workflow enforcement or audit coverage.
Technical Context
Language/Version: PHP 8.4.15
Primary Dependencies: Laravel 12, Filament v5, Livewire v4, Laravel Sail, Pest v4, PHPUnit v12
Storage: PostgreSQL with existing findings and audit_logs tables; no new storage engine or external log store
Testing: Pest feature and Livewire tests run through vendor/bin/sail artisan test --compact; focused browser tests are optional for this slice and not the primary verification path
Target Platform: Laravel web application with tenant-context Filament resources and canonical workspace-scoped audit review
Project Type: Web application
Performance Goals: Single-record findings workflow mutations remain DB-only and transactional; list and view renders remain DB-only; bulk workflow actions continue to scale to 100+ findings while writing per-record audit history deterministically
Constraints: No silent lifecycle mutation without server-side authorization and audit history; non-members remain 404; in-scope members missing capability remain 403; existing finding-status badges remain centralized; no new Graph calls; no ad hoc audit strings or raw capability strings
Scale/Scope: Cross-cutting findings hardening across Finding, FindingWorkflowService, automated generators and auto-close services, Findings Filament surfaces, audit taxonomy, and focused regression suites
Filament v5 Implementation Notes
- Livewire v4.0+ compliance: Maintained. Findings list and view actions remain Livewire-backed Filament actions and stay within the Filament v5 + Livewire v4 contract.
- Provider registration location: No new panel is introduced. Existing panel providers remain registered in
bootstrap/providers.php. - Global search rule: This feature does not add a new globally searchable resource or alter global-search exposure rules.
- Destructive actions:
Resolve,Close,Risk accept, andReopenremain explicit, capability-gated, and confirmation-protected; this plan keeps them on existing action surfaces rather than inventing hidden mutation paths. - Asset strategy: No new Filament assets are planned. Existing deployment practice still includes
php artisan filament:assets, but this feature adds no asset registrations. - Testing plan: Add focused Pest feature and Livewire coverage for allowed transitions, invalid transitions, service-path bypass prevention, audit backstop behavior, automation-driven lifecycle paths, and tenant-scope authorization semantics.
Constitution Check
GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.
- Inventory-first: PASS. Findings remain tenant-owned observed-state artifacts; the feature hardens lifecycle truth and does not redefine inventory versus snapshot ownership.
- Read/write separation: PASS. This is a write hardening feature, so every covered mutation stays explicit, server-authorized, auditable, and test-covered.
- Graph contract path: PASS. No new Graph calls or contract-registry additions are introduced.
- Deterministic capabilities: PASS. Existing findings capability constants remain canonical; no raw capability strings are introduced in feature code.
- RBAC-UX plane separation: PASS. Findings mutations stay in the tenant/admin plane, while audit review stays on canonical
/adminmonitoring surfaces with tenant-safe filtering. - Workspace isolation: PASS. Workspace context remains required for findings and audit review, and historical finding audit detail remains workspace-scoped.
- Tenant isolation: PASS. Findings remain tenant-owned and every list, view, action, and audit drill-down stays tenant-safe.
- Destructive confirmation standard: PASS WITH WORK. Existing destructive-like findings actions remain confirmed; any newly introduced system-safe operator affordance must preserve the same rule.
- Global search tenant safety: PASS. No global-search change is in scope.
- Run observability: PASS. The feature is DB-only for lifecycle enforcement and audit history; no new
OperationRunusage is introduced. - Data minimization: PASS WITH WORK. Audit metadata must continue to exclude
evidence_jsonb, secrets, and oversized payloads while preserving meaningful before/after lifecycle history. - BADGE-001: PASS. No new finding status values are introduced; existing centralized finding badge semantics remain authoritative.
- UI-NAMING-001: PASS WITH WORK. Audit prose and workflow affordances keep the existing
Triage,Start progress,Assign,Resolve,Close,Risk accept, andReopenvocabulary. - Filament Action Surface Contract: PASS. Existing Findings list and view surfaces already define inspection and grouped workflow actions; the implementation must preserve this while changing only the enforcement path behind them.
- Filament UX-001: PASS. No new create or edit screens are introduced; existing Findings list and detail surfaces remain within the current layout model.
Project Structure
Documentation (this feature)
specs/151-findings-workflow-backstop/
├── plan.md
├── research.md
├── data-model.md
├── quickstart.md
├── contracts/
│ ├── findings-workflow.openapi.yaml
│ └── finding-audit-event.schema.yaml
└── tasks.md
Source Code (repository root)
app/
├── Filament/
│ └── Resources/
│ └── FindingResource.php
├── Models/
│ ├── Finding.php
│ └── AuditLog.php
├── Policies/
│ └── FindingPolicy.php
├── Services/
│ ├── Audit/
│ ├── Baselines/
│ ├── EntraAdminRoles/
│ ├── Findings/
│ └── PermissionPosture/
└── Support/
├── Audit/
├── Auth/
└── Badges/
database/
├── factories/
└── migrations/
tests/
├── Feature/
│ ├── Audit/
│ ├── Findings/
│ └── WorkspaceIsolation/
└── Unit/
Structure Decision: Keep the current Laravel and Filament structure. The implementation concentrates findings lifecycle enforcement in the existing findings service layer and audit support layer, then updates automated generator services and existing Filament action surfaces to call the same gateway instead of adding a parallel workflow stack.
Complexity Tracking
| Violation | Why Needed | Simpler Alternative Rejected Because |
|---|---|---|
| N/A | N/A | N/A |
Phase 0 — Research (complete)
- Output: specs/151-findings-workflow-backstop/research.md
- Confirmed that the current findings workflow has multiple real bypass paths: public mutation methods on
Finding, directforceFill()lifecycle writes in automated services, and no explicit backstop beyondFindingWorkflowService. - Selected a service-owned gateway strategy with regression guards instead of observer-heavy implicit enforcement.
- Confirmed that existing audit history for findings is currently service-path only and uses free-form
finding.*action IDs with structured metadata snapshots.
Phase 1 — Design & Contracts (complete)
- Output: specs/151-findings-workflow-backstop/data-model.md
- Output: specs/151-findings-workflow-backstop/quickstart.md
- Output: specs/151-findings-workflow-backstop/contracts/findings-workflow.openapi.yaml
- Output: specs/151-findings-workflow-backstop/contracts/finding-audit-event.schema.yaml
Post-design Constitution Re-check
- PASS: Findings remain tenant-owned and audit review remains workspace-scoped with tenant-safe filtering.
- PASS: No new Graph or
OperationRuncontract is introduced; the feature stays DB-only and audit-focused. - PASS WITH WORK: All human-driven and system-driven lifecycle mutations must converge on one service-owned workflow gateway.
- PASS WITH WORK: Existing destructive-like workflow actions must continue to use explicit confirmation and capability gating on Findings surfaces.
- PASS WITH WORK: Regression coverage must actively block direct status mutation and missing-audit regressions rather than relying on coding convention.
Phase 2 — Implementation Planning
tasks.md should cover:
- Consolidating findings lifecycle mutation entry points behind one canonical gateway.
- Retiring or neutralizing direct
Findinglifecycle mutators and other silent bypass paths. - Adopting the same gateway for auto-resolve and recurrence flows.
- Normalizing finding audit action taxonomy and audit metadata expectations.
- Preserving existing Filament action surfaces while rewiring list, bulk, and view actions to the unified gateway.
- Adding focused regression coverage for invalid transitions, bypass attempts, duplicate-audit prevention, automated lifecycle flows, and tenant-safe audit visibility.
Contract Implementation Note
- The OpenAPI contract is semantic rather than transport-prescriptive. It documents the canonical server-side workflow operations the UI and automation must honor, even if existing implementation continues to use Filament and Livewire action handlers instead of standalone REST controllers.
- The audit-event schema captures the durable history shape expected from every successful covered workflow mutation and can be used as the validation target for tests.
- No automation-path exemption is planned in this feature. All covered automated reopen and auto-resolve flows must converge on the same gateway as interactive actions.
Deployment Sequencing Note
- Schema changes should be avoided unless the chosen implementation requires a small additive support field; the preferred design is code-path hardening on top of existing findings and audit tables.
- Existing legacy
acknowledgeddata remains readable during rollout; any change to write semantics must preserve backward-compatible interpretation during the transition. - Focused findings and audit tests should pass before expanding the gateway to additional automation services.
Final Implementation Status
- Completed service-owned workflow hardening for human-driven mutations, including owner reassignment and reason-bearing transitions.
- Removed direct
Findinglifecycle mutators and moved covered automation paths onto the same canonical workflow gateway. - Added system-origin resolve and reopen support so baseline auto-close, permission posture, Entra admin roles, and baseline recurrence share one audit contract.
- Preserved the existing Filament action surface while tightening visibility and deny semantics for single-record list and view actions.
- Enforced tenant and workspace scope consistency in both policy and resource-layer checks.
Final Validation Snapshot
vendor/bin/sail bin pint --dirty --format agent- Focused human workflow and audit slice: 28 tests passed, 229 assertions.
- Focused automation and recurrence slice: 45 tests passed, 230 assertions.
- Focused UI and RBAC single-record slice: 14 tests passed, 151 assertions.
Rollout Conclusion
- No schema migration was required.
- No new Filament assets or panel registrations were introduced.
- No unapproved automation-path exemptions remain in the implemented slice.
- Legacy
acknowledgedstate remains readable for compatibility, while new workflow truth converges on the canonical statuses and gateway.