# Implementation Plan: Spec 419 - M365 TCM Workload Registry Expansion **Branch**: `419-m365-tcm-workload-registry-expansion` | **Date**: 2026-06-26 | **Spec**: `specs/419-m365-tcm-workload-registry-expansion/spec.md` **Input**: Feature specification from `/specs/419-m365-tcm-workload-registry-expansion/spec.md` ## Summary Expand the existing Coverage v2 registry so TenantPilot can classify Microsoft 365 TCM workload families as registry-only/detected planning truth without activating capture, compare, render, restore, certification, customer output, or UI changes. The implementation should reuse `TenantConfigurationResourceType`, `TenantConfigurationSupportedScope`, `ResourceTypeRegistry`, Coverage v2 enum/check constraints, metadata JSONB, and `ClaimGuard`. New non-Intune entries default conservatively and broad M365 claims remain blocked. ## Technical Context **Language/Version**: PHP 8.4.x, Laravel 12.x **Primary Dependencies**: existing Coverage v2 TenantConfiguration models/services/enums, Pest 4, PostgreSQL via Sail **Storage**: Existing `tenant_configuration_resource_types` and `tenant_configuration_supported_scopes`; JSONB `metadata` preferred for catalog/documentation metadata. No new core table by default. **Testing**: Pest 4 / PHPUnit 12 via Sail **Validation Lanes**: fast-feedback, confidence, PostgreSQL lane if enum/check constraints or JSONB/query constraints change, focused browser if active rows/scopes render on the existing Spec 418 surface **Target Platform**: Laravel Sail locally, Dokploy/container deployment for staging/production **Project Type**: Laravel monolith under `apps/platform` **Performance Goals**: deterministic local registry sync/seed, no remote calls, no runtime docs fetch, bounded test fixtures **Constraints**: registry-only, no runtime UI route/action changes, existing-surface data-impact proof if rendered, no Graph/TCM/provider calls, no concrete evidence rows, no `tenant_id`, no mini-platform tables/classes, no broad M365 customer claims **Scale/Scope**: representative or full static M365 TCM catalog entries for Entra, Exchange, Teams, Security and Compliance, plus safe workload-level Defender/Purview status ## UI / Surface Guardrail Plan - **Guardrail scope**: no runtime UI code change, no new route, and no new action; existing operator-facing Coverage v2 surface may change data-driven rows/scopes. - **Affected routes/pages/actions/states/navigation/panel/provider surfaces**: existing Spec 418 Coverage v2 readiness/resource-type registry surface only, via active registry data. No route, navigation, action, panel provider, Blade, or Livewire file change is planned. - **No-impact class, if applicable**: not applicable if new rows/scopes render. This is a backend registry/config/persistence seed with possible existing-surface data impact. - **Native vs custom classification summary**: N/A. - **Shared-family relevance**: no UI shared-family change. - **State layers in scope**: registry/persistence state plus existing Coverage v2 operator table/filter/scope data; no shell/page/detail/URL state changes. - **Audience modes in scope**: N/A. - **Decision/diagnostic/raw hierarchy plan**: existing rendered hierarchy only. Registry metadata must avoid customer-proof wording, raw provider payloads, and proof semantics. - **Raw/support gating plan**: no raw payload or provider response storage in manifest metadata. - **One-primary-action / duplicate-truth control**: no actions. - **Handling modes by drift class or surface**: hard stop if runtime UI file edits, route, navigation, action, report, customer output, or rendered labels are needed. Data-driven rows/scopes on the existing Spec 418 surface require focused proof. - **Repository-signal treatment**: no UI audit registry update unless implementation amends scope. - **Special surface test profiles**: N/A. - **Required tests or manual smoke**: unit and feature/static guards plus focused existing-surface browser proof if new active rows/scopes render. - **Exception path and spread control**: none. - **Active feature PR close-out entry**: Guardrail / Exception / Smoke Coverage with either focused existing-surface proof or explicit proof that no rendered output changed. - **UI/Productization coverage decision**: Existing operator surface data impact only; no UI code change. - **Coverage artifacts to update**: active Spec 419 artifacts and implementation report only. Do not rewrite Spec 418. - **No-impact rationale**: no runtime UI files are planned, but active registry rows/scopes may render on the existing generic Spec 418 surface. - **Navigation / Filament provider-panel handling**: no panel/provider registration change. - **Screenshot or page-report need**: focused browser proof is required if rendered rows/scopes change. ## Product Surface Contract Plan - **Product Surface Contract reference**: `docs/product/standards/product-surface-contract.md`. - **No-legacy posture**: canonical Coverage v2 registry expansion; no compatibility exception. - **Page archetype and surface budget plan**: existing internal/operator Coverage v2 readiness and registry inspection surface. Use the existing table/filter/modal budget only. - **Technical Annex and deep-link demotion plan**: Registry metadata must not create customer-facing proof, raw provider output, or raw technical output. Source/catalog notes remain internal metadata. - **Canonical status vocabulary plan**: Use existing internal Coverage v2 enums plus explicit registry-only/detected wording. Do not introduce page-local M365 coverage truth. - **Product Surface exceptions**: none. - **Browser verification plan**: focused existing-surface browser proof if active rows/scopes render; otherwise document proof that rendered output did not change. - **Human Product Sanity plan**: required if active rows/scopes render; otherwise N/A with proof. - **Visible complexity outcome target**: slightly broader existing registry data, no new surface family. - **Implementation report target**: `specs/419-m365-tcm-workload-registry-expansion/implementation-report.md`. ## Filament / Livewire / Deployment Posture - **Livewire v4 compliance**: Livewire v4.x remains the required runtime. No Livewire code is planned. - **Panel provider registration location**: Laravel 12 panel providers remain in `apps/platform/bootstrap/providers.php`; no panel/provider change is planned. - **Global search posture**: no Filament Resource changes. If a Resource is unexpectedly added, stop and amend the spec. - **Destructive/high-impact action posture**: none. No action may mutate tenant/provider state, start capture, restore, certify, publish, export, or override claims. - **Asset strategy**: no assets. `filament:assets` not required unless scope is amended to register Filament assets. - **Testing plan**: focused unit/feature/static guard tests for registry, manifest/defaults, supported scopes, Claim Guard, default-scope preservation, no runtime capture, no mini-platform, no `tenant_id`, plus focused existing-surface browser proof if active rows/scopes render. - **Deployment impact**: migrations/check constraints likely if workload enum values are persisted with PostgreSQL checks. No env vars, queues, scheduler, storage, assets, or workers. Static registry/seed changes must be validated on staging before production. ## Shared Pattern & System Fit - **Cross-cutting feature marker**: yes at domain-contract level; no UI interaction family. - **Systems touched**: `TenantConfigurationResourceType`, `TenantConfigurationSupportedScope`, `ResourceTypeRegistry`, `ClaimGuard`, `Workload`, possible enum/check constraints, registry tests. - **Shared abstractions reused**: existing Coverage v2 registry/scope/claim guard paths. - **New abstraction introduced? why?**: none by default. A static manifest/config file is allowed only if it replaces scattered arrays and remains local reviewed data. - **Why the existing abstraction was sufficient or insufficient**: Existing registry is sufficient for resource type and supported-scope truth, but its current workload enum/check values are Intune-only. - **Bounded deviation / spread control**: no M365-specific engine, table, dashboard, capture service, provider framework, or UI presenter. ## OperationRun UX Impact - **Touches OperationRun start/completion/link UX?**: no. - **Central contract reused**: N/A. - **Delegated UX behaviors**: N/A. - **Surface-owned behavior kept local**: none. - **Queued DB-notification policy**: N/A. - **Terminal notification path**: N/A. - **Exception path**: none. ## Provider Boundary & Portability Fit - **Shared provider/platform boundary touched?**: yes. - **Provider-owned seams**: Microsoft workload names, TCM catalog source URLs, source aliases, Microsoft documentation review metadata, Graph fallback/beta source classification. - **Platform-core seams**: workload enum/check constraints, canonical resource type, source class, support state, coverage/evidence/identity/claim defaults, supported scopes, restore tier, Claim Guard. - **Neutral platform terms / contracts preserved**: provider connection, managed environment, resource type, workload, source class, supported scope, coverage level, evidence state, claim state, restore tier. - **Retained provider-specific semantics and why**: Microsoft M365 workload labels remain because this spec is explicitly an M365 TCM registry expansion. They stay source metadata and classification, not ownership truth. - **Bounded extraction or follow-up path**: document-in-feature if a static manifest path is introduced; follow-up-spec only for capture/compare/render/restore/certification packs. ## Constitution Check - Inventory/evidence truth: PASS. Registry entries are denominator/planning truth only and do not create evidence rows. - Read/write separation: PASS. No tenant/provider write action or restore path. - Graph contract path: PASS. No Graph calls; no hardcoded endpoints outside contracts. - Deterministic capabilities: PASS. Defaults are deterministic and testable. - RBAC-UX: PASS. No new UI/action. Future consumers must enforce existing RBAC. - Workspace isolation: PASS. Registry definitions are platform/product metadata; concrete evidence remains workspace/managed-environment scoped. - OperationRun: PASS. No queued/remote work. - Evidence/currentness: PASS. `not_captured` remains explicit for new entries. - Customer output: PASS. No Review Pack/report/customer surface changes. - Provider boundary: PASS with Microsoft source metadata bounded to registry fields. - Product Surface: PASS for data-driven existing-surface impact if active rows/scopes render; otherwise N/A only with proof that no rendered output changed. - Test governance: PASS. Unit/feature/pgsql-if-needed lanes named; no browser/heavy family. - Proportionality: PASS. Workload enum/registry expansion is justified by false-claim prevention and future pack consistency. - No premature abstraction: PASS if implementation reuses existing registry. - Persisted truth: PASS. Registry definitions are durable product metadata; no new core table by default. - Behavioral state: PASS with existing conservative support/coverage/evidence/claim/restore values. New source/support/restore states require amendment. - No legacy / pre-production lean: PASS. No compatibility paths, `tenant_id`, v1 adapters, fallback readers, or dual writes. ## Test Governance Check - **Test purpose / classification by changed surface**: Unit for enum/default manifest/Claim Guard; Feature for persisted registry/supported scopes/no-overclaim/no-runtime/no-mini-platform/no-tenant-id; PostgreSQL if migrations/check constraints change. - **Affected validation lanes**: fast-feedback, confidence, pgsql only if schema/check constraints are changed, focused browser if active rows/scopes render on the existing Spec 418 operator surface. - **Why this lane mix is the narrowest sufficient proof**: The change is registry/config/persistence truth. Unit and feature/static guards prove registry/default/claim behavior; focused browser proof is required only for the existing rendered surface when new active rows/scopes become visible. - **Narrowest proving command(s)**: - `cd apps/platform && ./vendor/bin/sail bin pint --dirty --format agent` - `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Unit/Support/TenantConfiguration/Spec419M365WorkloadRegistryTest.php tests/Unit/Support/TenantConfiguration/Spec419M365ResourceTypeManifestTest.php tests/Unit/Support/TenantConfiguration/Spec419M365ClaimGuardTest.php tests/Unit/Support/TenantConfiguration/Spec419M365RestoreTierDefaultTest.php tests/Unit/Support/TenantConfiguration/Spec419M365DocumentationStatusTest.php` - `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/TenantConfiguration/Spec419M365RegistryExpansionTest.php tests/Feature/TenantConfiguration/Spec419M365SupportedScopesTest.php tests/Feature/TenantConfiguration/Spec419M365NoOverclaimTest.php tests/Feature/TenantConfiguration/Spec419M365NoRuntimeCaptureTest.php tests/Feature/TenantConfiguration/Spec419M365NoMiniPlatformTest.php tests/Feature/TenantConfiguration/Spec419M365NoTenantIdTest.php` - `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Browser/Spec419M365RegistryOperatorSurfaceSmokeTest.php` if active rows/scopes render on the existing operator surface, or the repo-equivalent focused browser smoke path - `cd apps/platform && ./vendor/bin/sail php vendor/bin/pest -c phpunit.pgsql.xml tests/Feature/TenantConfiguration/Spec419M365RegistryExpansionTest.php tests/Feature/TenantConfiguration/Spec419M365SupportedScopesTest.php` if migrations/check constraints/indexes change - `git diff --check` - **Fixture / helper / factory / seed / context cost risks**: keep M365 registry factories/fixtures local or opt-in; do not make broad workspace/provider setup default. - **Expensive defaults or shared helper growth introduced?**: none expected. - **Heavy-family additions, promotions, or visibility changes**: focused existing-surface browser smoke only when rows/scopes render; no broad browser family by default. - **Surface-class relief / special coverage rule**: browser proof may be N/A only when implementation proves no rendered output changed. - **Closing validation and reviewer handoff**: implementation report records exact files, matrices, default-scope proof, no-overclaim proof, no-runtime proof, no-tenant-id proof, no-mini-platform proof, Product Surface/browser proof or N/A proof, commands/results, and deferred work. - **Budget / baseline / trend follow-up**: none expected unless PostgreSQL lane or guard tests become broad. - **Review-stop questions**: lane fit, partial/full catalog honesty, broad claim blocking, no runtime capture, no mini-platform, no `tenant_id`, no runtime UI code scope, and Product Surface data-impact proof. - **Escalation path**: document-in-feature for contained metadata/enum expansion; follow-up-spec for capture, UI, full-catalog import tooling, or new registry tables. - **Active feature PR close-out entry**: Guardrail / Exception / Smoke Coverage. - **Why no dedicated follow-up spec is needed**: this is the bounded registry denominator expansion; downstream capability packs are listed separately. ## Project Structure ### Documentation (this feature) ```text specs/419-m365-tcm-workload-registry-expansion/ +-- spec.md +-- plan.md +-- tasks.md +-- checklists/ +-- requirements.md ``` ### Source Code (likely affected in later implementation) ```text apps/platform/app/ +-- Services/TenantConfiguration/ | +-- ResourceTypeRegistry.php | +-- ClaimGuard.php +-- Support/TenantConfiguration/ | +-- Workload.php | +-- SourceClass.php | +-- SupportState.php | +-- CoverageLevel.php | +-- EvidenceState.php | +-- ClaimState.php | +-- RestoreTier.php +-- Models/ +-- TenantConfigurationResourceType.php +-- TenantConfigurationSupportedScope.php apps/platform/database/migrations/ +-- apps/platform/tests/ +-- Unit/Support/TenantConfiguration/ +-- Feature/TenantConfiguration/ ``` **Structure Decision**: Reuse the existing Coverage v2 registry and scope infrastructure. Prefer existing `metadata` JSONB and static registry definitions. Do not add workload-specific tables/classes or a parallel M365 registry service. ## Implementation Phases ### Phase 0 - Preflight Capture branch, HEAD, dirty state, activated skills, current Coverage v2 registry/service names, related completed-spec guardrail, and stop conditions. Confirm no unrelated dirty files before implementation. ### Phase 1 - Inspect Existing Registry Truth Inspect current enum values/check constraints, `ResourceTypeRegistry::defaultDefinitions()`, existing supported scope definitions, default scope selection, `ClaimGuard`, migrations, factories, and Spec 418 surface behavior. Map draft terms to current repo states before editing. ### Phase 2 - Tests First: Workload And Manifest Defaults Add focused tests for workload values, representative M365 entries, Defender/Purview supported-scope metadata, documentation status metadata, full-vs-seeded catalog markers, default scope preservation, and conservative default support/coverage/evidence/claim/restore states. ### Phase 3 - Tests First: Claim And Guard Boundaries Add Claim Guard/no-overclaim tests and static/feature guards for no runtime docs fetch, no Graph/TCM calls, no concrete evidence rows, no `tenant_id`, and no mini-platform tables/classes. ### Phase 4 - Registry And Scope Expansion Expand workload enum/check values, static resource type defaults or manifest/config, supported-scope planning entries, aggregate M365 workload metadata, default-scope safeguards, metadata conventions, and idempotent sync/upsert behavior. ### Phase 5 - Claim Guard Expansion Block broad M365, certified, restore-ready, complete-tenant, all-resource, and unscoped percent claims. Allow only explicit internal registry-only denominator-scoped wording when safe. ### Phase 6 - Persistence And PostgreSQL Validation If migrations/check constraints/columns change, keep them reversible and narrow. Run focused PostgreSQL validation for changed TenantConfiguration paths. ### Phase 7 - Product Surface Data-Impact Verification Confirm no UI route, page, navigation, Filament provider, action, report, download, customer output, or browser-visible claim changed. If active registry rows/scopes render on the existing Spec 418 surface, run focused browser proof that the page remains internal/operator-only, no broad M365 coverage label appears, default scope behavior is intentional, and no capture/restore/certify/report/download action appears. If runtime UI code changes are required, stop and amend artifacts before implementation continues. ### Phase 8 - Validation And Implementation Report Run Pint dirty, focused unit/feature tests, PostgreSQL lane if required, and `git diff --check`. Complete implementation report with workload/resource matrices and required no-overclaim/no-runtime/no-tenant-id/no-mini-platform proof. ## Stop Conditions Stop and update `spec.md`, `plan.md`, and `tasks.md` before continuing if any of these appear: - Capture, compare, render, restore, apply, certify, publish, export, customer report, Review Pack, or dashboard behavior is needed. - Graph/TCM/provider remote work or runtime Microsoft documentation fetch is needed. - A new UI route/page/navigation/action/table/form/rendered label is needed. - Existing Coverage v2 operator surface default scope changes without explicit Product Surface/browser proof. - A full static catalog import is too large for bounded review and the implementation cannot mark the catalog partial. - A new `SourceClass`, `SupportState`, or `RestoreTier` value is needed without proportionality proof and tests. - `tenant_id` appears as Coverage v2 ownership truth. - A workload-specific table, model, engine, provider framework, or mini-platform appears. - Partial catalog wording implies full M365 coverage. - A broad M365/certified/restore-ready/customer claim needs to be allowed.