---

description: "Task list for Provider Foundation v1"

---

# Tasks: Provider Foundation v1 (Microsoft-first, Security-first)

**Input**: Design documents from `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/specs/061-provider-foundation/`  
**Prerequisites**: `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/specs/061-provider-foundation/plan.md` (required), `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/specs/061-provider-foundation/spec.md` (required), `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/specs/061-provider-foundation/research.md`, `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/specs/061-provider-foundation/data-model.md`, `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/specs/061-provider-foundation/contracts/`, `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/specs/061-provider-foundation/quickstart.md`

**Tests**: REQUIRED (Pest) for runtime behavior changes  
**Operations**: Provider operations MUST create/reuse a canonical `OperationRun`, be enqueue-only, and keep Monitoring/ProviderConnections pages DB-only at render/poll time  
**Badges**: If ProviderConnection status/health is rendered as a badge, use `BadgeCatalog` / `BadgeRenderer` (BADGE-001) and add mapping tests

**Organization**: Tasks are grouped by user story (US1, US2, US3) to enable independent delivery.

## Phase 1: Setup (Shared Infrastructure)

**Purpose**: Prepare local environment + validate baseline

- [X] T001 Start containers with `./vendor/bin/sail up -d` (script: `./vendor/bin/sail`)
- [X] T002 Run baseline Ops-UX DB-only tests with `./vendor/bin/sail artisan test tests/Feature/Monitoring/OperationsDbOnlyTest.php` (test: `tests/Feature/Monitoring/OperationsDbOnlyTest.php`)
- [X] T003 [P] Review existing idempotency + run tracking patterns in `app/Services/OperationRunService.php` and `app/Jobs/Middleware/TrackOperationRun.php`

---

## Phase 2: Foundational (Blocking Prerequisites)

**Purpose**: Core provider foundation that all stories depend on (schema, models, policies, ops primitives)

**Checkpoint**: Migrations + models + policies exist; provider operation types are registered; shared run-gating utilities exist

- [X] T004 Create provider connections migration in `database/migrations/2026_01_24_000001_create_provider_connections_table.php`
- [X] T005 Create provider credentials migration in `database/migrations/2026_01_24_000002_create_provider_credentials_table.php`
- [X] T005a Add DB-level invariant: partial unique index ensuring only one default per (tenant_id, provider) (example: `unique (tenant_id, provider) WHERE is_default = true`; ensure default flipping is atomic/transactional)
- [X] T006 [P] Create `App\Models\ProviderConnection` in `app/Models/ProviderConnection.php` (relations, casts, default-connection invariant)
- [X] T007 [P] Create `App\Models\ProviderCredential` in `app/Models/ProviderCredential.php` (encrypted payload cast, hidden attributes, 1:1 relation)
- [X] T008 [P] Add tenant relationship for connections in `app/Models/Tenant.php` (e.g., `providerConnections()` / `providerCredentials()` as needed)
- [X] T009 [P] Add factories in `database/factories/ProviderConnectionFactory.php` and `database/factories/ProviderCredentialFactory.php`
- [X] T010 Create authorization policy + gates for `provider.view`, `provider.manage`, `provider.run` (capabilities-first; tenant-scoped). Map roles to capabilities: Owner/Manager=view+manage+run; Operator=view+run; Readonly=view only. No `role == X` checks in feature code.
- [X] T010b [P] Add RBAC capability tests in `tests/Feature/ProviderConnections/ProviderRbacCapabilitiesTest.php` (Operator can start operations but cannot manage; Readonly is view-only)
- [X] T011 Register the policy + gates in `app/Providers/AuthServiceProvider.php` (create it and register in `bootstrap/providers.php` if missing; otherwise use the project’s canonical policy registration provider)
- [X] T012 Add provider operation labels in `app/Support/OperationCatalog.php` using canonical operation types: `provider.connection.check`, `inventory.sync`, `compliance.snapshot` (ensure UI/actions/jobs use these exact strings; update `tests/Feature/OpsUx/OperationCatalogCoverageTest.php` to detect multi-dot types)
- [X] T013 Add provider related links in `app/Support/OperationRunLinks.php` (link provider runs to Provider Connections pages when `context.provider_connection_id` exists)
- [X] T014 Create provider operation registry in `app/Services/Providers/ProviderOperationRegistry.php` (central allowlist + metadata for v1 operations)
- [X] T014a [P] Define provider capability interfaces + DTOs in `app/Services/Providers/Contracts/ProviderHealthCheck.php`, `app/Services/Providers/Contracts/HealthResult.php`, `app/Services/Providers/Contracts/ProviderInventoryCollector.php`, `app/Services/Providers/Contracts/ProviderComplianceCollector.php`, `app/Services/Providers/Contracts/ProviderDirectoryCollector.php`, `app/Services/Providers/Contracts/ProviderScriptExecutor.php`
- [X] T014b [P] Implement credential retrieval/rotation service in `app/Services/Providers/CredentialManager.php` (reads `provider_credentials`, validates required keys, never logs decrypted payload)
- [X] T014c [P] Implement provider gateway in `app/Services/Providers/ProviderGateway.php` (build Graph request context from ProviderConnection + CredentialManager, centralize correlation IDs + failure mapping, call `GraphClientInterface`)
- [X] T014d Enable Graph client binding for per-request credentials in `config/graph.php` and `app/Providers/AppServiceProvider.php` (e.g., `GRAPH_ENABLED` override; do not require env client_secret for binding when ProviderGateway supplies request context)
- [X] T014e [P] Add unit tests for provider gateway + credential manager in `tests/Unit/Providers/CredentialManagerTest.php` and `tests/Unit/Providers/ProviderGatewayTest.php`
- [X] T015 Create run gating service in `app/Services/Providers/ProviderOperationStartGate.php` (DB transaction + `lockForUpdate()` on ProviderConnection; dedupe same-operation; block different-operation as “scope busy”; returns active-run link)
- [X] T016 [P] Extend failure sanitization in `app/Support/OpsUx/RunFailureSanitizer.php` (provider auth/throttling/outage reason codes + stronger secret redaction)
- [X] T017 [P] Add unit tests for failure sanitization in `tests/Unit/OpsUx/RunFailureSanitizerTest.php`
- [X] T018 [P] Add unit tests for run gating in `tests/Unit/Providers/ProviderOperationStartGateTest.php`
- [X] T019 Run foundational tests with `./vendor/bin/sail artisan test tests/Unit/OpsUx/RunFailureSanitizerTest.php` (test: `tests/Unit/OpsUx/RunFailureSanitizerTest.php`)

