Implements spec 111 (Findings workflow + SLA) and fixes Workspace findings SLA settings UX/validation. Key changes: - Findings workflow service + SLA policy and alerting. - Workspace settings: allow partial SLA overrides without auto-filling unset severities in the UI; effective values still resolve via defaults. - New migrations, jobs, command, UI/resource updates, and comprehensive test coverage. Tests: - `vendor/bin/sail artisan test --compact` (1779 passed, 8 skipped). Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de> Reviewed-on: #135
340 lines
21 KiB
Markdown
340 lines
21 KiB
Markdown
---
|
||
|
||
description: "Task list for Findings Workflow V2 + SLA (111)"
|
||
---
|
||
|
||
# Tasks: Findings Workflow V2 + SLA (111)
|
||
|
||
**Input**: Design documents from `specs/111-findings-workflow-sla/`
|
||
**Prerequisites**: `specs/111-findings-workflow-sla/plan.md` (required), `specs/111-findings-workflow-sla/spec.md` (required for user stories), `specs/111-findings-workflow-sla/research.md`, `specs/111-findings-workflow-sla/data-model.md`, `specs/111-findings-workflow-sla/contracts/api-contracts.md`, `specs/111-findings-workflow-sla/quickstart.md`
|
||
|
||
**Tests**: REQUIRED (Pest) — runtime behavior + UX contract enforcement.
|
||
|
||
**Organization**: Tasks are grouped by user story to enable independent implementation and testing of each story.
|
||
|
||
## Phase 1: Setup (Shared Infrastructure)
|
||
|
||
**Purpose**: Confirm a clean baseline before large schema + workflow changes.
|
||
|
||
- [X] T001 [P] Start local environment (Sail) and confirm containers are healthy via `vendor/bin/sail`
|
||
- [X] T002 [P] Run baseline Pest suite for current Findings/Drift flows and record failures (if any) in `tests/Feature/Drift/`
|
||
|
||
---
|
||
|
||
## Phase 2: Foundational (Blocking Prerequisites)
|
||
|
||
**Purpose**: Core data model + RBAC + settings + badges that MUST be complete before ANY user story can ship.
|
||
|
||
**⚠️ CRITICAL**: No user story work can begin until this phase is complete.
|
||
|
||
- [X] T003 Add v2 lifecycle/SLA/ownership columns to findings in `database/migrations/2026_02_24_160000_add_finding_lifecycle_v2_fields_to_findings_table.php`
|
||
- [X] T004 Add recurrence_key + SLA/query indexes for findings in `database/migrations/2026_02_24_160001_add_finding_recurrence_key_and_sla_indexes_to_findings_table.php`
|
||
- [X] T005 Update v2 Finding model (statuses, casts, relationships, open/terminal status helpers, legacy acknowledged mapping) in `app/Models/Finding.php`
|
||
- [X] T006 Update Finding status badge mapping for v2 statuses (incl legacy `acknowledged` → `triaged` surface) in `app/Support/Badges/Domains/FindingStatusBadge.php`
|
||
- [X] T007 [P] Update status badge unit tests for v2 mapping in `tests/Unit/Badges/FindingBadgesTest.php`
|
||
- [X] T008 [P] Update status badge feature tests for v2 mapping in `tests/Feature/Support/Badges/FindingBadgeTest.php`
|
||
- [X] T009 Update Finding factory defaults/states to populate new lifecycle fields for tests in `database/factories/FindingFactory.php`
|
||
- [X] T010 Add configurable Findings SLA policy setting (`findings.sla_days`) with defaults + validation + normalizer in `app/Support/Settings/SettingsRegistry.php`
|
||
- [X] T011 Expose Findings SLA policy setting in Workspace Settings UI in `app/Filament/Pages/Settings/WorkspaceSettings.php`
|
||
- [X] T012 [P] Add settings tests for findings SLA policy validation/normalization in `tests/Unit/Settings/FindingsSlaDaysSettingTest.php`
|
||
- [X] T013 Add v2 Findings capabilities (view/triage/assign/resolve/close/risk_accept) to registry in `app/Support/Auth/Capabilities.php`
|
||
- [X] T014 Map v2 Findings capabilities to tenant roles and keep `TENANT_FINDINGS_ACKNOWLEDGE` as deprecated alias for triage in `app/Services/Auth/RoleCapabilityMap.php`
|
||
- [X] T015 Update FindingPolicy to require `TENANT_FINDINGS_VIEW` and expose per-action authorization for workflow mutations in `app/Policies/FindingPolicy.php`
|
||
- [X] T016 Update review pack open-finding selection to use v2 open statuses helper in `app/Jobs/GenerateReviewPackJob.php`
|
||
- [X] T017 Update review pack fingerprint computation to use v2 open statuses helper in `app/Services/ReviewPackService.php`
|
||
|
||
**Checkpoint**: Foundation ready; user story implementation can now begin.
|
||
|
||
---
|
||
|
||
## Phase 3: User Story 1 - See Open Findings (Priority: P1) 🎯 MVP
|
||
|
||
**Goal**: Default Findings list shows open findings across all finding types (not drift-only), with quick filters and due/assignee visibility.
|
||
|
||
**Independent Test**: Seed a tenant with findings across multiple types and statuses, then verify the default list shows open workflow statuses across all types without adjusting filters.
|
||
|
||
### Tests for User Story 1
|
||
|
||
- [X] T018 [P] [US1] Add default list visibility test (open statuses across all types) in `tests/Feature/Findings/FindingsListDefaultsTest.php`
|
||
- [X] T019 [P] [US1] Add quick filter tests (Overdue, High severity, My assigned) in `tests/Feature/Findings/FindingsListFiltersTest.php`
|
||
|
||
### Implementation for User Story 1
|
||
|
||
- [X] T020 [US1] Update FindingResource access gating to use `TENANT_FINDINGS_VIEW` in `app/Filament/Resources/FindingResource.php`
|
||
- [X] T021 [US1] Remove drift-only + status=new default filters and default to open v2 statuses in `app/Filament/Resources/FindingResource.php`
|
||
- [X] T022 [US1] Add quick filters (Open/Overdue/High severity/My assigned) and due_at/assignee columns in `app/Filament/Resources/FindingResource.php`
|
||
- [X] T023 [US1] Update ListFindings filter helpers to match new filter shapes/defaults in `app/Filament/Resources/FindingResource/Pages/ListFindings.php`
|
||
|
||
**Checkpoint**: User Story 1 is functional and independently testable.
|
||
|
||
---
|
||
|
||
## Phase 4: User Story 2 - Triage, Assign, And Resolve (Priority: P1)
|
||
|
||
**Goal**: Consistent v2 workflow actions (single-record) with server-side enforcement, reasons, timestamps, audit logging, and correct 404/403 semantics.
|
||
|
||
**Independent Test**: Create an open finding, execute each allowed status transition, and verify transitions are enforced server-side, recorded with timestamps/actors, and audited.
|
||
|
||
### Tests for User Story 2
|
||
|
||
- [X] T024 [P] [US2] Add workflow service unit tests (transition validation, reasons required, due_at reset on reopen, due_at stability across severity changes while open, assignment targets must be current tenant members) in `tests/Unit/Findings/FindingWorkflowServiceTest.php`
|
||
- [X] T025 [P] [US2] Add Livewire row-action workflow tests (triage/start/assign/resolve/close/risk accept/reopen; assignee/owner picker limited to current tenant members; non-member IDs rejected) in `tests/Feature/Findings/FindingWorkflowRowActionsTest.php`
|
||
- [X] T026 [P] [US2] Add Livewire view-header workflow tests (same action set; assignee/owner picker limited to current tenant members) in `tests/Feature/Findings/FindingWorkflowViewActionsTest.php`
|
||
- [X] T027 [P] [US2] Add RBAC 404/403 matrix tests for workflow mutations (non-member 404; member missing cap 403) in `tests/Feature/Findings/FindingRbacTest.php`
|
||
- [X] T028 [P] [US2] Add audit log tests for workflow mutations (before/after + reasons + actor; assert evidence payloads are never included) in `tests/Feature/Findings/FindingAuditLogTest.php`
|
||
|
||
### Implementation for User Story 2
|
||
|
||
- [X] T029 [US2] Implement SLA resolver service (reads `findings.sla_days` via SettingsResolver) in `app/Services/Findings/FindingSlaPolicy.php`
|
||
- [X] T030 [US2] Implement workflow transition service (enforced map + timestamps + reason validation + due_at semantics + assignee/owner tenant membership validation + AuditLogger) in `app/Services/Findings/FindingWorkflowService.php`
|
||
- [X] T031 [US2] Update Finding model legacy methods/compat helpers for v2 workflow (keep legacy `acknowledged` readable) in `app/Models/Finding.php`
|
||
- [X] T032 [US2] Replace acknowledge row action with v2 workflow row actions (UiEnforcement, confirmations for destructive, assignee/owner options limited to current tenant members) in `app/Filament/Resources/FindingResource.php`
|
||
- [X] T033 [US2] Add v2 workflow actions to ViewFinding header actions (same capability gates + confirmations; assignee/owner options limited to current tenant members) in `app/Filament/Resources/FindingResource/Pages/ViewFinding.php`
|
||
- [X] T034 [US2] Expand ViewFinding infolist to show lifecycle + assignment + SLA fields (first/last seen, times_seen, due_at, assignee/owner, timestamps, reasons; preserve historical assignee/owner display even if membership is later removed) in `app/Filament/Resources/FindingResource.php`
|
||
- [X] T035 [US2] Update FindingResource ActionSurface declaration to satisfy v2 UI Action Matrix (detail header actions now present) in `app/Filament/Resources/FindingResource.php`
|
||
- [X] T036 [US2] Update legacy drift row-action test from acknowledge to triage in `tests/Feature/Drift/DriftAcknowledgeTest.php`
|
||
- [X] T037 [US2] Update legacy drift row-action authorization test from acknowledge to triage capability semantics in `tests/Feature/Drift/DriftAcknowledgeAuthorizationTest.php`
|
||
- [X] T038 [US2] Update Finding model behavior tests for v2 workflow semantics in `tests/Feature/Models/FindingResolvedTest.php`
|
||
|
||
**Checkpoint**: Single-record workflow is functional, enforced, and audited.
|
||
|
||
---
|
||
|
||
## Phase 5: User Story 3 - SLA Due Visibility And Alerts (Priority: P1)
|
||
|
||
**Goal**: SLA due alert producer emits one tenant-level event when newly-overdue open findings exist; AlertRule UI allows selecting “SLA due”.
|
||
|
||
**Independent Test**: Create newly-overdue open findings for a tenant, run alert evaluation, and verify a single tenant-level SLA due event is produced and can match an enabled alert rule.
|
||
|
||
### Tests for User Story 3
|
||
|
||
- [X] T039 [P] [US3] Add SLA due producer tests (tenant-level aggregation + newly overdue gating using `window_start`; payload includes overdue_total + overdue_by_severity; idempotent per tenant+window) in `tests/Feature/Alerts/SlaDueAlertTest.php`
|
||
- [X] T040 [P] [US3] Update test asserting sla_due is hidden to now assert it is selectable in `tests/Feature/ReviewPack/ReviewPackPruneTest.php`
|
||
- [X] T041 [P] [US3] Extend event type options test coverage to include sla_due label in `tests/Feature/EntraAdminRoles/AdminRolesAlertIntegrationTest.php`
|
||
|
||
### Implementation for User Story 3
|
||
|
||
- [X] T042 [US3] Implement `slaDueEvents()` producer and include in EvaluateAlertsJob event list in `app/Jobs/Alerts/EvaluateAlertsJob.php` (use `window_start` from alert evaluation; `fingerprint_key` stable per tenant+window; event metadata contains overdue_total + overdue_by_severity)
|
||
- [X] T043 [US3] Re-enable `sla_due` option in AlertRuleResource event type options in `app/Filament/Resources/AlertRuleResource.php`
|
||
|
||
**Checkpoint**: SLA due alerting is end-to-end functional.
|
||
|
||
---
|
||
|
||
## Phase 6: User Story 4 - Recurrence Reopens (Priority: P2)
|
||
|
||
**Goal**: Recurring findings reopen (from resolved only), lifecycle counters update, drift uses stable recurrence identity, and stale drift auto-resolves.
|
||
|
||
**Independent Test**: Simulate a finding being resolved and then being detected again, verifying it transitions to `reopened`, counters update, and due date resets.
|
||
|
||
### Tests for User Story 4
|
||
|
||
- [X] T044 [P] [US4] Add drift recurrence tests (resolved→reopened; closed/risk_accepted stays terminal; times_seen increments; due_at resets; concurrency: do not auto-reopen if `resolved_at` is after observation time) in `tests/Feature/Findings/FindingRecurrenceTest.php`
|
||
- [X] T045 [P] [US4] Add stale drift auto-resolve test (not detected in latest run → resolved_reason=no_longer_detected) in `tests/Feature/Findings/DriftStaleAutoResolveTest.php`
|
||
- [X] T046 [P] [US4] Update permission posture generator tests for reopened + lifecycle fields in `tests/Feature/PermissionPosture/PermissionPostureFindingGeneratorTest.php`
|
||
- [X] T047 [P] [US4] Update Entra admin roles generator tests for reopened + lifecycle fields in `tests/Feature/EntraAdminRoles/EntraAdminRolesFindingGeneratorTest.php`
|
||
- [X] T048 [P] [US4] Update alert evaluation tests to include status=reopened where appropriate in `tests/Feature/Alerts/PermissionMissingAlertTest.php`
|
||
- [X] T049 [P] [US4] Update alert evaluation tests to include status=reopened where appropriate in `tests/Feature/EntraAdminRoles/AdminRolesAlertIntegrationTest.php`
|
||
|
||
### Implementation for User Story 4
|
||
|
||
- [X] T050 [US4] Add recurrence_key computation helpers (dimension + subject identity) for drift findings in `app/Services/Drift/DriftFindingGenerator.php`
|
||
- [X] T051 [US4] Upsert drift findings by `(tenant_id, recurrence_key)` and set canonical `fingerprint = recurrence_key` in `app/Services/Drift/DriftFindingGenerator.php`
|
||
- [X] T052 [US4] Maintain lifecycle fields (first/last seen, times_seen) and set due_at on create/reset on reopen for drift findings in `app/Services/Drift/DriftFindingGenerator.php` (do not retroactively change due_at on severity changes)
|
||
- [X] T053 [US4] Implement drift auto-reopen only from resolved → reopened (closed/risk_accepted remain terminal; still update seen fields; do not reopen if `resolved_at` is after observation time) in `app/Services/Drift/DriftFindingGenerator.php`
|
||
- [X] T054 [US4] Implement stale drift auto-resolve for open drift findings not seen in run (resolved_reason=no_longer_detected) in `app/Services/Drift/DriftFindingGenerator.php`
|
||
- [X] T055 [US4] Update permission posture generator to set lifecycle fields + due_at semantics + reopened status handling in `app/Services/PermissionPosture/PermissionPostureFindingGenerator.php` (do not retroactively change due_at on severity changes)
|
||
- [X] T056 [US4] Update Entra admin roles generator to set lifecycle fields + due_at semantics + reopened status handling in `app/Services/EntraAdminRoles/EntraAdminRolesFindingGenerator.php` (do not retroactively change due_at on severity changes)
|
||
- [X] T057 [US4] Update EvaluateAlertsJob existing producers to include status `reopened` where “new/re-detected” should alert in `app/Jobs/Alerts/EvaluateAlertsJob.php`
|
||
|
||
**Checkpoint**: Recurrence behavior is correct and drift stops creating “new row per re-drift” noise.
|
||
|
||
---
|
||
|
||
## Phase 7: User Story 6 - Backfill Existing Findings (Priority: P2)
|
||
|
||
**Goal**: One-time backfill/consolidation operation upgrades legacy findings to v2 fields, maps acknowledged→triaged, sets due_at from backfill time, and consolidates drift duplicates.
|
||
|
||
**Independent Test**: Seed legacy findings (missing lifecycle fields, `acknowledged` status, drift duplicates), run the backfill operation, and verify fields are populated, statuses are mapped, and duplicates are consolidated.
|
||
|
||
### Tests for User Story 6
|
||
|
||
- [X] T058 [P] [US6] Add backfill tests (ack→triaged, lifecycle fields, due_at from backfill time, drift duplicate consolidation) in `tests/Feature/Findings/FindingBackfillTest.php`
|
||
|
||
### Implementation for User Story 6
|
||
|
||
- [X] T059 [US6] Register operation type `findings.lifecycle.backfill` in OperationCatalog (label + duration) in `app/Support/OperationCatalog.php`
|
||
- [X] T060 [US6] Add backfill artisan command entrypoint (OperationRun-backed; deduped; dispatches job) in `app/Console/Commands/TenantpilotBackfillFindingLifecycle.php`
|
||
- [X] T061 [US6] Implement backfill job skeleton (lock + chunking + summary_counts updates via OperationRunService) in `app/Jobs/BackfillFindingLifecycleJob.php`
|
||
- [X] T062 [US6] Implement backfill mapping (acknowledged→triaged; set first_seen_at/last_seen_at/times_seen; due_at from backfill time + SLA days for legacy open) in `app/Jobs/BackfillFindingLifecycleJob.php`
|
||
- [X] T063 [US6] Implement drift recurrence_key computation for legacy drift evidence in `app/Jobs/BackfillFindingLifecycleJob.php`
|
||
- [X] T064 [US6] Implement drift duplicate consolidation (canonical row; duplicates resolved_reason=consolidated_duplicate; clear recurrence_key) in `app/Jobs/BackfillFindingLifecycleJob.php`
|
||
- [X] T065 [US6] Add tenant-context Filament action to trigger backfill with Ops-UX queued toast + View run link in `app/Filament/Resources/FindingResource/Pages/ListFindings.php`
|
||
|
||
**Checkpoint**: Backfill operation is observable, safe, and upgrades legacy data correctly.
|
||
|
||
---
|
||
|
||
## Phase 8: User Story 5 - Bulk Manage Findings (Priority: P3)
|
||
|
||
**Goal**: Bulk workflow actions (triage/assign/resolve/close/risk accept) are safe, audited, and efficient for high volumes.
|
||
|
||
**Independent Test**: Select multiple findings and run each bulk action, verifying that all selected findings update consistently and each change is audited.
|
||
|
||
### Tests for User Story 5
|
||
|
||
- [X] T066 [P] [US5] Add bulk workflow action tests (bulk triage/assign/resolve/close/risk accept + audit per record; cover >=100 records for at least one bulk action) in `tests/Feature/Findings/FindingBulkActionsTest.php`
|
||
- [X] T067 [P] [US5] Update legacy bulk acknowledge selected test to bulk triage selected in `tests/Feature/Drift/DriftBulkAcknowledgeTest.php`
|
||
- [X] T068 [P] [US5] Update legacy “acknowledge all matching” test to “triage all matching” in `tests/Feature/Drift/DriftBulkAcknowledgeAllMatchingTest.php`
|
||
- [X] T069 [P] [US5] Update legacy bulk authorization tests to new bulk action names/capabilities in `tests/Feature/Drift/DriftBulkAcknowledgeAuthorizationTest.php`
|
||
- [X] T070 [P] [US5] Update legacy “all matching requires typed confirmation >100” test to triage-all-matching in `tests/Feature/Drift/DriftBulkAcknowledgeAllMatchingConfirmationTest.php`
|
||
|
||
### Implementation for User Story 5
|
||
|
||
- [X] T071 [US5] Replace bulk acknowledge actions with bulk workflow actions (UiEnforcement + confirmations + AuditLogger) in `app/Filament/Resources/FindingResource.php`
|
||
- [X] T072 [US5] Implement “Triage all matching” header action (typed confirm >100; audited; respects current filters) in `app/Filament/Resources/FindingResource/Pages/ListFindings.php`
|
||
|
||
**Checkpoint**: Bulk management is safe and supports high-volume operations.
|
||
|
||
---
|
||
|
||
## Phase 9: Polish & Cross-Cutting Concerns
|
||
|
||
**Purpose**: Repo hygiene and end-to-end validation.
|
||
|
||
- [X] T073 [P] Run Pint on touched files and fix formatting via `./vendor/bin/sail bin pint --dirty`
|
||
- [X] T074 Run full test suite via Sail and fix failures in `tests/`
|
||
- [X] T075 Run quickstart validation for feature 111 in `specs/111-findings-workflow-sla/quickstart.md`
|
||
- [X] T076 Add post-backfill hardening migration to enforce NOT NULL on finding seen fields (`first_seen_at`, `last_seen_at`, `times_seen`) in `database/migrations/2026_02_24_160002_enforce_not_null_on_finding_seen_fields.php`
|
||
- [X] T077 [P] Verify no new external API calls were introduced (Graph/HTTP) by scanning touched files for `GraphClientInterface` usage and direct HTTP clients (e.g., `Http::`, `Guzzle`) and confirming all Findings/Alerts logic remains DB-only
|
||
|
||
---
|
||
|
||
## Dependencies & Execution Order
|
||
|
||
### Phase Dependencies
|
||
|
||
- **Setup (Phase 1)**: No dependencies
|
||
- **Foundational (Phase 2)**: Depends on Setup completion; BLOCKS all user stories
|
||
- **User Stories (Phase 3+)**:
|
||
- US1/US2/US3 can proceed in parallel after Foundational
|
||
- US4 depends on Foundational (and should align with US2 status model)
|
||
- US6 depends on Foundational and should follow US4’s recurrence_key definition for drift
|
||
- US5 depends on US2 (bulk actions reuse the workflow service + action builders)
|
||
- **Polish (Phase 9)**: Depends on all desired user stories being complete
|
||
|
||
### User Story Dependencies
|
||
|
||
- **US1 (P1)**: No dependencies besides Foundational
|
||
- **US2 (P1)**: No dependencies besides Foundational
|
||
- **US3 (P1)**: Depends on Foundational (due_at exists + open status semantics defined)
|
||
- **US4 (P2)**: Depends on Foundational (v2 statuses + lifecycle fields exist)
|
||
- **US6 (P2)**: Depends on Foundational; recommended after US4 so drift recurrence_key matches generator semantics
|
||
- **US5 (P3)**: Depends on US2 (workflow service + single-record semantics must be correct first)
|
||
|
||
### Within Each User Story
|
||
|
||
- Tests MUST be written and FAIL before implementation
|
||
- Core services/helpers before UI actions
|
||
- UI actions before bulk mutations
|
||
- Story complete before moving to next priority
|
||
|
||
---
|
||
|
||
## Parallel Examples
|
||
|
||
### Parallel Example: User Story 1
|
||
|
||
```bash
|
||
# Tests can run in parallel:
|
||
T018 # default list visibility test
|
||
T019 # quick filter tests
|
||
|
||
# Implementation can be split by file:
|
||
T020 # resource access gating
|
||
T023 # list filter helper adjustments
|
||
```
|
||
|
||
### Parallel Example: User Story 2
|
||
|
||
```bash
|
||
# Tests can run in parallel:
|
||
T024 # workflow service unit tests
|
||
T025 # row action tests
|
||
T026 # view header action tests
|
||
T027 # RBAC semantics tests
|
||
T028 # audit log tests
|
||
|
||
# Implementation can be split by file:
|
||
T029 # SLA resolver service
|
||
T033 # view infolist expansion
|
||
T036 # legacy drift test migration
|
||
```
|
||
|
||
### Parallel Example: User Story 3
|
||
|
||
```bash
|
||
# Tests can run in parallel:
|
||
T039 # SLA due producer tests
|
||
T040 # AlertRuleResource option visibility test update
|
||
T041 # alert options label assertions
|
||
```
|
||
|
||
### Parallel Example: User Story 4
|
||
|
||
```bash
|
||
# Generator test updates can run in parallel:
|
||
T046 # permission posture generator tests
|
||
T047 # entra admin roles generator tests
|
||
T048 # permission_missing alert evaluation tests
|
||
T049 # entra admin roles alert evaluation tests
|
||
```
|
||
|
||
### Parallel Example: User Story 6
|
||
|
||
```bash
|
||
# Backfill code and tests can be split:
|
||
T058 # backfill tests
|
||
T059 # OperationCatalog registration
|
||
T060 # artisan command entrypoint
|
||
```
|
||
|
||
### Parallel Example: User Story 5
|
||
|
||
```bash
|
||
# Bulk tests can run in parallel with legacy test migrations:
|
||
T066 # bulk actions tests
|
||
T067 # migrate bulk-selected test
|
||
T068 # migrate triage-all-matching test
|
||
T069 # migrate bulk auth tests
|
||
```
|
||
|
||
---
|
||
|
||
## Implementation Strategy
|
||
|
||
### MVP First (User Story 1 Only)
|
||
|
||
1. Complete Phase 1: Setup
|
||
2. Complete Phase 2: Foundational
|
||
3. Complete Phase 3: User Story 1
|
||
4. STOP and validate User Story 1 independently
|
||
|
||
### Incremental Delivery
|
||
|
||
1. Setup + Foundational
|
||
2. US1 → validate
|
||
3. US2 → validate
|
||
4. US3 → validate
|
||
5. US4 + US6 → validate
|
||
6. US5 → validate
|
||
|
||
---
|
||
|
||
## Notes
|
||
|
||
- `[P]` tasks = can run in parallel (different files, no dependencies)
|
||
- `[US#]` label maps tasks to user stories for traceability
|
||
- Destructive-like actions MUST use `->requiresConfirmation()` and be capability-gated (UiEnforcement)
|
||
- Operations MUST comply with Ops-UX 3-surface contract when OperationRun is used
|