12 KiB
Tasks: 094 Assignment Operations Observability Hardening
Input: Design documents from specs/094-assignment-ops-observability-hardening/
Prerequisites: specs/094-assignment-ops-observability-hardening/plan.md (required), specs/094-assignment-ops-observability-hardening/spec.md (required), specs/094-assignment-ops-observability-hardening/research.md, specs/094-assignment-ops-observability-hardening/data-model.md, specs/094-assignment-ops-observability-hardening/contracts/assignment-ops.openapi.yaml
Tests: Required (Pest) for runtime behavior changes.
Phase 1: Setup (Shared Infrastructure)
Purpose: Ensure the implementation has the correct local workflow context and that the existing conventions to extend are understood.
- T001 Run SpecKit prerequisites check via
.specify/scripts/bash/check-prerequisites.sh --jsonand recordFEATURE_DIR+AVAILABLE_DOCSfor this feature - T002 Review existing OperationRun tracking conventions in
app/Jobs/Middleware/TrackOperationRun.phpandapp/Services/OperationRunService.php - T003 Review existing OperationRun test patterns in
tests/Feature/TrackOperationRunMiddlewareTest.phpandtests/Feature/OperationRunServiceTest.php
Phase 2: Foundational (Blocking Prerequisites)
Purpose: Confirm the persistence + dedupe primitives exist and match the spec’s requirements before adding new run types and audits.
⚠️ CRITICAL: Complete this phase before implementing any user story.
- T004 Verify
operation_runsschema supportsrun_identity_hash,summary_counts, andfailure_summaryperdatabase/migrations/2026_01_16_180642_create_operation_runs_table.php - T005 Verify active-run dedupe constraints exist and align with
OperationRunService::ensureRunWithIdentity()inapp/Services/OperationRunService.phpanddatabase/migrations/2026_02_10_004939_add_unique_index_for_backup_schedule_scheduled_operation_runs.php - T006 [P] Identify existing restore audit logging expectations to preserve by reviewing
tests/Feature/RestoreAuditLoggingTest.phpandapp/Services/Intune/AuditLogger.php - T034 [P] Verify active-run dedupe constraints cover the new assignment run identity shapes; if not, add a DB-enforced partial unique index/migration + regression test for dedupe behavior
Checkpoint: Foundation ready — user story work can begin.
Phase 3: User Story 1 — Observe assignment operations end-to-end (Priority: P1) 🎯 MVP
Goal: Assignment fetch (during backup capture when assignments are included) and assignment restore (during restore execution) are observable via OperationRun with stable failure codes, normalized reason_code, correct counters, and exactly one audit log entry per assignment restore execution.
Independent Test: Trigger (a) backup creation with assignments included and (b) a restore that applies assignments; verify operation_runs rows exist and audit_logs contains exactly one assignment-restore summary entry.
Tests for User Story 1 (write first)
- T007 [P] [US1] Add test for assignment restore OperationRun lifecycle + counters in
tests/Feature/Operations/AssignmentRestoreOperationRunTest.php - T008 [P] [US1] Add test for stable failure
code+ normalizedreason_codeon assignment restore failure intests/Feature/Operations/AssignmentRestoreOperationRunFailureTest.php - T009 [P] [US1] Add test that assignment restore writes exactly one audit log entry per restore execution in
tests/Feature/Audit/AssignmentRestoreAuditSummaryTest.php - T010 [P] [US1] Add test that backup capture with assignments included records an assignment-fetch OperationRun entry for the resulting backup item in
tests/Feature/Operations/AssignmentFetchOperationRunTest.php - T035 [P] [US1] Add regression test for Monitoring pages being DB-only at render time (no outbound calls) when viewing Operations list + detail in
tests/Feature/Monitoring/OperationsDbOnlyRenderTest.php
Implementation for User Story 1
- T011 [US1] Add assignment-restore OperationRun creation + dedupe by
restore_run_idinapp/Services/Intune/RestoreService.php(useOperationRunService::ensureRunWithIdentity(); identity inputs includerestore_run_id) - T012 [US1] Update assignment-restore OperationRun summary counts (
totalattempted,processedsucceeded,failedfailed) inapp/Services/Intune/RestoreService.phpandapp/Services/OperationRunService.php - T013 [US1] Record stable failure
codenamespaces + normalizedreason_code+ sanitized messages for assignment restore failures inapp/Services/Intune/RestoreService.php(leverageOperationRunServicefailure sanitization) - T014 [US1] Remove per-assignment audit log emission from
app/Services/AssignmentRestoreService.php(replace per-itemauditLogger->log(...)calls with in-memory aggregation only) - T015 [US1] Add exactly one audit log entry per assignment restore execution in
app/Services/Intune/RestoreService.phpusingapp/Services/Intune/AuditLogger.php(resourceTyperestore_run, resourceId = restore run id) - T016 [US1] Add assignment-fetch OperationRun creation + dedupe keyed by
backup_item_idinapp/Services/AssignmentBackupService.php(wrap the Graph fetch insideenrichWithAssignments()usingOperationRunService::ensureRunWithIdentity()) - T017 [US1] Ensure assignment-related job classes can be OperationRun-tracked if they are used in the future by adding
public ?OperationRun $operationRun+middleware()returningTrackOperationRuninapp/Jobs/FetchAssignmentsJob.phpandapp/Jobs/RestoreAssignmentsJob.php - T036 [US1] Identify and document all start surfaces that can trigger assignment fetch/restore, and ensure each path creates/reuses the same run identity (avoid “tracked in one path, untracked in another” gaps)
Checkpoint: US1 delivers Monitoring visibility + auditability for assignment restore and assignment fetch during backup capture.
Phase 4: User Story 2 — Enforce correct access control semantics on affected admin surfaces (Priority: P2)
Goal: Cross-plane access returns 404, non-member access returns 404, and member-without-capability returns 403. Remove any authorization bypasses.
Independent Test: Access the affected routes/surfaces with wrong guard and with insufficient permissions; assert 404 vs 403 semantics.
Tests for User Story 2 (write first)
- T018 [P] [US2] Add regression test for
/admin/w/{workspace}guard enforcement (cross-plane 404) intests/Feature/Guards/AdminWorkspaceRoutesGuardTest.php - T019 [P] [US2] Add regression test ensuring Provider Connection create CTA does not bypass authorization in
tests/Feature/ProviderConnections/ProviderConnectionListAuthorizationTest.php - T020 [P] [US2] Add regression test for membership (404) before capability (403) in backup items relation manager in
tests/Feature/Rbac/BackupItemsRelationManagerSemanticsTest.php
Implementation for User Story 2
- T021 [US2] Add missing
ensure-correct-guard:webmiddleware to the/admin/w/{workspace}route group inroutes/web.php - T022 [US2] Remove the
->authorize(fn (): bool => true)bypass from header and empty-state create actions inapp/Filament/Resources/ProviderConnectionResource/Pages/ListProviderConnections.php - T023 [US2] Fix membership (404) vs capability (403) ordering for backup items under a backup set in
app/Filament/Resources/BackupSetResource/RelationManagers/BackupItemsRelationManager.php - T024 [US2] Swap legacy enforcement helper imports to canonical RBAC helper in
app/Filament/Resources/BackupSetResource/Pages/ListBackupSets.phpandapp/Filament/Resources/RestoreRunResource/Pages/ListRestoreRuns.php - T037 [US2] Verify any destructive-like Provider Connections actions still require explicit confirmation (no regressions), and that server authorization remains enforced for both header and empty-state CTAs
Checkpoint: US2 closes cross-plane leak + removes bypasses + restores correct 404/403 semantics.
Phase 5: User Story 3 — Validate assignment operations safely in non-production contexts (Priority: P3)
Goal: Assignment-related Graph services depend only on GraphClientInterface, enabling tests to run with NullGraphClient or fakes without concrete client coupling.
Independent Test: Resolve the assignment Graph services from the container with Graph disabled and run a minimal flow that exercises the interface contract.
Tests for User Story 3 (write first)
- T025 [P] [US3] Add unit test asserting assignment Graph services resolve with
GraphClientInterfacebinding intests/Feature/Graph/AssignmentGraphServiceResolutionTest.php - T026 [P] [US3] Add test using a fake
GraphClientInterfaceto simulate assignment fetch failures without real HTTP intests/Feature/Operations/AssignmentFetchOperationRunFailureTest.php
Implementation for User Story 3
- T027 [P] [US3] Replace
MicrosoftGraphClientconstructor type-hint withGraphClientInterfaceinapp/Services/Graph/AssignmentFetcher.php - T028 [P] [US3] Replace
MicrosoftGraphClientconstructor type-hint withGraphClientInterfaceinapp/Services/Graph/GroupResolver.php - T029 [P] [US3] Replace
MicrosoftGraphClientconstructor type-hint withGraphClientInterfaceinapp/Services/Graph/AssignmentFilterResolver.php - T030 [US3] Confirm container bindings remain canonical and no concrete client is injected directly by reviewing
app/Providers/AppServiceProvider.php
Checkpoint: US3 enables deterministic tests and non-prod validation for assignment operations.
Phase 6: Polish & Cross-Cutting Concerns
Purpose: Formatting + targeted verification commands + confidence checks.
- T031 [P] Run formatting for touched files via
./vendor/bin/sail bin pint --dirty(seespecs/094-assignment-ops-observability-hardening/quickstart.md) - T032 Run targeted tests via
./vendor/bin/sail artisan test --compact tests/Feature/Operations tests/Feature/Rbac tests/Feature/Guards tests/Feature/Audit(seespecs/094-assignment-ops-observability-hardening/quickstart.md) - T033 Run the full test suite via
./vendor/bin/sail artisan test --compact(seespecs/094-assignment-ops-observability-hardening/quickstart.md) - T038 Update any requirement references in tasks/spec if FR numbering changes (keep traceability from FR-001..FR-012 to the task IDs)
Dependencies & Execution Order
Phase Dependencies
- Setup (Phase 1): No dependencies.
- Foundational (Phase 2): Depends on Phase 1.
- US1 (Phase 3): Depends on Phase 2. No dependency on US2/US3.
- US2 (Phase 4): Depends on Phase 2. Independent from US1/US3.
- US3 (Phase 5): Depends on Phase 2. Independent from US1/US2.
- Polish (Phase 6): Depends on completing the desired stories.
User Story Dependencies
- US1 (P1): Standalone MVP.
- US2 (P2): Standalone hardening.
- US3 (P3): Standalone testability hardening.
Parallel Example: User Story 1
Parallelizable test tasks: T007, T008, T009, T010 (different files under tests/Feature/...).
Parallel Example: User Story 2
Parallelizable test tasks: T018, T019, T020 (different files under tests/Feature/...).
Parallelizable implementation tasks (after tests land): T021 + T022 (different files under routes/ vs app/Filament/...).
Parallel Example: User Story 3
Parallelizable implementation tasks: T027, T028, T029 (different files under app/Services/Graph/...).
Implementation Strategy
MVP First (User Story 1 Only)
- Complete Phase 1 (Setup) + Phase 2 (Foundational)
- Complete Phase 3 (US1) including tests
- Validate Monitoring visibility + audit log semantics
Incremental Delivery
- US1 → deploy/demo
- US2 → deploy/demo
- US3 → deploy/demo