---

description: "Task list for Graph Contracts — LIST $expand Parity Fix"
---

# Tasks: Graph Contracts — LIST `$expand` Parity Fix

**Input**: Design documents from `specs/112-list-expand-parity/`

**Prerequisites**: plan.md (required), spec.md (required for user stories), research.md, data-model.md, contracts/

**Tests**: REQUIRED (Pest) — this feature changes runtime Graph request shapes.

## Phase 1: Setup (Shared)

**Purpose**: Confirm inputs and establish a baseline before changing runtime behavior.

- [X] T001 [Shared] Read implementation plan in specs/112-list-expand-parity/plan.md
- [X] T002 [P] [Shared] Verify $expand allowlist exists for Entra role assignments in config/graph_contracts.php
- [X] T003 [P] [Shared] Identify existing Graph tests to extend in tests/Unit/GraphContractRegistryTest.php and tests/Unit/MicrosoftGraphClientListPoliciesSelectTest.php

---

## Phase 2: Foundational (Blocking Prerequisites)

**Purpose**: Establish pre-change confidence and shared test baseline.

- [X] T004 [Shared] Run baseline focused tests listed in specs/112-list-expand-parity/quickstart.md

**Checkpoint**: Baseline established; safe to implement.

---

## Phase 3: User Story 1 — Admin roles report shows real principal names (Priority: P1) 🎯 MVP

**Goal**: Admin Roles report requests `principal` expansion and renders real principal display names when Graph provides them.

**Independent Test**: Simulate role assignment list retrieval and assert report output uses `principal.displayName` when present, else falls back to `Unknown`.

### Tests for User Story 1

- [X] T005 [P] [US1] Create report service test scaffolding in tests/Unit/EntraAdminRolesReportServiceTest.php (RefreshDatabase + mock GraphClientInterface)
- [X] T006 [US1] Implement tests asserting `entraRoleAssignments` call includes `expand => 'principal'` and payload uses principal display name in tests/Unit/EntraAdminRolesReportServiceTest.php

### Implementation for User Story 1

- [X] T007 [US1] Pass `expand => 'principal'` when fetching assignments in app/Services/EntraAdminRoles/EntraAdminRolesReportService.php
- [X] T008 [US1] Forward caller-provided `expand` into LIST query input in app/Services/Graph/MicrosoftGraphClient.php
- [X] T009 [US1] Normalize + cap `$expand` in app/Services/Graph/GraphContractRegistry.php (split string on top-level commas only; preserve commas inside balanced parentheses; trim, drop empty, dedupe, maxTokenLen=200, maxItems=10 after allowlist)

### Regression tests for User Story 1

- [X] T010 [US1] Add LIST `$expand` request-shape test coverage in tests/Unit/MicrosoftGraphClientListPoliciesSelectTest.php
- [X] T011 [US1] Add `$expand` normalization/cap test coverage in tests/Unit/GraphContractRegistryTest.php
- [X] T012 [US1] Run story tests in tests/Unit/EntraAdminRolesReportServiceTest.php plus Graph tests in tests/Unit/MicrosoftGraphClientListPoliciesSelectTest.php

**Checkpoint**: Entra Admin Roles report can render real principal names when upstream provides them.

---

## Phase 4: User Story 2 — Engineers can request explicit LIST expansions safely (Priority: P2)

**Goal**: LIST supports explicit expansions safely and discoverably (document option shape, allowlist enforcement).

**Independent Test**: Inspect outgoing LIST request and confirm `$expand` is included only when explicitly provided and allowlisted.

### Tests for User Story 2

- [X] T013 [P] [US2] Add test asserting LIST omits `$expand` when `expand` is not provided in tests/Unit/MicrosoftGraphClientListPoliciesSelectTest.php
- [X] T014 [P] [US2] Add tests asserting (a) disallowed expand tokens are removed (exact-match allowlist, no partial matches) and (b) allowlisted subquery/select-style expand tokens that contain commas inside parentheses are preserved as a single token (no naive comma-splitting) in tests/Unit/GraphContractRegistryTest.php

### Implementation for User Story 2

- [X] T015 [US2] Document supported `listPolicies()` options (select/expand/filter/top/platform + input shapes) in app/Services/Graph/GraphClientInterface.php
- [X] T016 [US2] Ensure `GraphContractCheck` query shape remains compatible (select + expand) in app/Console/Commands/GraphContractCheck.php

**Checkpoint**: Engineers can safely request allowlisted expansions on LIST without silent capability mismatch.

---

## Phase 5: User Story 3 — Misuse is detectable in non-production (Priority: P3)

**Goal**: When `$expand` is sanitized (removed or truncated), maintainers get a diagnostic signal in non-production with low noise in production.

**Independent Test**: Trigger mixed allowed/disallowed expand and assert a non-production warning is emitted with policy type + removed values.

### Tests for User Story 3

- [X] T017 [US3] Add Log::spy-based test for non-production diagnostics when `$expand` is sanitized in tests/Unit/GraphContractRegistryTest.php
- [X] T018 [US3] Add production-noise guard test (debug-level or suppressed warn) for `$expand` sanitization in tests/Unit/GraphContractRegistryTest.php

### Implementation for User Story 3

- [X] T019 [US3] Emit structured diagnostic logs when `$expand` is removed/truncated in app/Services/Graph/GraphContractRegistry.php

**Checkpoint**: Sanitization is visible during dev/staging without spamming production logs.

---

## Phase 6: Polish & Cross-Cutting Concerns

**Purpose**: Keep the change safe, consistent, and shippable.

- [X] T020 [Shared] Run formatting on touched files (Pint) after changes in app/Services/Graph/GraphContractRegistry.php
- [X] T021 [Shared] Run focused suite listed in specs/112-list-expand-parity/quickstart.md

---

## Dependencies & Execution Order

### Phase Dependencies

- Setup (Phase 1) → Foundational (Phase 2) → US1 (Phase 3) → US2 (Phase 4) → US3 (Phase 5) → Polish (Phase 6)

### User Story Dependencies

- US1 depends on LIST `$expand` parity (implemented as part of US1 via app/Services/Graph/MicrosoftGraphClient.php + app/Services/Graph/GraphContractRegistry.php).
- US2 extends US1 with discoverability + additional safety regression coverage.
- US3 depends on the sanitization logic (adds diagnostics + tests).

### Parallel Opportunities

- Phase 1: T002 and T003 can run in parallel.
- US1: T005 can be done in parallel with T008/T009 (different files).
- US2: T013 and T014 can run in parallel.

---

## Parallel Example: User Story 1

- In parallel:
  - T005 (test scaffolding) in tests/Unit/EntraAdminRolesReportServiceTest.php
  - T008 (LIST forwards expand) in app/Services/Graph/MicrosoftGraphClient.php
  - T009 (sanitize/normalize/cap expand) in app/Services/Graph/GraphContractRegistry.php

---

## Implementation Strategy

### MVP First (User Story 1 Only)

1. Complete Phase 1–2
2. Complete US1 (Phase 3)
3. Stop and ship if acceptance + regression tests pass

### Then harden (US2 + US3)

- Add discoverability + extra regression guards (US2)
- Add diagnostics behavior + tests (US3)