---

## Phase 3: User Story 1 — Set up a provider connection safely (Priority: P1) 🎯 MVP

**Goal**: Owner/Manager can create/manage Microsoft provider connections, attach credentials, set a default connection, and never expose secrets.

**Independent Test**: An Owner/Manager can create a connection + credentials, later view/edit it, and no secret values are displayed or leaked; pages remain DB-only at render/poll time.

### Tests for User Story 1

- [X] T020 [P] [US1] Add DB-only render test for Provider Connections in `tests/Feature/Filament/ProviderConnectionsDbOnlyTest.php`
- [X] T021 [P] [US1] Add role authorization test in `tests/Feature/ProviderConnections/ProviderConnectionAuthorizationTest.php`
- [X] T022 [P] [US1] Add credential encryption/non-disclosure test in `tests/Feature/ProviderConnections/ProviderCredentialSecurityTest.php`
- [X] T022b [P] [US1] Add credential leak guard test in `tests/Feature/ProviderConnections/CredentialLeakGuardTest.php` (assert no secrets appear in OperationRun failure records or captured logs; forbidden substrings: `client_secret`, `Bearer `, `access_token`, `refresh_token`, `Authorization`)

### Implementation for User Story 1

- [X] T023 [US1] Create Filament resource skeleton in `app/Filament/Resources/ProviderConnectionResource.php`
- [X] T024 [P] [US1] Create pages in `app/Filament/Resources/ProviderConnectionResource/Pages/ListProviderConnections.php`, `app/Filament/Resources/ProviderConnectionResource/Pages/CreateProviderConnection.php`, `app/Filament/Resources/ProviderConnectionResource/Pages/EditProviderConnection.php`
- [X] T025 [US1] Implement list/table + filters in `app/Filament/Resources/ProviderConnectionResource.php` (tenant-scoped; DB-only)
- [X] T026 [US1] Implement create/edit forms in `app/Filament/Resources/ProviderConnectionResource.php` (provider=microsoft, `entra_tenant_id`, `display_name`, `is_default`, status/health read-only fields)
- [X] T027 [US1] Implement credential upsert action (Owner/Manager only; secrets never shown) in `app/Filament/Resources/ProviderConnectionResource/Pages/EditProviderConnection.php`
- [X] T028 [US1] Implement disable connection action with confirmation + audit log in `app/Filament/Resources/ProviderConnectionResource/Pages/EditProviderConnection.php`
- [X] T029 [US1] Write audit log entries for connection + credential changes using `app/Services/Intune/AuditLogger.php` (called from `app/Filament/Resources/ProviderConnectionResource/Pages/CreateProviderConnection.php` and `app/Filament/Resources/ProviderConnectionResource/Pages/EditProviderConnection.php`)
- [X] T030 [US1] Run story tests with `./vendor/bin/sail artisan test tests/Feature/ProviderConnections/ProviderConnectionAuthorizationTest.php` (test: `tests/Feature/ProviderConnections/ProviderConnectionAuthorizationTest.php`)

