# Implementation Plan: Finding Outcome Taxonomy & Verification Semantics **Branch**: `231-finding-outcome-taxonomy` | **Date**: 2026-04-22 | **Spec**: `/Users/ahmeddarrazi/Documents/projects/wt-plattform/specs/231-finding-outcome-taxonomy/spec.md` **Input**: Feature specification from `/Users/ahmeddarrazi/Documents/projects/wt-plattform/specs/231-finding-outcome-taxonomy/spec.md` **Note**: This plan keeps the work inside the existing findings workflow, tenant findings resource, current risk-governance interpretation, shared findings action catalog copy, pre-production lifecycle normalization jobs, system resolve/reopen paths, and current review/report consumers. The intended implementation adds one bounded outcome-semantics seam over existing `Finding` records and existing transition services. It does not add a new findings queue, a new panel, a new asset family, a second workflow state store, or a new primary findings status. ## Summary Introduce one bounded findings outcome taxonomy that sits on top of the existing lifecycle in `FindingWorkflowService` and current `Finding` records. Keep `status` unchanged, tighten `resolved_reason`, `closed_reason`, and `reopen_reason` into canonical keys, distinguish operator-declared resolution from later trusted system-cleared outcomes, and apply that same contract to `FindingResource`, the existing list and detail narratives, system resolve/reopen producers such as `BaselineAutoCloseService`, `EntraAdminRolesFindingGenerator`, `PermissionPostureFindingGenerator`, and `CompareBaselineToTenantJob`, supporting pre-production normalization jobs, shared findings action copy in `GovernanceActionCatalog`, the risk-governance interpreter in `FindingRiskGovernanceResolver`, plus current findings-derived review/report consumers such as `ReviewRegister` and `TenantReviewResource`. ## Technical Context **Language/Version**: PHP 8.4.15, Laravel 12, Filament v5, Livewire v4, Blade **Primary Dependencies**: `App\Models\Finding`, `App\Filament\Resources\FindingResource`, `App\Services\Findings\FindingWorkflowService`, `App\Services\Findings\FindingRiskGovernanceResolver`, `App\Services\Baselines\BaselineAutoCloseService`, `App\Services\EntraAdminRoles\EntraAdminRolesFindingGenerator`, `App\Services\PermissionPosture\PermissionPostureFindingGenerator`, `App\Jobs\CompareBaselineToTenantJob`, `App\Jobs\BackfillFindingLifecycleJob`, `App\Jobs\BackfillFindingLifecycleTenantIntoWorkspaceRunJob`, `App\Filament\Pages\Reviews\ReviewRegister`, `App\Filament\Resources\TenantReviewResource`, `App\Support\Ui\GovernanceActions\GovernanceActionCatalog`, `BadgeCatalog`, `BadgeRenderer`, `AuditLog` metadata via `AuditLogger` **Storage**: PostgreSQL via existing `findings`, `finding_exceptions`, `tenant_reviews`, `stored_reports`, and audit-log tables; no schema changes planned **Testing**: Pest v4 feature tests with Filament/Livewire assertions and workflow-service coverage **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 findings list and review/report rollups free of new N+1 behavior, keep terminal-outcome rendering derived from existing record state, and avoid new background or polling work **Constraints**: No new primary findings status, no new queue surface, no new Graph calls, no new asset family, no cross-tenant leakage, no comments or attachments scope, and no second reporting-only persistence layer **Scale/Scope**: One narrow semantics helper or equivalent local mapping seam, one workflow-service hardening slice, one risk-governance alignment slice, four existing system producer paths, two pre-production normalization jobs, one shared action-catalog touchpoint, two existing operator surfaces, and a small set of focused test suites ## UI / Surface Guardrail Plan - **Guardrail scope**: changed surfaces - **Native vs custom classification summary**: native Filament resource list, native record detail, and existing review/report surfaces only - **Shared-family relevance**: findings workflow surfaces, status messaging, filters, review/report viewers, centralized badge semantics - **State layers in scope**: page, detail, shell - **Handling modes by drift class or surface**: review-mandatory - **Repository-signal treatment**: review-mandatory - **Special surface test profiles**: standard-native-filament - **Required tests or manual smoke**: functional-core, state-contract - **Exception path and spread control**: none; the feature must converge all in-scope outcome language on one bounded semantics seam rather than permitting page-local synonyms - **Active feature PR close-out entry**: Guardrail ## Shared Pattern & System Fit - **Cross-cutting feature marker**: yes - **Systems touched**: - `App\Filament\Resources\FindingResource` - `App\Services\Findings\FindingWorkflowService` - `App\Services\Findings\FindingRiskGovernanceResolver` - `App\Models\Finding` - `App\Services\Baselines\BaselineAutoCloseService` - `App\Services\EntraAdminRoles\EntraAdminRolesFindingGenerator` - `App\Services\PermissionPosture\PermissionPostureFindingGenerator` - `App\Jobs\CompareBaselineToTenantJob` - `App\Jobs\BackfillFindingLifecycleJob` - `App\Jobs\BackfillFindingLifecycleTenantIntoWorkspaceRunJob` - `App\Filament\Pages\Reviews\ReviewRegister` - `App\Filament\Resources\TenantReviewResource` - `App\Support\Ui\GovernanceActions\GovernanceActionCatalog` - centralized badge and audit metadata paths - **Shared abstractions reused**: existing `FindingWorkflowService`, existing `Finding` model lifecycle constants, existing `BadgeCatalog` and `BadgeRenderer`, existing `GovernanceActionCatalog` copy contracts, existing review/register consumers, and current audit metadata structure in `FindingWorkflowService` - **New abstraction introduced? why?**: one narrow `FindingOutcomeSemantics`-style helper or equivalent local seam is justified because the same terminal-outcome meaning must be reused by workflow mutations, list/detail narratives, filters, and review/report rollups - **Why the existing abstraction was sufficient or insufficient**: the existing findings lifecycle is sufficient as the primary workflow status layer, but it is insufficient for terminal-outcome meaning because free-form reasons and page-local narratives cannot reliably separate operator-resolved, system-verified, and non-remediation closure outcomes - **Bounded deviation / spread control**: no parallel presentation path is allowed; if a review/report consumer needs compressed wording, it must still derive from the same canonical keys and verified-clear rules as the operator surfaces ## Constitution Check *GATE: Passed before implementation design. Re-check after any scope expansion.* | Principle | Status | Notes | |-----------|--------|-------| | Read/write separation | PASS | The feature only tightens meaning on existing finding transitions and existing read models; no new write family is introduced | | Single contract path / no Graph bypass | PASS | No Graph calls or contract-registry changes are involved | | Deterministic capabilities / RBAC-UX | PASS | Existing tenant findings permissions remain authoritative; no new capability family or plane change is introduced | | Workspace and tenant isolation | PASS | All touched operator surfaces remain tenant-entitlement scoped; review/report rollups must continue to hide unauthorized tenant data | | No new persisted truth without need | PASS | Existing `resolved_reason`, `closed_reason`, audit metadata, and exception validity semantics are reused; no new table or artifact is planned | | No new state without behavioral consequence | PASS | The plan keeps the primary lifecycle statuses unchanged and limits new semantics to terminal-outcome meaning that changes filters, audit reading, and reporting buckets | | No premature abstraction / few layers | PASS | One narrow helper is the maximum justified addition because at least four producer paths and multiple readers need the same semantics | | Shared pattern first | PASS | One shared outcome semantics seam will feed workflow actions, UI narratives, filters, and reporting rather than separate local mappings | | Badge semantics (BADGE-001) | PASS | Existing centralized badge/rendering rules remain authoritative; no page-local color language is planned | | Filament-native UI (UI-FIL-001) | PASS | Findings list/detail and review surfaces stay on existing Filament resources/pages with updated filters, modals, and narratives only | | Livewire v4.0+ / Filament v5 compliance | PASS | The plan stays within existing Filament v5 and Livewire v4 surface patterns | | Provider registration / global search / assets | PASS | Panel providers remain in `apps/platform/bootstrap/providers.php`; no new globally searchable resource, no new asset family, and no deploy-step change beyond the existing `cd apps/platform && php artisan filament:assets` policy | | Test governance (TEST-GOV-001) | PASS | Proof stays in focused feature suites; no browser or heavy-governance expansion is planned | ## Test Governance Check - **Test purpose / classification by changed surface**: `Feature` for workflow-service semantics, system resolve/reopen paths, list/detail terminal-outcome presentation, filters, and findings-derived review/report buckets - **Affected validation lanes**: `fast-feedback`, `confidence` - **Why this lane mix is the narrowest sufficient proof**: The risk is integrated business meaning across existing findings mutations and existing operator or report consumers. Focused feature tests prove that meaning without adding browser rendering or heavy-governance breadth. - **Narrowest proving command(s)**: - `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Findings/FindingWorkflowServiceTest.php tests/Feature/Findings/FindingRecurrenceTest.php` - `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Findings/FindingsListFiltersTest.php tests/Feature/Filament/FindingResolvedReferencePresentationTest.php tests/Feature/Findings/FindingOutcomeSummaryReportingTest.php tests/Feature/Findings/FindingRiskGovernanceProjectionTest.php` - **Fixture / helper / factory / seed / context cost risks**: Moderate. Tests need open, resolved, closed, reopened, and risk-accepted findings; system actor paths that clear or reopen findings; valid and invalid exception coverage; and at least one findings-derived review/report scenario. - **Expensive defaults or shared helper growth introduced?**: no; any new helpers should stay findings-local and reuse existing findings workflow test concerns - **Heavy-family additions, promotions, or visibility changes**: none - **Surface-class relief / special coverage rule**: `standard-native-filament`; keep coverage centered on existing resource and service seams instead of adding browser tests - **Closing validation and reviewer handoff**: Reviewers should verify that no new primary findings status was added, that canonical keys replaced free-form reporting meaning, that `risk_accepted` stayed governed by exception validity, that system-cleared findings are distinct from operator-resolved findings, and that the same semantics appear in workflow service, list filters, detail narrative, and report buckets. - **Budget / baseline / trend follow-up**: none - **Review-stop questions**: Did the implementation add a new status instead of a derived outcome layer? Did any producer path keep a free-form-only reason as the primary meaning? Did review/report code invent a second bucket taxonomy? Did risk-accepted findings collapse into verified-clear logic? - **Escalation path**: document-in-feature unless implementation pressure requires a schema change or a second cross-domain presenter, 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**: The current operator and reporting gap can be closed inside the existing findings workflow and report seams without broader framework work ## Project Structure ### Documentation (this feature) ```text specs/231-finding-outcome-taxonomy/ ├── plan.md ├── research.md ├── data-model.md ├── quickstart.md ├── contracts/ │ └── finding-outcome-taxonomy.logical.openapi.yaml ├── checklists/ │ └── requirements.md └── tasks.md ``` ### Source Code (repository root) ```text apps/platform/ ├── app/ │ ├── Filament/ │ │ ├── Pages/ │ │ │ └── Reviews/ │ │ │ └── ReviewRegister.php │ │ └── Resources/ │ │ ├── FindingResource.php │ │ └── TenantReviewResource.php │ ├── Jobs/ │ │ └── CompareBaselineToTenantJob.php │ ├── Models/ │ │ ├── Finding.php │ │ └── FindingException.php │ ├── Services/ │ │ ├── Baselines/ │ │ │ └── BaselineAutoCloseService.php │ │ ├── EntraAdminRoles/ │ │ │ └── EntraAdminRolesFindingGenerator.php │ │ ├── Findings/ │ │ │ └── FindingWorkflowService.php │ │ └── PermissionPosture/ │ │ └── PermissionPostureFindingGenerator.php │ └── Support/ │ ├── Badges/ │ │ ├── BadgeCatalog.php │ │ ├── BadgeDomain.php │ │ └── BadgeRenderer.php │ └── Findings/ │ └── FindingOutcomeSemantics.php └── tests/ └── Feature/ ├── Filament/ │ └── FindingResolvedReferencePresentationTest.php └── Findings/ ├── FindingWorkflowServiceTest.php ├── FindingRecurrenceTest.php ├── FindingRiskGovernanceProjectionTest.php ├── FindingOutcomeSummaryReportingTest.php └── FindingsListFiltersTest.php ``` **Structure Decision**: Standard Laravel monolith. The work stays inside the existing findings workflow, system finding-generator seams, and current report/register consumers. No new base directory, no new panel, and no new persistence layer are required. ## Complexity Tracking | Violation | Why Needed | Simpler Alternative Rejected Because | |-----------|------------|-------------------------------------| | One narrow findings-outcome semantics seam | Multiple producer and consumer paths need the same terminal-outcome meaning | Keeping meaning inside `FindingResource` static methods or free-form workflow-service strings would let reporting and automation drift immediately | ## Proportionality Review - **Current operator problem**: Terminal findings are currently too ambiguous for operators and reviewers to tell whether the issue was fixed, only declared fixed, system-confirmed clear, or closed as non-actionable. - **Existing structure is insufficient because**: The current lifecycle status plus free-form reasons cannot safely drive filters, audit interpretation, and report rollups with the same meaning. - **Narrowest correct implementation**: Preserve the existing status model and existing finding rows, add bounded canonical reason keys plus one narrow verified-clear interpretation seam, and reuse it across workflow producers and readers. - **Ownership cost created**: One findings-local semantics helper or equivalent seam, updates to workflow-service validation and audit metadata, updates to current generators and readers, and focused regression coverage. - **Alternative intentionally rejected**: A new primary findings status such as `verified`, or a generic governance-case framework, was rejected because both add broader workflow complexity than current release truth requires. - **Release truth**: Current-release truth. This feature corrects the meaning of today's findings workflow and today's downstream review/report consumers. ## Planned Phase Outputs - `research.md`: map current free-form reason usage in `FindingWorkflowService`, current system resolve/reopen producers, and current report/list consumers that read terminal findings - `data-model.md`: document the existing `Finding` fields, the canonical reason families, the derived verification meaning, and the report-bucket mapping - `quickstart.md`: record the narrowest implementation and validation workflow for service, UI, and reporting changes - `contracts/finding-outcome-taxonomy.logical.openapi.yaml`: describe the logical transition inputs and terminal-outcome outputs for list/detail/report consumers ## Implementation Strategy ### Phase A - Define the bounded outcome taxonomy close to the finding domain **Goal**: Create one canonical source for resolve, close, reopen, and verification meaning without changing primary lifecycle status. | Step | File | Change | |------|------|--------| | A.1 | `apps/platform/app/Models/Finding.php` | Add or centralize canonical reason-key lists and any small helper methods needed to describe terminal-outcome meaning while preserving existing status constants | | A.2 | `apps/platform/app/Support/Findings/FindingOutcomeSemantics.php` | Add one narrow findings-local helper that maps current finding state, reason keys, and exception validity into operator-safe outcome labels, verification meaning, and report buckets | | A.3 | `apps/platform/app/Services/Findings/FindingRiskGovernanceResolver.php` | Keep accepted-risk historical context and warning semantics aligned to the same canonical outcome buckets without collapsing exception validity into verified-clear meaning | | A.4 | `apps/platform/app/Support/Badges/*` or existing finding narrative paths | Reuse centralized badge and narrative rules for any new emphasis instead of page-local color or label mappings | ### Phase B - Harden workflow transitions and audit metadata around canonical reasons **Goal**: Make manual and system transitions persist canonical meaning instead of free-form semantics. | Step | File | Change | |------|------|--------| | B.1 | `apps/platform/app/Services/Findings/FindingWorkflowService.php` | Replace free-form-only `validatedReason()` behavior for resolve, close, and reopen with canonical bounded keys plus optional secondary note support if needed | | B.2 | `apps/platform/app/Services/Findings/FindingWorkflowService.php` | Extend audit metadata and snapshots so resolve, close, reopen, and system-origin transitions preserve structured outcome meaning and verification-safe context | | B.3 | `apps/platform/app/Services/Findings/FindingWorkflowService.php` | Keep `status` unchanged, but ensure system resolve and system reopen paths clear or set the derived verified-clear interpretation consistently | | B.4 | `apps/platform/app/Jobs/BackfillFindingLifecycleJob.php` and `apps/platform/app/Jobs/BackfillFindingLifecycleTenantIntoWorkspaceRunJob.php` | Replace legacy reason strings in current pre-production lifecycle normalization paths so canonical keys stay single-source during backfill and workspace-run repair flows | ### Phase C - Align system producers to the same system-clear and recurrence semantics **Goal**: Remove semantic drift between manual workflow actions and automated finding producers. | Step | File | Change | |------|------|--------| | C.1 | `apps/platform/app/Services/Baselines/BaselineAutoCloseService.php` | Use canonical system resolve reasons for auto-cleared findings | | C.2 | `apps/platform/app/Services/EntraAdminRoles/EntraAdminRolesFindingGenerator.php` and `apps/platform/app/Services/PermissionPosture/PermissionPostureFindingGenerator.php` | Route system resolve and reopen paths through the same canonical keys and verified-clear semantics | | C.3 | `apps/platform/app/Jobs/CompareBaselineToTenantJob.php` | Keep recurrence-driven reopen behavior aligned with the same structured reopen reasons and verified-clear reset rules | ### Phase D - Update operator surfaces without creating a second findings UX language **Goal**: Make list, detail, and action modals speak the same terminal-outcome language. | Step | File | Change | |------|------|--------| | D.1 | `apps/platform/app/Support/Ui/GovernanceActions/GovernanceActionCatalog.php` | Align shared findings action labels, helper copy, and reason policy metadata with the canonical resolve, close, and reopen vocabulary | | D.2 | `apps/platform/app/Filament/Resources/FindingResource.php` | Update grouped resolve, close, and reopen actions to present canonical options and secondary help text instead of free-form-only terminal meaning | | D.3 | `apps/platform/app/Filament/Resources/FindingResource.php` | Update list filters, default-visible terminal summaries, and detail narratives to consume the shared semantics helper | | D.4 | `apps/platform/app/Filament/Resources/FindingResource.php` | Preserve open-backlog defaults from Spec 111 and keep `risk_accepted` distinct through existing governance-validity signals | ### Phase E - Align findings-derived review and report consumers **Goal**: Keep downstream consumers from inventing a second outcome taxonomy. | Step | File | Change | |------|------|--------| | E.1 | `apps/platform/app/Filament/Pages/Reviews/ReviewRegister.php` | Update any findings-derived bucket or label usage to consume the shared outcome semantics instead of local wording | | E.2 | `apps/platform/app/Filament/Resources/TenantReviewResource.php` | Keep review-pack, executive-pack export cues, and tenant review summary presentation aligned with the same terminal-outcome buckets and verified-clear rules | ### Phase F - Prove the taxonomy through focused regression coverage **Goal**: Lock workflow, UI, and reporting semantics together with the smallest sufficient test set. | Step | File | Change | |------|------|--------| | F.1 | `apps/platform/tests/Feature/Findings/FindingWorkflowServiceTest.php` | Extend transition tests for canonical resolve, close, and reopen reasons plus audit metadata | | F.2 | `apps/platform/tests/Feature/Findings/FindingRecurrenceTest.php` | Prove recurrence reopens remove verified-clear interpretation and keep structured reopen reasons | | F.3 | `apps/platform/tests/Feature/Findings/FindingsListFiltersTest.php` and `apps/platform/tests/Feature/Filament/FindingResolvedReferencePresentationTest.php` | Prove list filters and detail presentation distinguish `Resolved pending verification`, `Verified cleared`, and non-remediation closure | | F.4 | `apps/platform/tests/Feature/Findings/FindingOutcomeSummaryReportingTest.php` and `apps/platform/tests/Feature/Findings/FindingRiskGovernanceProjectionTest.php` | Prove report and governance projections keep `risk_accepted`, verified-cleared, and closed-non-remediation buckets distinct | | F.5 | `cd apps/platform && ./vendor/bin/sail bin pint --dirty --format agent` plus the focused Pest commands above | Run formatting and the narrow proving set before implementation close-out | ## Post-Design Constitution Re-check - **Read/write separation**: PASS. The design still changes only existing findings transitions and existing read consumers. - **Persisted truth**: PASS. No new table, entity, or second semantic store was introduced by the design artifacts. - **Behavioral state**: PASS. Verification meaning stays derived from bounded keys and existing status, not from a new primary state. - **Abstraction control**: PASS. The design still justifies only one findings-local semantics helper. - **Filament / Livewire / panel safety**: PASS. The design remains within Filament v5 and Livewire v4, keeps provider registration unchanged in `bootstrap/providers.php`, does not add a new globally searchable resource, and does not introduce a new asset family beyond the existing `filament:assets` deploy policy. - **Testing governance**: PASS. Proof remains in focused feature suites and does not widen into browser or heavy-governance lanes.