18 KiB
| description |
|---|
| Executable task breakdown for Spec 116 implementation |
Tasks: 116 — Baseline Drift Engine (Final Architecture)
Input: Design documents in specs/116-baseline-drift-engine/ (plan.md, spec.md, research.md, data-model.md, quickstart.md, specs/116-baseline-drift-engine/contracts/openapi.yaml)
Tests: REQUIRED (Pest) for all runtime behavior changes.
Phase 1: Setup (Shared Infrastructure)
Purpose: Ensure local dev + feature artifacts are ready.
- T001 Re-run Speckit prerequisites check via
.specify/scripts/bash/check-prerequisites.sh --json(referencesspecs/116-baseline-drift-engine/plan.md) - T002 Run required agent context update via
.specify/scripts/bash/update-agent-context.sh copilot(required byspecs/116-baseline-drift-engine/plan.md) - T003 Ensure Sail + migrations are up for local validation (references
vendor/bin/sail,docker-compose.yml, anddatabase/migrations/) - T004 [P] Re-validate Spec 116 UI Action Matrix: confirm “no changes required” OR update the matrix table in
specs/116-baseline-drift-engine/spec.md - T005 [P] Document the mapping of “Policy Types” / “Foundations” selectors to config sources in
specs/116-baseline-drift-engine/plan.md(and adjustconfig/tenantpilot.php/app/Support/Inventory/InventoryPolicyTypeMeta.phpif mismatched)
Phase 2: Foundational (Blocking Prerequisites)
Purpose: Shared primitives that all user stories depend on (scope semantics, data defaults, and UI inputs).
Independent Test: Baseline Profile can be created with the new scope shape, and scope defaults expand deterministically.
- T006 Update baseline scope schema + default semantics (policy_types excludes foundations by default; foundation_types defaults to none) in
app/Support/Baselines/BaselineScope.php - T007 [P] Update BaselineProfile default scope shape to include
foundation_typesindatabase/factories/BaselineProfileFactory.php - T008 [P] Ensure BaselineProfile scope casting/normalization supports
foundation_typessafely inapp/Models/BaselineProfile.php - T009 [P] Create focused tests for scope expansion defaults (empty policy_types => supported excluding foundations; empty foundation_types => none) in
tests/Unit/Baselines/BaselineScopeTest.php - T010 Update BaselineProfile Create/Edit form (UX-001 Main/Aside), scope picker (Policy Types + Foundations), and infolist display semantics in
app/Filament/Resources/BaselineProfileResource.php - T011 [P] Update create-page scope normalization to persist both
policy_typesandfoundation_typesinapp/Filament/Resources/BaselineProfileResource/Pages/CreateBaselineProfile.php - T012 [P] Update edit-page scope normalization to persist both
policy_typesandfoundation_typesinapp/Filament/Resources/BaselineProfileResource/Pages/EditBaselineProfile.php - T013 Run focused verification for foundational scope/UI changes with
vendor/bin/sail artisan test --compact tests/Unit/Baselines/BaselineScopeTest.php(referencestests/Unit/Baselines/BaselineScopeTest.php)
Checkpoint: Scope semantics + scope UI are correct and test-covered.
Phase 3: User Story 1 — Capture and compare a baseline with stable findings (Priority: P1)
Goal: Define the v1 meta-fidelity hash contract, use it for capture/compare, and make baseline-compare findings snapshot-scoped stable identities.
Independent Test: Capture a snapshot, run compare twice with the same inputs, and verify finding identity stability + lifecycle idempotency.
Tests for User Story 1 (write first)
- T014 [P] [US1] Update capture tests for effective_scope recording + contract-based hashing in
tests/Feature/Baselines/BaselineCaptureTest.php - T015 [P] [US1] Update compare findings tests to assert recurrence-key-based identity (no hashes in fingerprint) + lifecycle idempotency per run +
run.context.findings.counts_by_change_typeis present and accurate intests/Feature/Baselines/BaselineCompareFindingsTest.php - T016 [P] [US1] Add unit test for InventoryMetaContract normalization stability (ordering, missing fields, nullability) in
tests/Unit/Baselines/InventoryMetaContractTest.php - T017 [P] [US1] Add/adjust preconditions tests for default snapshot selection via
baseline_profiles.active_snapshot_id(“latest successful”) + explicit snapshot override (perspecs/116-baseline-drift-engine/contracts/openapi.yaml) intests/Feature/Baselines/BaselineComparePreconditionsTest.php - T018 [P] [US1] Add test that re-capturing (new snapshot id) produces new finding identities (snapshot-scoped) in
tests/Feature/Baselines/BaselineCompareFindingsTest.php - T019 [P] [US1] Extend compare-start auth tests to cover: unauthenticated→302, authenticated non-member/not entitled tenant access→404, authenticated member missing
tenant.sync→403, and success path intests/Feature/Filament/BaselineCompareLandingStartSurfaceTest.php - T020 [P] [US1] Add capture-start auth tests for Baseline Profile “Capture Snapshot” action to cover: unauthenticated→302, authenticated non-member/not entitled workspace access→404, authenticated member missing
workspace_baselines.manage→403, and success path intests/Feature/Filament/BaselineProfileCaptureStartSurfaceTest.php - T021 [P] [US1] Confirm baseline profile create/edit mutation surfaces have positive + negative authorization coverage (302/404/403 semantics) and update for new scope shape if needed in
tests/Feature/Baselines/BaselineProfileAuthorizationTest.php
Implementation for User Story 1
- T022 [US1] Create + implement Inventory Meta Contract builder (normalized whitelist inputs; deterministic ordering) in
app/Services/Baselines/InventoryMetaContract.php - T023 [US1] Update snapshot hashing to hash ONLY the meta contract output (not entire meta_jsonb) in
app/Services/Baselines/BaselineSnapshotIdentity.php - T024 [US1] Update baseline capture job to compute/store
baseline_hashvia InventoryMetaContract and persistmeta_jsonbevidence (meta_contract,fidelity,source,observed_at,observed_operation_run_id) inapp/Jobs/CaptureBaselineSnapshotJob.php - T025 [US1] Ensure capture OperationRun context records
effective_scope.*(policy_types, foundation_types, all_types, foundations_included) inapp/Services/Baselines/BaselineCaptureService.php - T026 [US1] Update baseline compare job to compute
current_hashvia InventoryMetaContract consistently with capture inapp/Jobs/CompareBaselineToTenantJob.php - T027 [US1] Switch baseline-compare finding identity to recurrence key derived from (tenant, baseline_snapshot_id, policy_type, external_id, change_type) and set
fingerprint == recurrence_keyinapp/Jobs/CompareBaselineToTenantJob.php - T028 [US1] Enforce per-run idempotency by using
findings.current_operation_run_id(and/or evidence) sotimes_seenincrements at most once per run identity inapp/Jobs/CompareBaselineToTenantJob.php - T029 [US1] Write compare audit context fields (baseline ids +
findings.counts_by_change_type) onto the compare OperationRun context inapp/Jobs/CompareBaselineToTenantJob.php
Checkpoint: US1 tests pass: vendor/bin/sail artisan test --compact tests/Feature/Baselines/BaselineCaptureTest.php tests/Feature/Baselines/BaselineCompareFindingsTest.php tests/Feature/Baselines/BaselineComparePreconditionsTest.php.
Phase 4: User Story 2 — Coverage warnings prevent misleading missing-policy findings (Priority: P1)
Goal: Persist per-type coverage from inventory sync and enforce the coverage guard in baseline compare (uncovered types emit zero findings; compare outcome is partial with warnings).
Independent Test: Run compare with missing coverage for some in-scope types; verify partial outcome + zero findings for uncovered types.
Tests for User Story 2 (write first)
- T030 [P] [US2] Extend inventory sync tests to assert per-type coverage payload is written to OperationRun context in
tests/Feature/Inventory/InventorySyncStartSurfaceTest.php - T031 [P] [US2] Create coverage-guard regression test: (a) uncovered types => no findings of any kind; (b) no completed inventory sync run / missing coverage payload => fail-safe zero findings; (c) effective scope expands to zero types => warnings + zero findings; outcome partially_succeeded; covered types still emit findings in
tests/Feature/Baselines/BaselineCompareCoverageGuardTest.php
Implementation for User Story 2
- T032 [US2] Persist inventory sync coverage payload into latest inventory sync run context (
inventory.coverage.policy_types+inventory.coverage.foundation_types) inapp/Services/Inventory/InventorySyncService.php - T033 [P] [US2] Create a small coverage parser/helper to normalize context payload for downstream consumers in
app/Support/Inventory/InventoryCoverage.php - T034 [US2] Update baseline compare to read latest inventory sync run coverage, compute uncovered types, skip emission for uncovered types, and write coverage details into compare run context in
app/Jobs/CompareBaselineToTenantJob.php - T035 [US2] Treat missing coverage proof (no completed inventory sync run, or unreadable/missing coverage payload) as uncovered-for-all-types (fail-safe): emit zero findings and mark outcome partially_succeeded (via OperationRunService), setting numeric summary_counts (including errors_recorded) using canonical keys only in
app/Jobs/CompareBaselineToTenantJob.php
Note (canonical warning magnitude): For (c) “effective scope expands to zero types”, the compare MUST still surface a warning and therefore MUST set summary_counts.errors_recorded = 1 (even though uncovered-types count is 0), to keep the warning visible under the numeric-only summary_counts contract.
Checkpoint: US2 tests pass: vendor/bin/sail artisan test --compact tests/Feature/Inventory/InventorySyncStartSurfaceTest.php tests/Feature/Baselines/BaselineCompareCoverageGuardTest.php.
Phase 5: User Story 3 — Operators can understand scope, coverage, and fidelity in the UI (Priority: P2)
Goal: Surface effective scope, coverage status, and fidelity (meta) in Baseline Compare landing + drift findings surfaces.
Independent Test: Execute a compare with and without coverage warnings; verify UI surfaces show badge/banner + scope/coverage/fidelity context.
Tests for User Story 3 (write first)
- T036 [P] [US3] Update Baseline Compare landing tests to cover warning/coverage state rendering inputs (stats DTO fields) in
tests/Feature/Filament/BaselineCompareLandingStartSurfaceTest.php - T037 [P] [US3] Update drift landing comparison-info tests to include coverage/fidelity context when source is baseline compare in
tests/Feature/Drift/DriftLandingShowsComparisonInfoTest.php
Implementation for User Story 3
- T038 [US3] Extend BaselineCompareStats DTO to include coverage status + uncovered types summary + fidelity indicator sourced from latest compare run context in
app/Support/Baselines/BaselineCompareStats.php - T039 [US3] Wire new stats fields into the BaselineCompareLanding Livewire page state in
app/Filament/Pages/BaselineCompareLanding.php - T040 [US3] Render coverage badge + warning banner + fidelity label on the landing view in
resources/views/filament/pages/baseline-compare-landing.blade.php - T041 [US3] Add a findings-list banner when latest baseline compare run had uncovered types (linking to the run) in
app/Filament/Resources/FindingResource/Pages/ListFindings.php - T042 [US3] Ensure run detail already shows context; if needed, add baseline compare “Coverage” summary entry for readability in
app/Filament/Resources/OperationRunResource.php
Checkpoint: US3 tests pass: vendor/bin/sail artisan test --compact tests/Feature/Filament/BaselineCompareLandingStartSurfaceTest.php tests/Feature/Drift/DriftLandingShowsComparisonInfoTest.php.
Phase 6: Polish & Cross-Cutting Concerns
Purpose: Preserve operability semantics (auto-close, stats), Ops-UX compliance, and fast regression feedback.
- T043 Confirm baseline compare stats remain profile-grouped via
scope_key = baseline_profile:{id}after identity change inapp/Support/Baselines/BaselineCompareStats.php - T044 Ensure baseline auto-close behavior still works with snapshot-scoped identities (no stale open findings after successful compare) in
app/Services/Baselines/BaselineAutoCloseService.php - T045 [P] Update/verify auto-close regression test remains valid after identity change in
tests/Feature/Baselines/BaselineOperabilityAutoCloseTest.php - T046 [P] Add/extend guard test asserting OperationRun summary_counts are numeric-only and keys are limited to
OperationSummaryKeys::all()for baseline capture/compare runs intests/Feature/Baselines/BaselineCompareFindingsTest.php - T047 [P] Add Spec 116 “One engine” regression guard to prevent legacy compare/hash paths (no
DriftHasher::fingerprint()use for baseline compare; capture/compare hashing flows throughInventoryMetaContract) intests/Feature/Guards/Spec116OneEngineGuardTest.php - T048 [P] Add Spec 116 performance regression guard (compare is DB-only; no Graph/hydration calls; compare processes snapshot + inventory via chunking) in
tests/Feature/Baselines/BaselineComparePerformanceGuardTest.php - T049 [P] Verify OPS-UX-GUARD-001 remains satisfied for Spec 116 code paths; if guard tests fail, fix code (preferred) or update guard expectations intentionally in
tests/Feature/OpsUx/Constitution/DirectStatusTransitionGuardTest.php,tests/Feature/OpsUx/Constitution/JobDbNotificationGuardTest.php, andtests/Feature/OpsUx/Constitution/LegacyNotificationGuardTest.php - T050 [P] Create Baseline Profile archive action tests (confirmation required + RBAC 403/404 semantics + success path) in
tests/Feature/Baselines/BaselineProfileArchiveActionTest.php - T051 [P] Ensure archive action is declared in Action Surface slots and remains “More” row action only (max 2 visible row actions) in
tests/Feature/Guards/ActionSurfaceContractTest.php - T052 Run baseline-focused test pack for Spec 116:
vendor/bin/sail artisan test --compact tests/Feature/Baselines/(referencestests/Feature/Baselines/) - T053 Run Ops-UX guard test pack:
vendor/bin/sail artisan test --compact --group=ops-ux(referencestests/Feature/OpsUx/Constitution/) - T054 Run Pint formatter on changed files:
vendor/bin/sail pint --dirty --format agent(referencesapp/andtests/) - T055 Validate developer quickstart still matches real behavior (update if needed) in
specs/116-baseline-drift-engine/quickstart.md
Dependencies & Execution Order
User Story Dependency Graph
graph TD
P1[Phase 1: Setup] --> P2[Phase 2: Foundational]
P2 --> US1[US1: Stable capture/compare + findings]
US1 --> US2[US2: Coverage guard]
US2 --> US3[US3: UI context]
US2 --> P6[Phase 6: Polish]
US3 --> P6
Phase Dependencies
- Setup (Phase 1) → blocks nothing, but should be done first.
- Foundational (Phase 2) → BLOCKS all user stories.
- US1 / US2 / US3 → can start after Foundational; in practice US1 then US2 reduces merge conflicts in
app/Jobs/CompareBaselineToTenantJob.php. - Polish (Phase 6) → after US1/US2 at minimum.
User Story Dependencies
- US1 (P1): Depends on Phase 2.
- US2 (P1): Depends on Phase 2; touches compare + inventory sync. Strongly recommended after US1 to keep compare changes coherent.
- US3 (P2): Depends on US2 (needs coverage context) and Phase 2.
Parallel Example: User Story 1
# Tests can be updated in parallel:
Task: "Update capture tests" (tests/Feature/Baselines/BaselineCaptureTest.php)
Task: "Update compare findings tests" (tests/Feature/Baselines/BaselineCompareFindingsTest.php)
Task: "Update compare preconditions tests" (tests/Feature/Baselines/BaselineComparePreconditionsTest.php)
Task: "Update compare-start auth tests" (tests/Feature/Filament/BaselineCompareLandingStartSurfaceTest.php)
Task: "Add capture-start auth tests" (tests/Feature/Filament/BaselineProfileCaptureStartSurfaceTest.php)
# Implementation can be split with care (different files):
Task: "Implement InventoryMetaContract" (app/Services/Baselines/InventoryMetaContract.php)
Task: "Update CaptureBaselineSnapshotJob hashing" (app/Jobs/CaptureBaselineSnapshotJob.php)
Task: "Update CompareBaselineToTenantJob identity + hashing" (app/Jobs/CompareBaselineToTenantJob.php)
Parallel Example: User Story 2
# Tests can be updated in parallel:
Task: "Assert inventory sync coverage is written" (tests/Feature/Inventory/InventorySyncStartSurfaceTest.php)
Task: "Add coverage-guard regression test" (tests/Feature/Baselines/BaselineCompareCoverageGuardTest.php)
# Implementation can be split (different files):
Task: "Write coverage payload to sync run context" (app/Services/Inventory/InventorySyncService.php)
Task: "Implement coverage parser helper" (app/Support/Inventory/InventoryCoverage.php)
Parallel Example: User Story 3
# Tests can be updated in parallel:
Task: "Update landing test for coverage/fidelity state" (tests/Feature/Filament/BaselineCompareLandingStartSurfaceTest.php)
Task: "Update drift landing comparison-info test" (tests/Feature/Drift/DriftLandingShowsComparisonInfoTest.php)
# Implementation can be split (different files):
Task: "Extend stats DTO + wiring" (app/Support/Baselines/BaselineCompareStats.php)
Task: "Render landing banner/badges" (resources/views/filament/pages/baseline-compare-landing.blade.php)
Task: "Add findings list banner" (app/Filament/Resources/FindingResource/Pages/ListFindings.php)
Implementation Strategy
Suggested MVP Scope
- MVP: US1 (stable capture/compare + stable findings).
- Trust-critical follow-up: US2 (coverage guard) is also P1 in the spec and should typically ship immediately after MVP.
Incremental Delivery
- Phase 1 + Phase 2
- US1 → validate stable identities + contract hashing
- US2 → validate coverage guard + partial outcome semantics
- US3 → validate operator clarity (badges/banners)
- Phase 6 → ensure operability + guards + formatting
Notes
- [P] tasks = parallelizable (different files, minimal dependency)
- All tasks include explicit file targets for fast handoff
- Destructive actions already require confirmation (Filament actions use
->requiresConfirmation()); keep that invariant when editing UI surfaces - Spec 116 includes a v2 section for future work; v2 requirements are explicitly deferred and are not covered by this tasks list