---

## Phase 4: User Story 2 — Verify connection health without blocking the UI (Priority: P2)

**Goal**: Owner/Manager/Operator can enqueue a health check that creates an `OperationRun`, updates health state, and shows stable reason codes/messages on failure.

**Independent Test**: Clicking “Check connection” enqueues exactly one run (dedupe), never calls Graph in the request cycle, and produces a visible run outcome + updates connection health.

### Tests for User Story 2

- [X] T031 [P] [US2] Add start-surface test (no Graph in request; job queued; run created) in `tests/Feature/ProviderConnections/ProviderConnectionHealthCheckStartSurfaceTest.php`
- [X] T032 [P] [US2] Add job behavior test (success + categorized failure) in `tests/Feature/ProviderConnections/ProviderConnectionHealthCheckJobTest.php`
- [X] T032b [P] [US2] Assert OperationRun context contract for connection health checks in `tests/Feature/ProviderConnections/ProviderConnectionHealthCheckStartSurfaceTest.php` (`context.provider`, `context.module`, `context.provider_connection_id`, `context.target_scope.entra_tenant_id`)

### Implementation for User Story 2

- [X] T033 [US2] Add “Check connection” Filament action (enqueue-only) in `app/Filament/Resources/ProviderConnectionResource/Pages/EditProviderConnection.php` (MUST call `ProviderOperationStartGate` first: same-operation returns existing active run; different-operation for same scope blocks “scope busy” + link to active run)
- [X] T034 [US2] Create queued job in `app/Jobs/ProviderConnectionHealthCheckJob.php` (uses `app/Jobs/Middleware/TrackOperationRun.php`, updates `provider_connections` health fields, updates `operation_runs`)
- [X] T035 [US2] Create health check module in `app/Services/Providers/MicrosoftProviderHealthCheck.php` (implements `app/Services/Providers/Contracts/ProviderHealthCheck.php`, uses `ProviderGateway`, maps failures to reason codes via `app/Support/OpsUx/RunFailureSanitizer.php`)
- [X] T035b [P] [US2] Ensure health check uses ProviderConnection context (`entra_tenant_id`) and obtains tokens via ProviderGateway/CredentialManager (ProviderGateway is the only decryptor; no secrets in config/env beyond bootstrap)
- [X] T036 [US2] Run story tests with `./vendor/bin/sail artisan test tests/Feature/ProviderConnections/ProviderConnectionHealthCheckStartSurfaceTest.php` (test: `tests/Feature/ProviderConnections/ProviderConnectionHealthCheckStartSurfaceTest.php`)

---

## Phase 5: User Story 3 — Run provider operations with safety and observability (Priority: P3)

**Goal**: Owner/Manager/Operator can run provider operations (inventory + compliance snapshot) as observable runs, with per-scope concurrency rules (dedupe vs scope-busy) and summary counts.

**Independent Test**: Starting inventory/compliance creates runs, obeys dedupe/scope-busy rules, never calls Graph in the request cycle, and writes summary counts + failures safely.

### Tests for User Story 3

- [X] T037 [P] [US3] Add scope-busy + dedupe behavior tests in `tests/Feature/ProviderConnections/ProviderOperationConcurrencyTest.php`
- [X] T038 [P] [US3] Add compliance snapshot summary_counts tests in `tests/Feature/ProviderConnections/ProviderComplianceSnapshotJobTest.php`
- [X] T038b [P] [US3] Assert OperationRun context contract for inventory/compliance runs in `tests/Feature/ProviderConnections/ProviderOperationConcurrencyTest.php` and `tests/Feature/ProviderConnections/ProviderComplianceSnapshotJobTest.php` (`context.provider`, `context.module`, `context.provider_connection_id`, `context.target_scope.entra_tenant_id`)

### Implementation for User Story 3

