# Tasks: Enforce Creation-Time Finding Invariants **Input**: Design documents from `/specs/255-enforce-finding-creation-invariants/` **Prerequisites**: `plan.md`, `spec.md`, `research.md`, `data-model.md`, `quickstart.md`, `contracts/finding-creation-invariants.contract.yaml`, `checklists/requirements.md` **Tests (TEST-GOV-001)**: REQUIRED (Pest). Keep proof in the targeted `fast-feedback` and `confidence` lanes named in `specs/255-enforce-finding-creation-invariants/plan.md` and `specs/255-enforce-finding-creation-invariants/quickstart.md`. Keep the three writer suites as the primary proof in `apps/platform/tests/Feature/Baselines/BaselineCompareFindingsTest.php`, `apps/platform/tests/Feature/EntraAdminRoles/EntraAdminRolesFindingGeneratorTest.php`, and `apps/platform/tests/Feature/PermissionPosture/PermissionPostureFindingGeneratorTest.php`. Use only bounded adjacent regression in `apps/platform/tests/Feature/Findings/FindingRecurrenceTest.php`, `apps/platform/tests/Feature/Findings/FindingAutomationWorkflowTest.php`, `apps/platform/tests/Feature/Findings/FindingWorkflowServiceTest.php`, `apps/platform/tests/Feature/Findings/MyWorkInboxTest.php`, `apps/platform/tests/Feature/Findings/FindingsIntakeQueueTest.php`, `apps/platform/tests/Feature/Rbac/BaselineCompareMatrixAuthorizationTest.php`, and `apps/platform/tests/Feature/EntraAdminRoles/AdminRolesSummaryWidgetTest.php` where they prove shared recurrence, consumer honesty, or unchanged trigger authorization without inflating the implementation scope into direct UI rewrites. **Operations**: This slice does not add or change an `OperationRun` start family, does not add a new audit action ID, and must not introduce migrations, DB constraints, repair tooling, deploy hooks, or external integrations. Existing `current_operation_run_id` correlations stay contextual only where the current writers already set them, and any report-emission assertions remain bounded to the writer suites that already own them. **RBAC**: Preserve current tenant/workspace isolation, current `404` versus `403` behavior on the baseline compare matrix and admin-roles scan trigger surfaces, and the existing tenant-scoped background/system reopen semantics. Do not add a new capability, bypass, or customer-facing workflow branch. **UI / Surface Guardrails**: This is a `review-mandatory` write-time truth hardening slice with `standard-native-filament` relief. `apps/platform/app/Filament/Resources/FindingResource.php`, `apps/platform/app/Filament/Pages/Findings/MyFindingsInbox.php`, and `apps/platform/app/Filament/Pages/Findings/FindingsIntakeQueue.php` stay regression consumers only unless existing tests prove a shared-truth fix is insufficient. **Filament UI Action Surfaces**: No new Filament Resource, Page, RelationManager, panel, provider, or asset work is introduced. `FindingResource` already has a view page, so global-search compliance stays satisfied without new tasking. No new destructive action is introduced or changed. **Organization**: Tasks are grouped by user story so each slice stays independently verifiable. Recommended delivery order is Phase 1 -> Phase 2 -> `US1` -> `US2` -> `US3` -> final validation, because creation-readiness must be explicit before reopen and recurrence proofs are tightened. **Implementation note**: If creation-time invariants converge through the three writer paths plus `FindingWorkflowService` and `FindingSlaPolicy`, keep downstream findings surfaces untouched and make proof responsibility explicit in their existing test files rather than planning direct edits to every listed consumer file. ## Test Governance Checklist - [x] Lane assignment stays `fast-feedback` plus `confidence` and remains the narrowest sufficient proof for write-time lifecycle hardening. - [x] New or changed tests stay in focused `Feature` files only; no browser or new heavy-governance family is added. - [x] Shared helpers, factories, fixtures, and context defaults stay cheap by default; any broader setup is isolated to the findings suites that already need it. - [x] Planned validation commands stay limited to the quickstart command set, allowing the three writer-suite commands to be combined into one equivalent Sail invocation plus the shared recurrence, consumer, and trigger-authorization checks below. - [x] The declared surface test profile stays `standard-native-filament`; downstream findings surfaces remain proof consumers only. - [x] Any material residue or follow-up note resolves as `document-in-feature`, `follow-up-spec`, or `reject-or-split`, not as implicit scope drift. ## Phase 1: Setup (Shared Invariant Anchors) **Purpose**: Lock the bounded writer inventory, shared lifecycle seams, and proving commands before implementation starts. - [x] T001 [P] Verify the bounded feature package, stop conditions, and non-goals across `specs/255-enforce-finding-creation-invariants/spec.md`, `specs/255-enforce-finding-creation-invariants/plan.md`, `specs/255-enforce-finding-creation-invariants/research.md`, `specs/255-enforce-finding-creation-invariants/data-model.md`, `specs/255-enforce-finding-creation-invariants/quickstart.md`, and `specs/255-enforce-finding-creation-invariants/contracts/finding-creation-invariants.contract.yaml` - [x] T002 [P] Verify the active finding-writer and shared seam inventory across `apps/platform/app/Jobs/CompareBaselineToTenantJob.php`, `apps/platform/app/Services/EntraAdminRoles/EntraAdminRolesFindingGenerator.php`, `apps/platform/app/Services/PermissionPosture/PermissionPostureFindingGenerator.php`, `apps/platform/app/Services/Findings/FindingWorkflowService.php`, `apps/platform/app/Services/Findings/FindingSlaPolicy.php`, and `apps/platform/app/Models/Finding.php` - [x] T003 [P] Verify the narrow Sail validation commands and manual smoke expectations in `specs/255-enforce-finding-creation-invariants/plan.md` and `specs/255-enforce-finding-creation-invariants/quickstart.md` - [x] T004 [P] Verify downstream proof-only consumers across `apps/platform/tests/Feature/Findings/FindingRecurrenceTest.php`, `apps/platform/tests/Feature/Findings/FindingAutomationWorkflowTest.php`, `apps/platform/tests/Feature/Findings/MyWorkInboxTest.php`, and `apps/platform/tests/Feature/Findings/FindingsIntakeQueueTest.php` **Checkpoint**: The bounded invariant target, shared seams, and validation entry points are explicit before any runtime file changes begin. --- ## Phase 2: Foundational (Blocking Proof Surfaces) **Purpose**: Make the intended proof surfaces and adjacent cleanup guardrails explicit before the write paths are changed. **CRITICAL**: No user story work should begin until this phase is complete. - [x] T005 [P] Lock the per-family lifecycle-ready and inline-repair proof plan in `apps/platform/tests/Feature/Baselines/BaselineCompareFindingsTest.php`, `apps/platform/tests/Feature/EntraAdminRoles/EntraAdminRolesFindingGeneratorTest.php`, and `apps/platform/tests/Feature/PermissionPosture/PermissionPostureFindingGeneratorTest.php` - [x] T006 [P] Lock the shared recurrence and reopen proof plan in `apps/platform/tests/Feature/Findings/FindingRecurrenceTest.php`, `apps/platform/tests/Feature/Findings/FindingAutomationWorkflowTest.php`, and `apps/platform/tests/Feature/Findings/FindingWorkflowServiceTest.php` - [x] T007 [P] Audit incomplete-lifecycle fixture and helper anchors across `apps/platform/database/factories/FindingFactory.php`, `apps/platform/tests/Feature/Baselines/BaselineCompareFindingsTest.php`, `apps/platform/tests/Feature/EntraAdminRoles/EntraAdminRolesFindingGeneratorTest.php`, and `apps/platform/tests/Feature/PermissionPosture/PermissionPostureFindingGeneratorTest.php` - [x] T008 [P] Audit adjacent cleanup guardrails in `apps/platform/tests/Feature/Findings/RemoveFindingsLifecycleBackfillActionTest.php` and `apps/platform/tests/Feature/Findings/RemoveAcknowledgedCompatibilityWorkflowTest.php` so this slice does not reintroduce repair tooling or acknowledged compatibility **Checkpoint**: Writer-level proof, shared reopen proof, and adjacent no-regression guardrails are explicit and ready for bounded implementation work. --- ## Phase 3: User Story 1 - See Ready Findings Immediately (Priority: P1) **Goal**: Newly detected findings arrive lifecycle-ready on first persistence across the three active writer families. **Independent Test**: Trigger one new finding per writer family and verify the first persisted record already carries canonical open status, seen history, ownership anchors, and due or SLA truth without any repair or second-pass workflow. ### Tests for User Story 1 - [x] T009 [P] [US1] Extend baseline compare create-readiness and incomplete-field inline-repair coverage in `apps/platform/tests/Feature/Baselines/BaselineCompareFindingsTest.php` - [x] T010 [P] [US1] Extend Entra admin roles create-readiness and incomplete-field inline-repair coverage in `apps/platform/tests/Feature/EntraAdminRoles/EntraAdminRolesFindingGeneratorTest.php` - [x] T011 [P] [US1] Extend permission posture create-readiness and incomplete-field inline-repair coverage in `apps/platform/tests/Feature/PermissionPosture/PermissionPostureFindingGeneratorTest.php` ### Implementation for User Story 1 - [x] T012 [US1] Align baseline compare finding creation and active-record refresh with the lifecycle-ready contract in `apps/platform/app/Jobs/CompareBaselineToTenantJob.php` - [x] T013 [US1] Align Entra admin roles finding creation and active-record refresh with the lifecycle-ready contract in `apps/platform/app/Services/EntraAdminRoles/EntraAdminRolesFindingGenerator.php` - [x] T014 [US1] Align permission posture finding creation and active-record refresh with the lifecycle-ready contract in `apps/platform/app/Services/PermissionPosture/PermissionPostureFindingGenerator.php` - [x] T015 [US1] Keep ownership anchors plus due or SLA initialization explicit without introducing a migration or repair surface in `apps/platform/app/Models/Finding.php`, `apps/platform/app/Services/Findings/FindingSlaPolicy.php`, and the three story tests from `T009` through `T011` **Checkpoint**: User Story 1 is independently functional and all three active writers create lifecycle-ready findings in the same write path. --- ## Phase 4: User Story 2 - Reopen the Same Finding When the Risk Returns (Priority: P1) **Goal**: Resolved findings reopen through the existing shared workflow path with refreshed lifecycle truth and preserved canonical identity. **Independent Test**: Resolve an in-scope finding, re-observe the same issue through each writer family, and verify the same record reopens with cleared terminal markers and refreshed due or SLA truth. ### Tests for User Story 2 - [x] T016 [P] [US2] Add baseline compare resolved-to-reopened regression and `current_operation_run_id` continuity coverage in `apps/platform/tests/Feature/Baselines/BaselineCompareFindingsTest.php` - [x] T017 [P] [US2] Add Entra admin roles resolved-to-reopened regression and `current_operation_run_id` continuity coverage in `apps/platform/tests/Feature/EntraAdminRoles/EntraAdminRolesFindingGeneratorTest.php` - [x] T018 [P] [US2] Add permission posture resolved-to-reopened regression plus existing `current_operation_run_id` and stored-report emission continuity coverage in `apps/platform/tests/Feature/PermissionPosture/PermissionPostureFindingGeneratorTest.php` - [x] T019 [P] [US2] Tighten shared reopen-service proof for `reopenBySystem()` due or SLA recalculation, audit continuity, and terminal eligibility in `apps/platform/tests/Feature/Findings/FindingWorkflowServiceTest.php` ### Implementation for User Story 2 - [x] T020 [US2] Keep reopened-state mutation on `FindingWorkflowService::reopenBySystem()` and reconcile baseline compare call sites in `apps/platform/app/Services/Findings/FindingWorkflowService.php` and `apps/platform/app/Jobs/CompareBaselineToTenantJob.php` - [x] T021 [US2] Preserve same-finding reopen identity and family-specific evidence refresh in `apps/platform/app/Services/EntraAdminRoles/EntraAdminRolesFindingGenerator.php` and `apps/platform/app/Services/PermissionPosture/PermissionPostureFindingGenerator.php` - [x] T022 [US2] Reconcile reopened due-date, SLA, and resolved-marker expectations without adding new workflow states or audit dialects in `apps/platform/app/Services/Findings/FindingSlaPolicy.php`, `apps/platform/app/Services/Findings/FindingWorkflowService.php`, and `apps/platform/tests/Feature/Findings/FindingWorkflowServiceTest.php` **Checkpoint**: User Story 2 is independently functional and resolved findings reopen through the existing shared workflow semantics rather than duplicating records or adding a second reopen path. --- ## Phase 5: User Story 3 - Keep One Canonical Finding Identity Through Repeated Detection (Priority: P2) **Goal**: Repeated observation strengthens the same finding record, respects each family's observation boundary, and keeps downstream surfaces truthful without widening the feature into UI redesign. **Independent Test**: Re-run the same observation and then a later valid observation across the in-scope families and verify that one canonical finding identity remains in place, same-observation retries do not double count, and downstream findings surfaces still read honest lifecycle truth. ### Tests for User Story 3 - [x] T023 [P] [US3] Extend same-observation idempotence and canonical-identity reuse coverage in `apps/platform/tests/Feature/Baselines/BaselineCompareFindingsTest.php` and `apps/platform/tests/Feature/Baselines/BaselineCompareFindingRecurrenceKeyTest.php` - [x] T024 [P] [US3] Extend cross-family recurrence and observation-boundary coverage in `apps/platform/tests/Feature/Findings/FindingRecurrenceTest.php` and `apps/platform/tests/Feature/Findings/FindingAutomationWorkflowTest.php` - [x] T025 [P] [US3] Tighten downstream consumer and trigger-authorization proof that shared lifecycle truth still renders honestly and that non-members remain `404` while in-scope capability failures remain `403` in `apps/platform/tests/Feature/Findings/MyWorkInboxTest.php`, `apps/platform/tests/Feature/Findings/FindingsIntakeQueueTest.php`, `apps/platform/tests/Feature/Rbac/BaselineCompareMatrixAuthorizationTest.php`, and `apps/platform/tests/Feature/EntraAdminRoles/AdminRolesSummaryWidgetTest.php` ### Implementation for User Story 3 - [x] T026 [US3] Preserve family-owned recurrence keys, fingerprints, and observation boundaries while preventing duplicate canonical findings in `apps/platform/app/Jobs/CompareBaselineToTenantJob.php`, `apps/platform/app/Services/EntraAdminRoles/EntraAdminRolesFindingGenerator.php`, and `apps/platform/app/Services/PermissionPosture/PermissionPostureFindingGenerator.php` - [x] T027 [US3] Keep any shared lifecycle normalization bounded to `apps/platform/app/Services/Findings/` only when it replaces real duplication across all three writers, with proof confined to `apps/platform/tests/Feature/Baselines/BaselineCompareFindingsTest.php`, `apps/platform/tests/Feature/EntraAdminRoles/EntraAdminRolesFindingGeneratorTest.php`, `apps/platform/tests/Feature/PermissionPosture/PermissionPostureFindingGeneratorTest.php`, and `apps/platform/tests/Feature/Findings/FindingRecurrenceTest.php` rather than widening into new workflow or UI files **Checkpoint**: User Story 3 is independently functional and recurrence keeps one canonical finding identity without double-counting or forcing direct downstream surface rewrites. --- ## Phase 6: Polish & Cross-Cutting Concerns **Purpose**: Keep the slice bounded, run the narrow validation workflow, and check for out-of-scope residue. - [x] T028 [P] Run formatting for touched PHP files with `export PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && cd apps/platform && ./vendor/bin/sail bin pint --dirty --format agent` - [x] T029 [P] Run the focused writer-suite Sail command `export PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Baselines/BaselineCompareFindingsTest.php tests/Feature/EntraAdminRoles/EntraAdminRolesFindingGeneratorTest.php tests/Feature/PermissionPosture/PermissionPostureFindingGeneratorTest.php` - [x] T030 [P] Run the focused shared-recurrence Sail command `export PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Findings/FindingRecurrenceTest.php tests/Feature/Findings/FindingAutomationWorkflowTest.php tests/Feature/Findings/FindingWorkflowServiceTest.php` - [x] T031 [P] Run the downstream-consumer and trigger-RBAC Sail command `export PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Findings/MyWorkInboxTest.php tests/Feature/Findings/FindingsIntakeQueueTest.php tests/Feature/Rbac/BaselineCompareMatrixAuthorizationTest.php tests/Feature/EntraAdminRoles/AdminRolesSummaryWidgetTest.php` - [ ] T032 [P] Execute quickstart manual smoke steps 1 through 4 from `specs/255-enforce-finding-creation-invariants/quickstart.md` against `/admin/t/{tenant}/findings`, `MyFindingsInbox`, and `FindingsIntakeQueue`, then leave diff/scope review to `T034` - [x] T033 [P] Run residue searches for `backfill`, `repair`, `constraint`, `migration`, and any new `Finding::STATUS_` additions across `apps/platform/app/`, `apps/platform/tests/`, `apps/platform/database/`, and `specs/255-enforce-finding-creation-invariants/`, then classify each remaining match as allowed shared-consumer proof, in-scope cleanup to delete now, or `reject-or-split` - [x] T034 Verify that no file under `apps/platform/database/migrations/` changed, no new repair or rollout entry point appeared under `apps/platform/app/Console/Commands/` or `apps/platform/app/Services/Runbooks/`, and no direct workflow expansion landed in `apps/platform/app/Filament/Resources/FindingResource.php`, `apps/platform/app/Filament/Pages/Findings/MyFindingsInbox.php`, or `apps/platform/app/Filament/Pages/Findings/FindingsIntakeQueue.php`; if truth-consumer proof from `T025` and `T031` suggests a direct UI edit is necessary, stop and record that as `document-in-feature` or `reject-or-split` instead of treating it as default in-scope work --- ## Dependencies & Execution Order ### Phase Dependencies - **Setup (Phase 1)**: Starts immediately and locks the exact scope, writer inventory, and proving commands. - **Foundational (Phase 2)**: Depends on Setup and blocks all story work until proof files, fixtures, and adjacent cleanup guardrails are explicit. - **User Story 1 (Phase 3)**: Depends on Foundational and establishes the lifecycle-ready create contract. - **User Story 2 (Phase 4)**: Depends on User Story 1 because reopen semantics should refresh the same lifecycle-ready contract established at creation time. - **User Story 3 (Phase 5)**: Depends on User Story 1 and User Story 2 because recurrence and consumer proof only mean the right thing after create and reopen behavior are aligned. - **Polish (Phase 6)**: Depends on all desired user stories being complete so final validation and residue checks run on the finished slice. ### User Story Dependencies - **US1**: No dependencies beyond Foundational. - **US2**: Depends on US1. - **US3**: Depends on US1 and US2. ### Within Each User Story - Add or update the story tests first and confirm they fail before implementation edits are considered complete. - Keep recurrence identity family-owned instead of introducing a generic invariant framework. - Keep downstream findings surfaces as proof consumers unless shared-truth tests prove a concrete need for direct edits. - Keep migrations, DB constraints, repair tooling, acknowledged cleanup, external support-desk or PSA work, customer-facing workflow changes, and broad findings redesign out of scope. ### Parallel Opportunities - `T001`, `T002`, `T003`, and `T004` can run in parallel during Setup. - `T005`, `T006`, `T007`, and `T008` can run in parallel during Foundational work. - `T009`, `T010`, and `T011` can run in parallel for User Story 1 before `T012`, `T013`, `T014`, and `T015`. - `T016`, `T017`, `T018`, and `T019` can run in parallel for User Story 2 before `T020`, `T021`, and `T022`. - `T023`, `T024`, and `T025` can run in parallel for User Story 3 before `T026` and `T027`. - `T029`, `T030`, `T031`, `T032`, and `T033` can run in parallel during final validation after `T028`, followed by `T034` as the final scope-boundary check. --- ## Parallel Example: User Story 1 ```bash # User Story 1 tests in parallel T009 apps/platform/tests/Feature/Baselines/BaselineCompareFindingsTest.php T010 apps/platform/tests/Feature/EntraAdminRoles/EntraAdminRolesFindingGeneratorTest.php T011 apps/platform/tests/Feature/PermissionPosture/PermissionPostureFindingGeneratorTest.php # User Story 1 implementation after the tests are in place T012 apps/platform/app/Jobs/CompareBaselineToTenantJob.php T013 apps/platform/app/Services/EntraAdminRoles/EntraAdminRolesFindingGenerator.php T014 apps/platform/app/Services/PermissionPosture/PermissionPostureFindingGenerator.php ``` ## Parallel Example: User Story 2 ```bash # User Story 2 tests in parallel T016 apps/platform/tests/Feature/Baselines/BaselineCompareFindingsTest.php T017 apps/platform/tests/Feature/EntraAdminRoles/EntraAdminRolesFindingGeneratorTest.php T018 apps/platform/tests/Feature/PermissionPosture/PermissionPostureFindingGeneratorTest.php T019 apps/platform/tests/Feature/Findings/FindingWorkflowServiceTest.php # User Story 2 implementation after the tests are in place T020 apps/platform/app/Services/Findings/FindingWorkflowService.php + apps/platform/app/Jobs/CompareBaselineToTenantJob.php T021 apps/platform/app/Services/EntraAdminRoles/EntraAdminRolesFindingGenerator.php + apps/platform/app/Services/PermissionPosture/PermissionPostureFindingGenerator.php ``` ## Parallel Example: User Story 3 ```bash # User Story 3 tests in parallel T023 apps/platform/tests/Feature/Baselines/BaselineCompareFindingsTest.php + apps/platform/tests/Feature/Baselines/BaselineCompareFindingRecurrenceKeyTest.php T024 apps/platform/tests/Feature/Findings/FindingRecurrenceTest.php + apps/platform/tests/Feature/Findings/FindingAutomationWorkflowTest.php T025 apps/platform/tests/Feature/Findings/MyWorkInboxTest.php + apps/platform/tests/Feature/Findings/FindingsIntakeQueueTest.php # User Story 3 implementation after the tests are in place T026 apps/platform/app/Jobs/CompareBaselineToTenantJob.php + apps/platform/app/Services/EntraAdminRoles/EntraAdminRolesFindingGenerator.php + apps/platform/app/Services/PermissionPosture/PermissionPostureFindingGenerator.php T027 apps/platform/app/Services/Findings/ ``` --- ## Implementation Strategy ### MVP First (User Stories 1 and 2) 1. Complete Phase 1: Setup. 2. Complete Phase 2: Foundational. 3. Complete Phase 3: User Story 1. 4. Complete Phase 4: User Story 2. 5. Run `T028`, `T029`, and `T030` before widening into recurrence and consumer-proof cleanup. ### Incremental Delivery 1. Lock the bounded writer inventory, proof files, and stop conditions. 2. Make create-time lifecycle readiness explicit for baseline compare, Entra admin roles, and permission posture. 3. Preserve the shared reopen path and refresh lifecycle truth when resolved findings return. 4. Tighten recurrence, idempotence, and downstream proof without widening into UI redesign or repair tooling. 5. Finish with focused Sail validation, manual smoke, and residue checks. ### Parallel Team Strategy 1. One contributor can own the three writer-family tests while another confirms shared recurrence and downstream consumer proof after Phase 2. 2. After User Story 1 lands, one contributor can align the reopen path while another prepares the recurrence and consumer proof for User Story 3. 3. Finish with one bounded pass for formatting, focused Sail validation, and residue or scope-boundary review. --- ## Notes - Suggested MVP scope: Phase 1 through Phase 4. Creation readiness without reopen reuse is not sufficient for this feature. - Explicit non-goals remain: runtime backfill surfaces, acknowledged cleanup, new workflow states, broad findings redesign, migrations or DB constraints, repair tooling, external support-desk or PSA work, and customer-facing workflow expansion. - Filament remains on Livewire v4.0+; no panel/provider or asset strategy changes are needed, and `apps/platform/bootstrap/providers.php` remains the relevant provider-registration location if later Filament work is ever required. - All tasks above follow the required checklist format with task ID, optional parallel marker, story label where applicable, and concrete file paths.