TenantAtlas/specs/081-provider-connection-cutover/tasks.md
ahmido 4db8030f2a Spec 081: Provider connection cutover (#98)
Implements Spec 081 provider-connection cutover.

Highlights:
- Adds provider connection resolution + gating for operations/verification.
- Adds provider credential observer wiring.
- Updates Filament tenant verify flow to block with next-steps when provider connection isn’t ready.
- Adds spec docs under specs/081-provider-connection-cutover/ and extensive Spec081 test coverage.

Tests:
- vendor/bin/sail artisan test --compact tests/Feature/Filament/TenantSetupTest.php
- Focused suites for ProviderConnections/Verification ran during implementation (see local logs).

Co-authored-by: Ahmed Darrazi <ahmeddarrazi@MacBookPro.fritz.box>
Reviewed-on: #98
2026-02-08 11:28:51 +00:00

13 KiB

Tasks: Provider Connection Full Cutover (Spec 081)

Input: Design documents from specs/081-provider-connection-cutover/
Prerequisites: plan.md (required), spec.md (required), research.md, data-model.md, contracts/, quickstart.md

Tests: REQUIRED (Pest) — this feature changes runtime credential sourcing, operations behavior, and admin UI.

Phase 1: Setup (Shared Infrastructure)

  • T001 Create new Spec 081 test files tests/Feature/ProviderConnections/ProviderConnectionCutoverSpec081Test.php and tests/Feature/Guards/NoTenantCredentialRuntimeReadsSpec081Test.php
  • T002 Define Spec 081 test naming/tagging conventions in tests/Pest.php so vendor/bin/sail artisan test --compact --filter=Spec081 is a stable focused command (quickstart aligns to this command)

Phase 2: Foundational (Blocking Prerequisites)

⚠️ CRITICAL: Complete this phase before starting any user story work.

  • T003 Implement default ProviderConnection resolver app/Services/Providers/ProviderConnectionResolver.php (resolve default per tenant/provider; detect missing/multiple/invalid defaults; return deterministic reason_code, including provider_connection_invalid for multi-default corruption)
  • T004 [P] Add reason-code constants (baseline) in app/Support/Providers/ProviderReasonCodes.php (mirror Appendix A)
  • T005 Implement link-only “next steps” registry app/Support/Providers/ProviderNextStepsRegistry.php (maps reason_code → label + URL generator; no secrets)
  • T006 Add blocked outcome support in app/Support/OperationRunOutcome.php and update casts/validation in app/Models/OperationRun.php
  • T007 Update centralized outcome badge mapping for new blocked value in app/Support/Badges/Domains/OperationRunOutcomeBadge.php and add mapping tests in tests/Feature/Badges/OperationRunOutcomeBadgeBlockedTest.php
  • T008 Add OperationRunService helper to finalize blocked runs with sanitized failures in app/Services/OperationRunService.php (store reason_code + next_steps links; set status=completed, outcome=blocked)
  • T009 Update provider operation start gate to use resolver + blocked-run helper in app/Services/Providers/ProviderOperationStartGate.php (create run even when blocked; dedupe identity uses provider_connection_id when present)
  • T010 [P] Update verification report normalization to accept registry-produced next steps in app/Support/Verification/VerificationReportWriter.php and sanitizer app/Support/Verification/VerificationReportSanitizer.php (no behavior drift, just centralize usage)
  • T011 Add guard tests (runtime + static scan assertions) preventing reads/writes of legacy tenant credentials (->app_client_id / ->app_client_secret) outside allowed backfill tooling in tests/Feature/Guards/NoTenantCredentialRuntimeReadsSpec081Test.php
  • T042 Add regression coverage for default uniqueness invariant in tests/Feature/ProviderConnections/ProviderConnectionDefaultInvariantSpec081Test.php (assert partial unique index exists for (tenant_id, provider) where is_default=true, and ProviderConnection::makeDefault() preserves invariant)

Checkpoint: Foundation ready — default resolution, blocked outcomes, badge mapping, next steps registry, guard test framework, and default uniqueness invariant coverage exist.


Phase 3: User Story 1 — Deterministic provider operations (Priority: P1) 🎯 MVP

Goal: All provider-backed operation starts deterministically use default ProviderConnection; missing/invalid configuration blocks with stable reason codes and observable runs.

Independent Test: Start a provider-backed operation with (a) default connection and (b) missing default; verify run outcome and context include provider_connection_id and stable reason_code.

  • T012 [P] [US1] Add provider-connection resolution tests for missing default → blocked run in tests/Feature/ProviderConnections/ProviderConnectionCutoverSpec081Test.php
  • T013 [P] [US1] Add provider-connection resolution tests for missing credential → blocked run in tests/Feature/ProviderConnections/ProviderConnectionCutoverSpec081Test.php
  • T043 [P] [US1] Add provider-connection resolution tests for invalid multi-default data → deterministic blocked/fail result with reason code provider_connection_invalid (extension detail allowed, e.g. ext.multiple_defaults_detected) in tests/Feature/ProviderConnections/ProviderConnectionCutoverSpec081Test.php
  • T014 [US1] Introduce a single entry point for “Graph options from connection” use in app/Services/Providers/ProviderGateway.php (ensure all call sites accept ProviderConnection)
  • T015 [US1] Refactor app/Services/Inventory/InventorySyncService.php to resolve ProviderConnection and build Graph options via ProviderGateway (remove $tenant->app_client_* runtime reads)
  • T016 [US1] Refactor app/Services/Intune/PolicySyncService.php to resolve ProviderConnection and build Graph options via ProviderGateway (remove $tenant->app_client_* runtime reads)
  • T017 [US1] Refactor app/Services/Intune/PolicySnapshotService.php to resolve ProviderConnection and build Graph options via ProviderGateway (remove $tenant->app_client_* runtime reads)
  • T018 [US1] Refactor app/Services/Intune/RestoreService.php to resolve ProviderConnection and build Graph options via ProviderGateway (remove $tenant->app_client_* runtime reads)
  • T019 [US1] Refactor app/Services/Graph/ScopeTagResolver.php to resolve ProviderConnection and build Graph options via ProviderGateway (remove $tenant->app_client_* runtime reads)
  • T020 [US1] Refactor app/Services/Intune/RbacOnboardingService.php to use ProviderConnection + Graph options (remove $tenant->app_client_id checks; gate via resolver)
  • T021 [US1] Refactor app/Services/Intune/RbacHealthService.php to use ProviderConnection + Graph options (remove $tenant->app_client_id filter usage)
  • T022 [US1] Refactor app/Http/Controllers/AdminConsentCallbackController.php to resolve/persist consent outcomes against ProviderConnection records and stop all reads/writes of tenants.app_* for callback handling
  • T023 [P] [US1] Deprecate Tenant::graphOptions() and add a test ensuring it is unused in runtime services/jobs in app/Models/Tenant.php and tests/Feature/Guards/NoTenantCredentialRuntimeReadsSpec081Test.php
  • T044 [US1] Audit all Graph call sites touched by Spec 081 refactors and ensure each is represented in config/graph_contracts.php; add/update regression coverage in tests/Feature/Graph/GraphContractRegistryCoverageSpec081Test.php
  • T045 [P] [US1] Add DB-only render regression test for Monitoring → Operations surfaces in tests/Feature/Monitoring/OperationsDbOnlyRenderingSpec081Test.php (assert no provider/network call path is invoked during page render)

Backfill (Microsoft default provider connections) — Out of scope for Spec 081 (won't do)

  • T024 [US1] Implement idempotent backfill command app/Console/Commands/TenantpilotBackfillMicrosoftDefaultProviderConnections.php with signature tenantpilot:provider-connections:backfill-microsoft-defaults (won't do for 081: no legacy tenant credential data migration required)
  • T025 [US1] Implement backfill logic service app/Services/Providers/ProviderConnectionBackfillService.php (decision tree from FR-081-010; allowed to read legacy tenants.app_* once) (won't do for 081: no legacy tenant credential data migration required)
  • T026 [P] [US1] Add feature tests for backfill decision tree in tests/Feature/Console/ProviderConnectionsBackfillMicrosoftDefaultsCommandTest.php (won't do for 081: no legacy tenant credential data migration required)
  • T027 [US1] Add a regression test that backfill is idempotent (no duplicates) in tests/Feature/Console/ProviderConnectionsBackfillMicrosoftDefaultsCommandTest.php (won't do for 081: no legacy tenant credential data migration required)

Phase 4: User Story 2 — Safe credential management with audit (Priority: P2)

Goal: Credentials are managed only via ProviderConnection UI/actions with explicit confirmation, correct authorization semantics, and audit logging (no secret leakage).

Independent Test: Rotate credentials and confirm (a) confirmation required, (b) audit event exists with redacted payload, (c) secrets never appear outside encrypted payload.

  • T028 [P] [US2] Remove tenant credential fields from registration form in app/Filament/Pages/Tenancy/RegisterTenant.php and replace with helper text linking to Provider Connections management
  • T029 [P] [US2] Remove tenant credential fields from tenant form/infolist in app/Filament/Resources/TenantResource.php (no app_client_id / app_client_secret inputs or copyable entries)
  • T030 [US2] Ensure ProviderConnection management is the single credential surface by adding/confirming a navigation action from tenant view to provider connections in app/Filament/Resources/TenantResource/Pages/ViewTenant.php
  • T031 [US2] Enforce confirmed, server-side authorized credential mutations for ProviderCredential updates in app/Filament/Resources/ProviderConnectionResource/Pages/EditProviderConnection.php using Action::make(...)->action(...)->requiresConfirmation()
  • T032 [US2] Extend the existing audit pipeline so credential create/update/rotate emits stable, redacted audit events via app/Observers/ProviderCredentialObserver.php
  • T033 [P] [US2] Add authorization tests for ProviderConnection management: non-member 404, member missing capability 403 in tests/Feature/ProviderConnections/ProviderConnectionAuthorizationSpec081Test.php
  • T034 [P] [US2] Add audit + redaction tests for credential rotation in tests/Feature/Audit/ProviderCredentialAuditSpec081Test.php
  • T046 [P] [US2] Add DB-only render regression tests for TenantResource/ProviderConnectionResource view pages in tests/Feature/ProviderConnections/ProviderConnectionViewsDbOnlyRenderingSpec081Test.php (no Graph/provider calls during render)
  • T047 [US2] Implement and test blocked-state guidance on operation start surfaces (reason code + primary “Manage Provider Connections” link) in app/Filament/Resources/TenantResource/Pages/ViewTenant.php and tests/Feature/ProviderConnections/ProviderOperationBlockedGuidanceSpec081Test.php

Phase 5: User Story 3 — Troubleshoot failures using stable reason codes (Priority: P3)

Goal: Blocked/failed provider operations consistently expose stable reason_code and link-only next steps across OperationRuns and Verification reports, with zero secret leakage.

Independent Test: Trigger blocked outcomes and assert reason_code + next_steps shape is consistent and secrets are absent.

  • T035 [P] [US3] Add tests asserting blocked OperationRuns store reason_code + next_steps links and never include secrets in context/failure summaries in tests/Feature/Monitoring/OperationRunBlockedSpec081Test.php
  • T036 [US3] Update both provider health checks and permission check clusters to use the central registry for next steps in app/Jobs/ProviderConnectionHealthCheckJob.php and app/Support/Verification/TenantPermissionCheckClusters.php
  • T037 [US3] Normalize and document reason codes used by provider exceptions in app/Support/RunFailureSanitizer.php (map to Appendix A taxonomy where possible, document intentional extension/deviation mappings, unknown → unknown_error)
  • T038 [P] [US3] Add verification report schema tests for next_steps structure (label + url) in tests/Feature/Verification/VerificationReportNextStepsSchemaSpec081Test.php

Phase 6: Polish & Cross-Cutting Concerns

  • T039 [P] Run Pint formatting for all changed files via vendor/bin/sail bin pint --dirty
  • T040 Run focused test pack for Spec 081 via vendor/bin/sail artisan test --compact --filter=Spec081
  • T041 Run impacted test suites: tests/Feature/ProviderConnections, tests/Feature/Guards, tests/Feature/Monitoring, tests/Feature/Verification via vendor/bin/sail artisan test --compact

Dependencies & Execution Order

Dependency Graph (User Stories)

  • Phase 1 → Phase 2 → US1 → (US2, US3) → Polish

US2 and US3 can run in parallel after US1 (they reuse the foundational blocked-run + registry primitives).

Parallel Execution Examples

US1 (P1)

  • In parallel after Phase 2:
    • T043 tests/Feature/ProviderConnections/ProviderConnectionCutoverSpec081Test.php
    • T015 app/Services/Inventory/InventorySyncService.php
    • T016 app/Services/Intune/PolicySyncService.php
    • T017 app/Services/Intune/PolicySnapshotService.php
    • T018 app/Services/Intune/RestoreService.php
    • T019 app/Services/Graph/ScopeTagResolver.php
    • T045 tests/Feature/Monitoring/OperationsDbOnlyRenderingSpec081Test.php

US2 (P2)

  • In parallel:
    • T028 app/Filament/Pages/Tenancy/RegisterTenant.php
    • T029 app/Filament/Resources/TenantResource.php
    • T033 tests/Feature/ProviderConnections/ProviderConnectionAuthorizationSpec081Test.php
    • T046 tests/Feature/ProviderConnections/ProviderConnectionViewsDbOnlyRenderingSpec081Test.php

US3 (P3)

  • In parallel:
    • T035 tests/Feature/Monitoring/OperationRunBlockedSpec081Test.php
    • T038 tests/Feature/Verification/VerificationReportNextStepsSchemaSpec081Test.php

Implementation Strategy

MVP scope: Phase 1 + Phase 2 + US1.

  • Deliver deterministic starts, blocked runs, backfill command, and remove runtime legacy reads first.
  • Then deliver admin UX + audit (US2) and next-step consistency across verification/monitoring (US3).