- [X] T039 [US3] Add managed devices contract entry for compliance snapshot in `config/graph_contracts.php` (resource `deviceManagement/managedDevices`, select includes compliance state)
- [X] T040 [US3] Add allowed summary keys for compliance counts in `app/Support/OpsUx/OperationSummaryKeys.php` (add `compliant`, `noncompliant`, `unknown`)
- [X] T041 [US3] Create compliance snapshot collector in `app/Services/Providers/MicrosoftComplianceSnapshotService.php` (implements `app/Services/Providers/Contracts/ProviderComplianceCollector.php`, uses `ProviderGateway`, Graph list + count compliance states)
- [X] T042 [US3] Create queued job in `app/Jobs/ProviderComplianceSnapshotJob.php` (writes `OperationRun` context + summary_counts; updates failures with sanitized reason codes)
- [X] T043 [US3] Add “Compliance snapshot” Filament action (enqueue-only) in `app/Filament/Resources/ProviderConnectionResource/Pages/EditProviderConnection.php` (MUST call `ProviderOperationStartGate` first: same-operation returns existing active run; different-operation for same scope blocks “scope busy” + link to active run)
- [X] T044 [US3] Create minimal inventory collector in `app/Services/Providers/MicrosoftProviderInventoryCollector.php` (implements `app/Services/Providers/Contracts/ProviderInventoryCollector.php`, uses `ProviderGateway`, contract-backed listing + summary counts only)
- [X] T045 [US3] Create queued job in `app/Jobs/ProviderInventorySyncJob.php` (writes `OperationRun` context + summary_counts; obeys scope-busy rules via start gate)
- [X] T046 [US3] Add “Inventory sync” Filament action (enqueue-only) in `app/Filament/Resources/ProviderConnectionResource/Pages/EditProviderConnection.php` (MUST call `ProviderOperationStartGate` first: same-operation returns existing active run; different-operation for same scope blocks “scope busy” + link to active run)
- [X] T047 [US3] Run story tests with `./vendor/bin/sail artisan test tests/Feature/ProviderConnections/ProviderOperationConcurrencyTest.php` (test: `tests/Feature/ProviderConnections/ProviderOperationConcurrencyTest.php`)

---

## Phase 6: Polish & Cross-Cutting Concerns

**Purpose**: Reduce regressions and improve consistency across stories

- [X] T048 [P] Ensure provider operations set `context.target_scope.entra_tenant_id` and `context.target_scope.entra_tenant_name` in `app/Jobs/ProviderConnectionHealthCheckJob.php`, `app/Jobs/ProviderComplianceSnapshotJob.php`, and `app/Jobs/ProviderInventorySyncJob.php`
- [X] T049 [P] If ProviderConnection status/health is shown as badges, add a badge mapper + tests in `app/Support/Badges/Domains/` and `tests/Unit/Badges/`
- [X] T050 Run formatting with `./vendor/bin/sail php ./vendor/bin/pint --dirty` (script: `./vendor/bin/pint`)
- [X] T051 Run focused feature tests with `./vendor/bin/sail artisan test tests/Feature/ProviderConnections` (folder: `tests/Feature/ProviderConnections`)

---

## Dependencies & Execution Order

### User Story Dependency Graph

```text
Phase 1 (Setup)
  ↓
Phase 2 (Foundation: schema/models/policy + ops primitives)
  ↓
US1 (Connections + credentials UI) ─┬─→ US2 (Health check run)
                                   └─→ US3 (Inventory + compliance runs)
```

### Parallel Opportunities

- Phase 2 tasks marked `[P]` can be done in parallel (different files).
- Within each user story phase, `[P]` tests can be written in parallel.
- Caution: US2 and US3 both extend `app/Filament/Resources/ProviderConnectionResource/Pages/EditProviderConnection.php` (avoid parallel edits to that file unless coordinated).

---

## Parallel Example: User Story 1

```bash
Task: "Add DB-only render test for Provider Connections in tests/Feature/Filament/ProviderConnectionsDbOnlyTest.php"
Task: "Add role authorization test in tests/Feature/ProviderConnections/ProviderConnectionAuthorizationTest.php"
Task: "Add credential encryption/non-disclosure test in tests/Feature/ProviderConnections/ProviderCredentialSecurityTest.php"
```

---

## Implementation Strategy

### MVP First (User Story 1)

1. Complete Phase 1 + Phase 2
2. Complete US1 (Provider Connections + credentials management)
3. Validate with `tests/Feature/ProviderConnections/*` and DB-only render checks

### Incremental Delivery

1. US1 → demo/manage connections safely (no provider calls)
2. US2 → add health check (first real provider call, but enqueue-only + observable)
3. US3 → add inventory + compliance snapshot operations (summary counts + scope-busy rules)
