# Implementation Plan: Provider Readiness Source-of-Truth Cleanup **Branch**: `179-provider-truth-cleanup` | **Date**: 2026-04-04 | **Spec**: `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/specs/179-provider-truth-cleanup/spec.md` **Input**: Feature specification from `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/specs/179-provider-truth-cleanup/spec.md` **Note**: This plan keeps the current tenant and provider connection model intact. It cleans up which already-stored fields are allowed to act as leading operator truth and explicitly avoids introducing a new readiness model, new persistence, or a new semantic framework. ## Summary Harden the existing tenant and provider operator surfaces so lifecycle, consent, and verification become the only leading truth axes on the targeted pages. The implementation will remove `Tenant.app_status` from primary tenant surfaces, repoint the tenant-detail Provider section from legacy connection `status` and `health_status` to current consent and verification, and reorganize provider connection list, view, and edit surfaces so legacy projections become secondary diagnostics. The narrowest safe tenant-list choice in this slice is to omit a new row-level provider readiness signal rather than invent one. The provider list will instead promote consent and verification to the default-visible connection-state axes, while existing legacy fields remain persisted and available only as secondary diagnostics. ## Technical Context **Language/Version**: PHP 8.4.15 **Primary Dependencies**: Laravel 12, Filament v5, Livewire v4, Pest v4, existing `TenantResource`, `ProviderConnectionResource`, `TenantVerificationReport`, `BadgeCatalog`, `BadgeRenderer`, `TenantOperabilityService`, `ProviderConsentStatus`, `ProviderVerificationStatus`, and shared provider-state Blade partials **Storage**: PostgreSQL unchanged; no new table, column, or persisted artifact is introduced **Testing**: Pest 4 feature tests and Livewire-style Filament surface tests through Laravel Sail, reusing existing tenant and provider surface tests plus focused Spec 179 truth-cleanup coverage **Target Platform**: Laravel monolith web application running in Sail locally and containerized Linux environments in staging and production **Project Type**: web application **Performance Goals**: Keep existing DB-only render guarantees, avoid adding uncontrolled per-row provider queries to the tenant list, and keep provider and tenant detail rendering bounded to already-loaded or single-record derived state **Constraints**: No new readiness enum or score, no new persisted truth, no new presenter or taxonomy framework, no authorization widening, no cross-tenant leakage, no new asset pipeline work, and no false equivalence between `active`, `connected`, `consented`, and `ready` **Scale/Scope**: Two existing Filament resources, five operator-facing surfaces, one existing tenant provider-summary partial, one tenant verification widget, three legacy badge domains under review, and a focused regression pack around truth hierarchy and filter semantics ## Constitution Check *GATE: Passed before Phase 0 research. Re-check after Phase 1 design.* | Principle | Pre-Research | Post-Design | Notes | |-----------|--------------|-------------|-------| | Inventory-first / snapshots-second | PASS | PASS | The feature changes only read-time presentation of already-stored tenant and provider truth. No snapshot or inventory contract changes are introduced. | | Read/write separation | PASS | PASS | No new mutation path, preview flow, or destructive action is added. Existing provider and tenant mutations remain unchanged. | | Graph contract path | N/A | N/A | No Graph contract or `config/graph_contracts.php` change is required. | | Deterministic capabilities | PASS | PASS | Existing provider view/manage and tenant membership rules remain authoritative. | | RBAC-UX authorization semantics | PASS | PASS | All touched surfaces remain in `/admin`, preserve tenant and workspace scoping, keep non-members at `404`, and keep member-without-capability behavior unchanged. | | Workspace and tenant isolation | PASS | PASS | No new route or query broadens tenant visibility. Canonical tenantless provider routes remain tenant-aware and scoped. | | Run observability / Ops-UX | PASS | PASS | No new `OperationRun` type or queueing behavior is introduced. Existing verification and provider-run actions keep their current semantics. | | Data minimization | PASS | PASS | No new persisted data, logs, or surface payloads are introduced. | | Proportionality / no premature abstraction | PASS | PASS | The plan reuses existing resources, helpers, and partials instead of adding a new readiness service, presenter, or DTO layer. | | Persisted truth / behavioral state | PASS | PASS | No new state family or table is planned. Existing legacy fields remain persisted for compatibility but lose leading surface prominence. | | UI semantics / few layers | PASS | PASS | The plan uses direct lifecycle, consent, and verification truth instead of introducing a new readiness interpreter. | | Badge semantics (BADGE-001) | PASS | PASS | Existing badge semantics remain centralized. Where provider consent or verification need status-like badges, the plan extends `BadgeCatalog` and `BadgeRenderer` through narrow centralized mappings instead of page-local labels or a synthetic readiness domain. | | Filament-native UI / Action Surface Contract | PASS | PASS | Existing Filament resources, sections, tables, infolists, view entries, and action groups are reused. No redundant inspect action or empty group is introduced. | | Filament UX-001 | PASS | PASS | No new screen type is introduced. Existing list, view, and edit surfaces remain within current Filament layout patterns while truth hierarchy changes inside those sections. | | Filament v5 / Livewire v4 compliance | PASS | PASS | The design stays within the current Filament v5 and Livewire v4 stack. | | Provider registration location | PASS | PASS | No provider or panel registration change is required; Laravel 11+ provider registration remains in `bootstrap/providers.php`. | | Global search hard rule | PASS | PASS | `TenantResource` remains globally searchable and already has view and edit pages. `ProviderConnectionResource` remains non-globally-searchable. | | Destructive action safety | PASS | PASS | No new destructive action is introduced. Existing archive, restore, force-delete, credential deletion, and disable flows remain confirmed and capability-gated. | | Asset strategy | PASS | PASS | No new asset bundle or deploy-time `filament:assets` change is required. | | Testing truth (TEST-TRUTH-001) | PASS | PASS | The plan adds focused regression coverage for default-visible truth hierarchy, filter semantics, and non-readiness interpretation. | ## Phase 0 Research Research outcomes are captured in `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/specs/179-provider-truth-cleanup/research.md`. Key decisions: - Remove tenant-list `app_status` prominence entirely instead of replacing it with a premature readiness badge. - Reuse the existing tenant-detail Provider section and `providerConnectionState()` helper, but make consent and verification the leading provider summary fields. - Keep `TenantVerificationReport` as the verification deep-dive and do not repurpose tenant operability gating as provider readiness truth. - Promote provider connection `consent_status` and `verification_status` to the list’s default-visible current-state axes. - Split provider connection detail and edit pages into current-state truth versus diagnostics rather than one mixed `Status` section. - Keep legacy persisted fields and existing projection writers intact; this slice is presentation cleanup only. - Update provider filters so the main operator filter language follows consent and verification rather than legacy status projections. - Extend existing surface-truth tests and add one focused provider-surface truth test instead of introducing a broader test framework. ## Phase 1 Design Design artifacts are created under `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/specs/179-provider-truth-cleanup/`: - `research.md`: planning decisions and rejected alternatives - `data-model.md`: persistent source truths and derived surface truth contracts - `contracts/provider-truth-cleanup.openapi.yaml`: internal route and surface contract for tenant and provider truth cleanup - `quickstart.md`: focused implementation and validation workflow Design highlights: - The tenant list will remain lifecycle-led and will not gain a new speculative readiness badge in this slice. - `TenantResource::providerConnectionState()` and the shared Provider infolist entry will be repointed to current consent and verification truth, with legacy `status` and `health_status` relegated to diagnostics. - `Tenant.app_status` will be removed from the leading tenant-list and tenant-detail operator contract rather than simply hidden behind a toggle. - `ProviderConnectionResource` will make consent and verification the default-visible list columns and primary detail/edit state section, while legacy status and health become secondary diagnostics. - No new readiness framework, readiness enum, or provider-state presenter will be introduced. Existing badge infrastructure will be extended only as needed with narrow centralized mappings for provider consent and provider verification, while legacy lifecycle and diagnostic mappings remain centralized. - Existing provider mutation, verification, and authorization behavior stays intact; only the truth hierarchy on touched surfaces changes. ## Phase 1 — Agent Context Update Run after artifact generation: - `.specify/scripts/bash/update-agent-context.sh copilot` ## Project Structure ### Documentation (this feature) ```text specs/179-provider-truth-cleanup/ ├── spec.md ├── plan.md ├── research.md ├── data-model.md ├── quickstart.md ├── contracts/ │ └── provider-truth-cleanup.openapi.yaml ├── checklists/ │ └── requirements.md └── tasks.md ``` ### Source Code (repository root) ```text app/ ├── Filament/ │ ├── Resources/ │ │ ├── TenantResource.php │ │ ├── TenantResource/ │ │ │ └── Pages/ │ │ │ ├── ListTenants.php │ │ │ └── ViewTenant.php │ │ ├── ProviderConnectionResource.php │ │ └── ProviderConnectionResource/ │ │ └── Pages/ │ │ ├── ListProviderConnections.php │ │ ├── ViewProviderConnection.php │ │ └── EditProviderConnection.php │ └── Widgets/ │ └── Tenant/ │ └── TenantVerificationReport.php ├── Models/ │ ├── Tenant.php │ ├── ProviderConnection.php │ ├── ProviderCredential.php │ └── OperationRun.php ├── Services/ │ └── Tenants/ │ └── TenantOperabilityService.php └── Support/ ├── Badges/ │ ├── BadgeCatalog.php │ ├── BadgeDomain.php │ └── Domains/ │ ├── TenantAppStatusBadge.php │ ├── ProviderConnectionStatusBadge.php │ └── ProviderConnectionHealthBadge.php ├── Providers/ │ ├── ProviderConsentStatus.php │ └── ProviderVerificationStatus.php └── Tenants/ └── TenantLifecycle.php resources/ └── views/ └── filament/ ├── infolists/ │ └── entries/ │ └── provider-connection-state.blade.php └── widgets/ └── tenant/ └── tenant-verification-report.blade.php tests/ ├── Feature/ │ ├── Filament/ │ │ ├── TenantLifecycleStatusDomainSeparationTest.php │ │ ├── TenantTruthCleanupSpec179Test.php │ │ └── ProviderConnectionsDbOnlyTest.php │ ├── ProviderConnections/ │ │ ├── ProviderConnectionTruthCleanupSpec179Test.php │ │ ├── ProviderConnectionListAuthorizationTest.php │ │ ├── ProviderConnectionAuthorizationTest.php │ │ └── RequiredFiltersTest.php │ ├── Tenants/ │ │ └── TenantProviderConnectionsCtaTest.php │ └── Rbac/ │ └── TenantResourceAuthorizationTest.php └── Unit/ └── Badges/ ├── TenantBadgesTest.php └── ProviderConnectionBadgesTest.php ``` **Structure Decision**: Keep the feature entirely inside the existing Laravel/Filament monolith. Update the current resources, shared provider-summary partial, tenant verification widget contract, and focused test files instead of creating a new provider-readiness subsystem. ## Complexity Tracking > No Constitution Check violations are planned. No exception or bloat trigger is currently justified. | Violation | Why Needed | Simpler Alternative Rejected Because | |-----------|------------|-------------------------------------| | — | — | — | ## Proportionality Review > No new enum, persisted entity, abstraction layer, taxonomy, or cross-domain UI framework is planned in this slice. - **Current operator problem**: Current tenant and provider surfaces let legacy status fields sound more authoritative than current lifecycle, consent, and verification truth. - **Existing structure is insufficient because**: The model already stores better truth, but the resources and shared provider-summary partial still foreground the wrong fields. - **Narrowest correct implementation**: Rewire the existing tenant and provider surfaces to consume the current truth hierarchy without inventing a new readiness model or dropping persisted compatibility fields. - **Ownership cost created**: Focused resource updates, one shared partial rewrite, and a small regression suite for surface truth hierarchy. - **Alternative intentionally rejected**: A new readiness enum, new presenter layer, or schema cleanup. - **Release truth**: Current-release truth cleanup. ## Implementation Strategy ### Phase A — Remove Legacy Tenant App Status From Primary Tenant Surfaces **Goal**: Make tenant lifecycle the only default-visible tenant status axis on the tenant list and tenant detail identity block. | Step | File | Change | |------|------|--------| | A.1 | `app/Filament/Resources/TenantResource.php` | Remove `app_status` from the default-visible tenant list column set and stop treating it as a primary list filter. Keep lifecycle and other tenant identity fields intact. | | A.2 | `app/Filament/Resources/TenantResource.php` | Remove `app_status` from the tenant-detail `Identity` section’s leading status contract rather than simply hiding it behind a toggle. | | A.3 | `tests/Feature/Filament/TenantLifecycleStatusDomainSeparationTest.php` and `tests/Feature/Filament/TenantTruthCleanupSpec179Test.php` | Prove tenant list and tenant detail no longer use `app_status` as primary operator truth. | ### Phase B — Repoint Tenant Detail Provider Summary To Current Provider Truth **Goal**: Make tenant detail show provider consent and verification rather than legacy provider connection status and health. | Step | File | Change | |------|------|--------| | B.1 | `app/Filament/Resources/TenantResource.php` | Refactor `providerConnectionState()` to derive provider summary fields from the chosen Microsoft connection’s `consent_status`, `verification_status`, `is_default`, `last_health_check_at`, and `last_error_reason_code`, while keeping missing-connection handling explicit. | | B.2 | `resources/views/filament/infolists/entries/provider-connection-state.blade.php` | Rewrite the Provider summary entry so consent and verification are the leading displayed values and legacy `status` and `health_status` move to optional diagnostics. | | B.3 | `app/Filament/Widgets/Tenant/TenantVerificationReport.php` and its Blade view | Keep the existing verification widget as the deep-dive stored-report surface and ensure the surrounding tenant-detail semantics do not duplicate or contradict it. | | B.4 | `tests/Feature/Tenants/TenantProviderConnectionsCtaTest.php` and `tests/Feature/Filament/TenantTruthCleanupSpec179Test.php` | Prove the tenant-detail provider CTA remains correct and the Provider section no longer implies readiness from legacy fields. | ### Phase C — Promote Consent And Verification On Provider Connection List Surfaces **Goal**: Make the provider connections list answer the current provider-state questions from consent and verification first. | Step | File | Change | |------|------|--------| | C.1 | `app/Filament/Resources/ProviderConnectionResource.php` | Add default-visible consent and verification columns using existing label helpers and keep connection type and default designation as supporting facts. | | C.2 | `app/Filament/Resources/ProviderConnectionResource.php` | Move legacy `status` and `health_status` columns to toggleable or explicitly diagnostic visibility and adjust visible-column defaults accordingly. | | C.3 | `app/Filament/Resources/ProviderConnectionResource.php` | Replace or demote core `status` and `health_status` filters so the leading filter language becomes consent and verification, with legacy filters retained only if clearly diagnostic. | | C.4 | `tests/Feature/ProviderConnections/RequiredFiltersTest.php`, `tests/Feature/Filament/ProviderConnectionsDbOnlyTest.php`, and `tests/Feature/ProviderConnections/ProviderConnectionTruthCleanupSpec179Test.php` | Prove default-visible columns and filters follow the new hierarchy without breaking DB-only rendering. | ### Phase D — Split Provider Connection View And Edit Into Current State Versus Diagnostics **Goal**: Prevent provider connection detail and edit pages from showing current and legacy status axes as peers. | Step | File | Change | |------|------|--------| | D.1 | `app/Filament/Resources/ProviderConnectionResource.php` | Replace the mixed `Status` infolist section with a primary current-state section for consent and verification and a secondary diagnostics section for legacy status, legacy health, migration review, and last-error fields. | | D.2 | `app/Filament/Resources/ProviderConnectionResource.php` | Apply the same separation to the edit form’s read-only state context so mutations happen in the presence of current consent and verification truth, with legacy fields shown only as diagnostics if retained. | | D.3 | `tests/Feature/ProviderConnections/ProviderConnectionTruthCleanupSpec179Test.php` | Prove current state and diagnostics are visually and semantically separate on provider connection view and edit surfaces. | ### Phase E — Preserve Central Truth Mapping Without Adding A New Badge Framework **Goal**: Keep badge and presentation semantics centralized while avoiding a new readiness taxonomy. | Step | File | Change | |------|------|--------| | E.1 | `app/Support/Badges/BadgeCatalog.php`, `app/Support/Badges/BadgeDomain.php`, `app/Support/Badges/Domains/ProviderConsentStatusBadge.php`, `app/Support/Badges/Domains/ProviderVerificationStatusBadge.php`, and existing legacy badge-domain classes | Add narrow centralized badge mappings for `consent_status` and `verification_status`, keep legacy app-status or connection-status mappings diagnostic-only, and do not introduce a synthetic readiness badge domain in this slice. | | E.2 | `tests/Unit/Badges/TenantBadgesTest.php` and `tests/Unit/Badges/ProviderConnectionBadgesTest.php` | Update unit badge coverage for the centralized provider consent and provider verification mappings and keep legacy diagnostic mapping expectations explicit. | ### Phase F — Protect Authorization, DB-only Rendering, And Formatting **Goal**: Keep the cleanup semantically tight and operationally safe. | Step | File | Change | |------|------|--------| | F.1 | Existing tenant and provider authorization tests | Re-run and extend current authorization coverage only where surface visibility or filter behavior changes could affect scope boundaries. | | F.2 | Focused Pest verification pack | Add or update tests proving legacy-status suppression, provider current-state promotion, filter truth alignment, DB-only rendering, and denial semantics. | | F.3 | `vendor/bin/sail bin pint --dirty --format agent` and focused Pest runs | Apply formatting and run the minimum Sail-first verification pack before implementation is considered complete. | ## Key Design Decisions ### D-001 — Do not create a new readiness model in a truth-cleanup spec The current feature exists to stop misleading primary surfaces, not to coin a new readiness taxonomy. Lifecycle, consent, and verification already provide the necessary authoritative inputs. ### D-002 — The tenant list should consciously omit provider readiness rather than compress it unsafely Any row-level provider readiness badge would require non-trivial aggregation and semantic compression across provider connections. Omitting that signal is safer and matches the spec’s fallback allowance. ### D-003 — Reuse the existing tenant-detail Provider section instead of adding another summary layer `providerConnectionState()` and the shared infolist entry already own the tenant-detail provider summary. Repointing that contract is narrower than building a new widget or presenter. ### D-004 — Provider detail pages need one current-state section and one diagnostics section Equal-weight status groupings are the core current UI problem. Separating current state from diagnostics is the smallest structural change that makes the leading truth obvious. ### D-005 — Keep legacy persisted fields, but remove their authority on targeted surfaces The model and some jobs still project `status` and `health_status`, and tenant `app_status` still exists in storage. This slice preserves compatibility while eliminating misleading prominence. ### D-006 — Do not use tenant operability gating as provider readiness truth `TenantOperabilityService` answers lifecycle and capability eligibility questions. It is not a substitute for consent or verification health and must stay separate in both implementation and messaging. ## Risk Assessment | Risk | Impact | Likelihood | Mitigation | |------|--------|------------|------------| | Removing `app_status` from the tenant list may reduce scanability before a later readiness spec lands | Medium | Medium | Keep the tenant list lifecycle-led, keep the provider CTA obvious on tenant detail, and treat any future list signal as follow-up work rather than a rushed addition now. | | Tenant detail may still show the wrong provider connection when multiple connections exist | High | Medium | Continue to prefer the default Microsoft connection when present and render an explicit missing or configured state rather than an optimistic readiness label. | | Provider list could retain legacy semantics if filters change more slowly than columns | High | Medium | Update both default-visible columns and core filter set together and protect them with `RequiredFiltersTest` plus a focused truth test. | | Existing tests currently asserting `app_status` visibility will fail noisily | Medium | High | Update the intentional tenant truth-separation test first and add a spec-specific replacement that encodes the new contract. | | New tenant-list provider queries could create unnecessary DB cost | Medium | Low | Avoid adding a new per-row provider signal on the tenant list in this slice. | ## Test Strategy - Update `tests/Feature/Filament/TenantLifecycleStatusDomainSeparationTest.php` so it protects the new tenant truth hierarchy instead of asserting legacy `app_status` prominence. - Add `tests/Feature/Filament/TenantTruthCleanupSpec179Test.php` to cover tenant list default-visible truth and tenant detail Provider section semantics. - Add `tests/Feature/ProviderConnections/ProviderConnectionTruthCleanupSpec179Test.php` to cover provider connection list, view, and edit truth hierarchy. - Update `tests/Feature/ProviderConnections/RequiredFiltersTest.php` so the core filter contract follows consent and verification instead of legacy status or health. - Update `tests/Feature/Filament/ProviderConnectionsDbOnlyTest.php` so visible-column expectations and DB-only rendering stay valid after the list contract changes. - Keep `tests/Feature/Tenants/TenantProviderConnectionsCtaTest.php` and existing authorization tests in the focused pack so route and scope semantics do not regress. - Keep tenant global-search safety and provider-connection global-search exclusion in the focused pack so discovery behavior does not regress. - Finish the feature with an explicit no-migration and no-new-persistence diff validation gate in addition to runtime regression coverage. - Run the focused Sail-first pack defined in `quickstart.md`, then run `vendor/bin/sail bin pint --dirty --format agent` before closing implementation. ## Constitution Check (Post-Design) Re-check result: PASS. - Livewire v4.0+ compliance: preserved because the plan stays within the current Filament v5 + Livewire v4 resources, widgets, and pages. - Provider registration location: unchanged; no panel or provider registration work is needed beyond the existing `bootstrap/providers.php` setup. - Global-searchable resources: `TenantResource` remains globally searchable and already has both view and edit pages; `ProviderConnectionResource` remains non-globally-searchable, so no global-search conflict is introduced. - Destructive actions: no new destructive action is added. Existing tenant archive, restore, and force-delete flows and existing provider credential or connection-state mutations remain confirmation-protected and capability-gated. - Asset strategy: unchanged; no new assets are introduced and the deploy-time `php artisan filament:assets` process remains unaffected. - Testing plan: focused Pest coverage will protect tenant legacy-status suppression, tenant-detail provider summary truth, provider current-state promotion, filter hierarchy, DB-only rendering, and unchanged authorization boundaries.