Some checks failed
Main Confidence / confidence (push) Failing after 59s
Automatisch erstellt: Merge `platform-dev` into `dev` (via MCP) Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de> Reviewed-on: #299
296 lines
25 KiB
Markdown
296 lines
25 KiB
Markdown
# Implementation Plan: Enforce Creation-Time Finding Invariants
|
|
|
|
**Branch**: `255-enforce-finding-creation-invariants` | **Date**: 2026-04-29 | **Spec**: `/Users/ahmeddarrazi/Documents/projects/wt-plattform/specs/255-enforce-finding-creation-invariants/spec.md`
|
|
**Input**: Feature specification from `/Users/ahmeddarrazi/Documents/projects/wt-plattform/specs/255-enforce-finding-creation-invariants/spec.md`
|
|
|
|
**Note**: This plan is prep-only. It updates only spec-package artifacts for implementation readiness and does not change application code, runtime behavior, migrations, assets, or repo files outside this spec directory.
|
|
|
|
## Summary
|
|
|
|
- Make lifecycle-ready finding creation and recurrence semantics explicit across the only three active finding writers currently persisting `Finding` records: baseline compare drift, Entra admin roles, and permission posture.
|
|
- Keep the slice narrow and repo-grounded: reuse existing `Finding` fields, existing recurrence identities, existing `FindingWorkflowService::reopenBySystem()`, and existing `FindingSlaPolicy` behavior; do not add repair tooling, workflow states, migrations, or a broader findings framework.
|
|
- Tighten validation where repo proof is already strongest: extend the three focused feature suites so they explicitly cover new creation, repeated observation, resolved-to-reopened behavior, and inline repair of incomplete lifecycle fields encountered on active write paths.
|
|
|
|
## Technical Context
|
|
|
|
**Language/Version**: PHP 8.4, Laravel 12
|
|
**Primary Dependencies**: Filament v5, Livewire v4, Pest v4, existing `FindingWorkflowService`, `FindingSlaPolicy`, baseline compare job, Entra admin roles finding generator, and permission posture finding generator
|
|
**Storage**: PostgreSQL existing `findings`, `operation_runs`, `stored_reports`, and `audit_logs` only; no new persistence or migration is planned
|
|
**Testing**: Pest feature tests in the existing generator and compare suites
|
|
**Validation Lanes**: fast-feedback, confidence
|
|
**Target Platform**: Sail-backed Laravel web application with tenant `/admin/t/{tenant}` findings surfaces and existing background jobs or services that already generate findings
|
|
**Project Type**: web
|
|
**Performance Goals**: lifecycle invariants must be satisfied in the same write path that creates or refreshes the finding; no second-pass repair job, no extra operator step, and no widened query surface should be required
|
|
**Constraints**: LEAN-001 replacement over compatibility shims; no new persistence; no new workflow states; no compare refresh or repair-tooling scope; preserve existing `404` vs `403` behavior; no new Filament assets, panel work, or provider registration changes
|
|
**Scale/Scope**: 3 active finding writer families, 1 shared workflow service, 1 shared SLA policy, 1 existing `Finding` model, and 3 established feature-test families plus downstream findings surfaces as regression consumers only
|
|
|
|
## Likely Affected Repo Surfaces
|
|
|
|
- Active write paths and their local recurrence or observation logic:
|
|
- `apps/platform/app/Jobs/CompareBaselineToTenantJob.php`
|
|
- `apps/platform/app/Services/EntraAdminRoles/EntraAdminRolesFindingGenerator.php`
|
|
- `apps/platform/app/Services/PermissionPosture/PermissionPostureFindingGenerator.php`
|
|
- Shared lifecycle and due-date seams already reused by those paths:
|
|
- `apps/platform/app/Services/Findings/FindingWorkflowService.php`
|
|
- `apps/platform/app/Services/Findings/FindingSlaPolicy.php`
|
|
- `apps/platform/app/Models/Finding.php`
|
|
- Downstream operator-facing regression consumers that should not need design changes but do rely on `due_at`, `reopened_at`, and canonical open-status truth:
|
|
- `apps/platform/app/Filament/Resources/FindingResource.php`
|
|
- `apps/platform/app/Filament/Resources/FindingResource/Pages/ListFindings.php`
|
|
- `apps/platform/app/Filament/Pages/Findings/MyFindingsInbox.php`
|
|
- `apps/platform/app/Filament/Pages/Findings/FindingsIntakeQueue.php`
|
|
- Current focused proof surfaces that already cover part of the invariant and should remain the primary validation entry points:
|
|
- `apps/platform/tests/Feature/Baselines/BaselineCompareFindingsTest.php`
|
|
- `apps/platform/tests/Feature/EntraAdminRoles/EntraAdminRolesFindingGeneratorTest.php`
|
|
- `apps/platform/tests/Feature/PermissionPosture/PermissionPostureFindingGeneratorTest.php`
|
|
|
|
## Domain / Model Fit
|
|
|
|
- `Finding` remains the single tenant-owned source of truth with required `workspace_id` and `tenant_id` anchors. No new entity, table, compatibility projection, or lifecycle wrapper is introduced.
|
|
- The slice does not change the canonical findings status set. `new`, `triaged`, `in_progress`, and `reopened` remain the active statuses; `resolved`, `closed`, and `risk_accepted` remain terminal statuses.
|
|
- Lifecycle-ready creation in this feature means that the first persisted or inline-repaired record is already safe for existing downstream workflow use: canonical active status, `first_seen_at`, `last_seen_at`, `times_seen >= 1`, and existing SLA or `due_at` truth when the current severity policy requires them.
|
|
- Recurrence identity stays family-owned and explicit rather than being normalized into a new shared engine:
|
|
- baseline compare uses `recurrence_key` plus `fingerprint`, with `current_operation_run_id` preventing double counting for the same compare run
|
|
- Entra admin roles uses its existing role-assignment and aggregate fingerprints
|
|
- permission posture uses its existing missing-permission and error fingerprints
|
|
- `OperationRun` and `StoredReport` remain contextual references only where current writers already use them. This slice does not introduce a new audit artifact or independent lifecycle store.
|
|
|
|
## UI / Filament & Livewire Fit
|
|
|
|
- No operator-facing surface change is planned. Existing findings resource, inbox, and intake surfaces are regression consumers of better write-time truth, not redesign targets.
|
|
- Filament remains v5 on Livewire v4.0+; no Livewire v3 behavior or API is in scope.
|
|
- `FindingResource` already has a view page, so the hard global-search rule remains satisfied without new work. No new globally searchable resource is added.
|
|
- No destructive action is introduced or changed. Any touched findings action surface must keep current server-side authorization and existing `->requiresConfirmation()` behavior where destructive-like actions already exist.
|
|
- No panel/provider work is planned. If provider registration ever became relevant later, Laravel 12 and Filament v5 still require panel providers under `apps/platform/bootstrap/providers.php`, not `bootstrap/app.php`.
|
|
- No asset change is planned. Deployment keeps the existing `cd apps/platform && php artisan filament:assets` expectation unchanged only for already-registered assets.
|
|
|
|
## RBAC / Policy Fit
|
|
|
|
- This slice should not add a new capability, new role mapping, or new policy branch. User-triggered actions that lead to in-scope finding writes keep their current authorization semantics.
|
|
- Tenant membership and workspace membership remain the isolation boundary: non-members stay `404`, in-scope members missing the current capability stay `403`, and no new write bypass is introduced for background or queued generation.
|
|
- If implementation appears to require a new capability or policy relaxation just to enforce lifecycle invariants, that is a stop condition and should be split rather than absorbed.
|
|
|
|
## Audit / Logging Fit
|
|
|
|
- `FindingWorkflowService::reopenBySystem()` remains the authoritative reopen path because it already owns reopened state mutation, audit context, and alert notification dispatch.
|
|
- No new `AuditActionId`, no new operation type, and no new completion notification path should be introduced.
|
|
- The feature should preserve existing `current_operation_run_id` and `StoredReport` correlation meaning where current writers already set them. Creation-time hardening must not create a second audit or run-tracking dialect.
|
|
|
|
## Data / Migration / Constraint Fit
|
|
|
|
- No migration, no historical data backfill, no deploy hook, and no repair command are planned.
|
|
- Under LEAN-001, stale local data or incomplete fixtures should be handled by fixture replacement or inline repair on active write paths, not by compatibility shims.
|
|
- A database-level constraint discussion is allowed only as an explicit follow-up or stop condition if planning or implementation proves that application-level write-path enforcement cannot satisfy the invariant safely. It must not be silently folded into this slice.
|
|
- If due-date initialization for already-open findings would require recomputing correct existing data instead of filling missing lifecycle fields only, stop and split rather than broadening this feature into a data repair rollout.
|
|
|
|
## UI / Surface Guardrail Plan
|
|
|
|
- **Guardrail scope**: no operator-facing surface change
|
|
- **Native vs custom classification summary**: N/A - existing native Filament findings surfaces remain regression consumers only
|
|
- **Shared-family relevance**: none; no new notification, header action, dashboard, or evidence viewer family is added
|
|
- **State layers in scope**: none
|
|
- **Audience modes in scope**: N/A
|
|
- **Decision/diagnostic/raw hierarchy plan**: N/A
|
|
- **Raw/support gating plan**: N/A
|
|
- **One-primary-action / duplicate-truth control**: existing findings workflow actions remain unchanged; tighter write-time truth prevents partial lifecycle data from competing with the existing canonical action flow
|
|
- **Handling modes by drift class or surface**: N/A
|
|
- **Repository-signal treatment**: review-mandatory for downstream regression only
|
|
- **Special surface test profiles**: standard-native-filament regression only
|
|
- **Required tests or manual smoke**: functional-core, state-contract
|
|
- **Exception path and spread control**: none
|
|
- **Active feature PR close-out entry**: Guardrail
|
|
|
|
## Shared Pattern & System Fit
|
|
|
|
- **Cross-cutting feature marker**: no
|
|
- **Systems touched**: N/A for shared operator interaction families; domain reuse stays within existing findings lifecycle services only
|
|
- **Shared abstractions reused**: existing `FindingWorkflowService` and `FindingSlaPolicy` only
|
|
- **New abstraction introduced? why?**: none by default; if a shared write-time normalizer is later proposed, it must be a narrow findings-domain replacement for duplicated inline repair across all three concrete writers, not a new registry or framework
|
|
- **Why the existing abstraction was sufficient or insufficient**: `reopenBySystem()` is already sufficient for terminal-to-reopened transitions. The current planning gap is open-record lifecycle repair, which is still duplicated and partially covered across the three writers.
|
|
- **Bounded deviation / spread control**: none; keep any repair logic either local to each writer or in one bounded findings-domain helper only if it replaces real duplication immediately
|
|
|
|
## OperationRun UX Impact
|
|
|
|
- **Touches OperationRun start/completion/link UX?**: no
|
|
- **Central contract reused**: N/A
|
|
- **Delegated UX behaviors**: N/A
|
|
- **Surface-owned behavior kept local**: existing baseline compare and other generation flows keep their current start and completion UX unchanged
|
|
- **Queued DB-notification policy**: N/A
|
|
- **Terminal notification path**: N/A
|
|
- **Exception path**: none
|
|
|
|
## Provider Boundary & Portability Fit
|
|
|
|
- **Shared provider/platform boundary touched?**: no
|
|
- **Provider-owned seams**: N/A
|
|
- **Platform-core seams**: existing tenant-owned findings truth only
|
|
- **Neutral platform terms / contracts preserved**: existing `Finding` lifecycle and tenant/workspace ownership vocabulary remain unchanged
|
|
- **Retained provider-specific semantics and why**: provider-specific recurrence evidence stays inside the existing writer families that already own it
|
|
- **Bounded extraction or follow-up path**: N/A
|
|
|
|
## Constitution Check
|
|
|
|
*GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.*
|
|
|
|
- LEAN-001: PASS - the slice is explicitly app-code hardening only; no compatibility shim, legacy alias, fallback reader, or migration path is planned.
|
|
- TEST-GOV-001: PASS - proof stays in the narrowest existing feature suites, with no browser lane and no new heavy-governance family.
|
|
- RBAC-UX: PASS - no new capability or policy branch is introduced; non-members remain `404`, members lacking the current capability remain `403`, and system generation stays tenant-scoped.
|
|
- PERSIST-001: PASS - no new persisted truth, table, artifact, or projection is introduced.
|
|
- STATE-001: PASS - no new state, reason-code family, or lifecycle branch is added; current findings states remain authoritative.
|
|
- PROP-001 / ABSTR-001: PASS - the narrowest plan is to align the three concrete write paths and reuse the existing reopen service. Any helper beyond that is a stop-and-justify decision, not a default.
|
|
- XCUT-001 / UI-SEM-001: PASS - no new operator interaction family or presentation framework is introduced.
|
|
- Filament v5 / Livewire v4 compliance: PASS - existing findings surfaces stay on native Filament v5 with Livewire v4.0+; no legacy API mixing is planned.
|
|
- Global-search hard rule: PASS - `FindingResource` already has a view page, and no new searchable resource is added.
|
|
- Panel/provider registration: PASS - no panel/provider work is planned; if needed later, Filament v5 on Laravel 12 still uses `apps/platform/bootstrap/providers.php`.
|
|
- Destructive confirmation standard: PASS - no new destructive action is added; existing destructive-like actions remain outside this slice.
|
|
- Asset strategy: PASS - no new panel or shared asset registration is planned; existing deploy behavior for `filament:assets` remains unchanged.
|
|
- Auditability and tenant isolation: PASS - reopen semantics remain on the current audited service path, and every in-scope write remains bound to tenant and workspace context.
|
|
|
|
## Test Governance Check
|
|
|
|
- **Test purpose / classification by changed surface**: Feature for writer-level creation-time lifecycle readiness, shared recurrence/workflow-service behavior, and narrow downstream consumer plus trigger-authorization continuity checks; no new unit, browser, or heavy-governance family is planned
|
|
- **Affected validation lanes**: fast-feedback, confidence
|
|
- **Why this lane mix is the narrowest sufficient proof**: the feature risk lives in domain write behavior already exercised through the existing compare and generator suites, but FR-255-005, FR-255-006, FR-255-009, and FR-255-011 also require bounded proof of shared recurrence/workflow behavior and unchanged consumer/auth continuity. Focused feature coverage is still sufficient because the adjacent checks stay limited to existing findings and trigger-authorization suites.
|
|
- **Narrowest proving command(s)**:
|
|
- `export PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Baselines/BaselineCompareFindingsTest.php`
|
|
- `export PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/EntraAdminRoles/EntraAdminRolesFindingGeneratorTest.php`
|
|
- `export PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/PermissionPosture/PermissionPostureFindingGeneratorTest.php`
|
|
- `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`
|
|
- `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`
|
|
- **Fixture / helper / factory / seed / context cost risks**: low to moderate but bounded; reuse existing tenant, operation-run, snapshot, generator, and trigger-surface fixtures. Avoid a new umbrella findings harness unless repeated setup clearly becomes the bottleneck.
|
|
- **Expensive defaults or shared helper growth introduced?**: no; the plan explicitly avoids a new generic invariant framework or new default-heavy helper layer.
|
|
- **Heavy-family additions, promotions, or visibility changes**: none
|
|
- **Surface-class relief / special coverage rule**: standard-native relief; no browser smoke is required because no operator-facing interaction changes are planned
|
|
- **Closing validation and reviewer handoff**: rerun the three writer suites plus the bounded recurrence/workflow and consumer/auth suites, confirm each family now proves missing-field inline repair in addition to existing create/idempotence/reopen behavior, and verify that no migration, no policy branch, and no new UI action was introduced while hardening write paths.
|
|
- **Budget / baseline / trend follow-up**: none expected beyond routine feature-test maintenance
|
|
- **Review-stop questions**: did implementation widen into a repair tool, migration, DB constraint rollout, or generic invariant framework; did it silently reset already-valid due dates; did it leave one writer family with only partial invariant proof
|
|
- **Escalation path**: reject-or-split
|
|
- **Active feature PR close-out entry**: Guardrail
|
|
- **Why no dedicated follow-up spec is needed**: routine lifecycle-hardening proof belongs in this feature unless a database-level constraint or a broader findings lifecycle redesign is proven necessary later
|
|
|
|
## Rollout & Risk Controls
|
|
|
|
- Rollout is code-only and bounded. No migration, queue worker sequencing, asset build, or provider registration step is expected.
|
|
- Recommended implementation order is:
|
|
1. confirm the shared invariant vocabulary and stop conditions against the three active writers only
|
|
2. harden baseline compare first because it already carries the strictest observation-boundary rule through `current_operation_run_id`
|
|
3. align permission posture and Entra admin roles creation and refresh logic around the same lifecycle-ready contract while preserving their family-specific recurrence rules
|
|
4. extract a shared normalizer only if the concrete code shows immediate duplication across all three paths and the helper replaces duplication instead of adding a new abstraction layer
|
|
5. extend focused regression tests and verify downstream findings surfaces do not require design changes
|
|
- Stop conditions for task execution:
|
|
- another shipped finding writer is discovered outside the three confirmed paths
|
|
- the invariant cannot be enforced safely without a migration or DB constraint
|
|
- the only available code shape is a new generic registry, strategy system, or lifecycle framework
|
|
- user-facing findings workflow affordances would need to change to compensate for missing write-time truth
|
|
|
|
## Project Structure
|
|
|
|
### Documentation (this feature)
|
|
|
|
```text
|
|
specs/255-enforce-finding-creation-invariants/
|
|
├── plan.md
|
|
├── research.md
|
|
├── data-model.md
|
|
├── quickstart.md
|
|
├── checklists/
|
|
│ └── requirements.md
|
|
├── contracts/
|
|
│ └── finding-creation-invariants.contract.yaml
|
|
└── tasks.md
|
|
```
|
|
|
|
### Source Code (repository root)
|
|
|
|
```text
|
|
apps/platform/
|
|
├── app/
|
|
│ ├── Jobs/
|
|
│ │ └── CompareBaselineToTenantJob.php
|
|
│ ├── Models/
|
|
│ │ └── Finding.php
|
|
│ ├── Services/
|
|
│ │ ├── EntraAdminRoles/
|
|
│ │ │ └── EntraAdminRolesFindingGenerator.php
|
|
│ │ ├── Findings/
|
|
│ │ │ ├── FindingSlaPolicy.php
|
|
│ │ │ └── FindingWorkflowService.php
|
|
│ │ └── PermissionPosture/
|
|
│ │ └── PermissionPostureFindingGenerator.php
|
|
│ └── Filament/
|
|
│ ├── Pages/Findings/
|
|
│ │ ├── FindingsIntakeQueue.php
|
|
│ │ └── MyFindingsInbox.php
|
|
│ └── Resources/
|
|
│ ├── FindingResource.php
|
|
│ └── FindingResource/
|
|
│ └── Pages/ListFindings.php
|
|
└── tests/
|
|
└── Feature/
|
|
├── Baselines/BaselineCompareFindingsTest.php
|
|
├── EntraAdminRoles/EntraAdminRolesFindingGeneratorTest.php
|
|
├── Findings/FindingRecurrenceTest.php
|
|
├── Findings/FindingAutomationWorkflowTest.php
|
|
├── Findings/FindingWorkflowServiceTest.php
|
|
├── Findings/MyWorkInboxTest.php
|
|
├── Findings/FindingsIntakeQueueTest.php
|
|
├── Rbac/BaselineCompareMatrixAuthorizationTest.php
|
|
├── EntraAdminRoles/AdminRolesSummaryWidgetTest.php
|
|
└── PermissionPosture/PermissionPostureFindingGeneratorTest.php
|
|
```
|
|
|
|
**Structure Decision**: Laravel monolith. The implementation should stay inside the existing finding writer services and job, the shared findings lifecycle service and model, and the current focused feature suites rather than creating a new namespace or framework.
|
|
|
|
## Complexity Tracking
|
|
|
|
No constitution violation is expected. If implementation later proposes a new persistence rule, a new lifecycle framework, or a broad helper layer that serves only speculative future writers, stop and split rather than justifying it inside this slice.
|
|
|
|
## Proportionality Review
|
|
|
|
N/A - this feature introduces no new enum, presenter, persisted entity, interface, registry, or taxonomy. Any narrow helper extracted during implementation must replace existing duplicated write-time lifecycle normalization immediately across the three confirmed writers or it should not be introduced.
|
|
|
|
## Phase 0 — Research (output: `research.md`)
|
|
|
|
See: `/Users/ahmeddarrazi/Documents/projects/wt-plattform/specs/255-enforce-finding-creation-invariants/research.md`
|
|
|
|
Goals:
|
|
- confirm that the three already-named write paths are still the full active finding-writer inventory in app code
|
|
- confirm where current code already repairs lifecycle fields inline and where `sla_days` or `due_at` normalization is still only implied on create or reopen
|
|
- document the narrowest shared seam decision: keep repair logic local per writer unless one bounded findings-domain helper clearly replaces real duplication across all three cases
|
|
- record the explicit stop condition for any database-level constraint or migration-based enforcement proposal
|
|
|
|
## Phase 1 — Design & Contracts (outputs: `data-model.md`, `contracts/`, `quickstart.md`)
|
|
|
|
See:
|
|
- `/Users/ahmeddarrazi/Documents/projects/wt-plattform/specs/255-enforce-finding-creation-invariants/data-model.md`
|
|
- `/Users/ahmeddarrazi/Documents/projects/wt-plattform/specs/255-enforce-finding-creation-invariants/contracts/finding-creation-invariants.contract.yaml`
|
|
- `/Users/ahmeddarrazi/Documents/projects/wt-plattform/specs/255-enforce-finding-creation-invariants/quickstart.md`
|
|
|
|
Design focus:
|
|
- capture one lifecycle-ready finding contract that all three active writers must satisfy without introducing a new persistence or workflow layer
|
|
- keep recurrence identity family-owned while making the create, refresh, and reopen guarantees explicit in one planning contract
|
|
- keep downstream Filament findings surfaces, inboxes, and intake queues as regression consumers only; no UI redesign is part of this slice
|
|
- document the no-migration, no-constraint-by-default posture and the explicit stop condition for any future constraint follow-up
|
|
|
|
## Phase 1 — Agent Context Update
|
|
|
|
- Deferred in this prep-only pass because the user explicitly limited edits to this spec directory.
|
|
- If maintainers later want full Spec Kit propagation outside the spec package, run:
|
|
- `/Users/ahmeddarrazi/Documents/projects/wt-plattform/.specify/scripts/bash/update-agent-context.sh copilot`
|
|
|
|
## Phase 2 — Implementation Outline (tasks created later in `/speckit.tasks`)
|
|
|
|
- keep the feature bounded to the three confirmed writer paths and the shared reopen service
|
|
- align creation-time lifecycle initialization and open-record inline repair in `CompareBaselineToTenantJob`, `EntraAdminRolesFindingGenerator`, and `PermissionPostureFindingGenerator`
|
|
- preserve family-specific recurrence and observation-boundary behavior while making it explicit in code and tests
|
|
- preserve `FindingWorkflowService::reopenBySystem()` as the only reopened-state mutation path
|
|
- extend the three focused feature suites so each family proves creation readiness, repeated observation, resolved-to-reopened behavior, and inline repair of incomplete lifecycle fields encountered on active paths
|
|
- verify that no migration, no new capability, no new workflow state, no repair surface, and no operator-facing workflow expansion slipped into the implementation slice
|
|
|
|
## Constitution Check (Post-Design)
|
|
|
|
Re-check target: PASS. The post-design shape remains prep-only, introduces no new persistence or state family, keeps Filament on Livewire v4.0+, leaves provider registration unchanged in `apps/platform/bootstrap/providers.php`, keeps global search unchanged through the existing `FindingResource` view page, leaves destructive actions untouched, and keeps the proving burden inside the three existing focused feature suites unless a bounded stop condition forces a split.
|
|
- **Ownership cost created**: focused ongoing maintenance in the three writer suites plus bounded shared recurrence/workflow and trigger-authorization regressions; no migration, framework, or new persistence cost is added.
|
|
- **Alternative intentionally rejected**: a generic invariant framework, a new repair or backfill path, and any DB-constraint rollout were rejected because the repo currently has three concrete writers and current-release truth only requires tightening those exact paths.
|
|
- **Release truth**: current-release truth. This package hardens already-shipped finding writers rather than preparing speculative future families.
|