## Summary
- implement Spec 179 to make tenant lifecycle, provider consent, and provider verification the primary truth axes on the targeted Filament surfaces
- demote legacy tenant app status and legacy provider status and health to diagnostic-only roles, add centralized badge mappings for provider consent and verification, and keep provider connections excluded from global search
- add the full Spec 179 artifact set under `specs/179-provider-truth-cleanup/` plus focused Pest coverage for tenant truth cleanup, provider truth cleanup, RBAC, discovery safety, and badge semantics
- fix the numeric out-of-scope tenant route regression so inaccessible `/admin/tenants/{id}` paths return `404 Not Found` instead of `500`
## Testing
- `vendor/bin/sail artisan test --compact tests/Feature/Filament/TenantLifecycleStatusDomainSeparationTest.php`
- `vendor/bin/sail artisan test --compact tests/Feature/Filament/TenantTruthCleanupSpec179Test.php`
- `vendor/bin/sail artisan test --compact tests/Feature/Filament/ProviderConnectionsDbOnlyTest.php`
- `vendor/bin/sail artisan test --compact tests/Feature/ProviderConnections/ProviderConnectionTruthCleanupSpec179Test.php`
- `vendor/bin/sail artisan test --compact tests/Feature/ProviderConnections/RequiredFiltersTest.php`
- `vendor/bin/sail artisan test --compact tests/Feature/Tenants/TenantProviderConnectionsCtaTest.php`
- `vendor/bin/sail artisan test --compact tests/Feature/Rbac/TenantResourceAuthorizationTest.php`
- `vendor/bin/sail artisan test --compact tests/Feature/ProviderConnections/ProviderConnectionListAuthorizationTest.php`
- `vendor/bin/sail artisan test --compact tests/Feature/ProviderConnections/ProviderConnectionAuthorizationTest.php`
- `vendor/bin/sail artisan test --compact tests/Feature/Rbac/AdminGlobalSearchContextSafetyTest.php`
- `vendor/bin/sail artisan test --compact tests/Feature/Filament/TenantGlobalSearchLifecycleScopeTest.php`
- `vendor/bin/sail artisan test --compact tests/Feature/Filament/TenantScopingTest.php`
- `vendor/bin/sail artisan test --compact tests/Unit/Badges/TenantBadgesTest.php`
- `vendor/bin/sail artisan test --compact tests/Unit/Badges/ProviderConnectionBadgesTest.php`
## Manual validation
- integrated-browser smoke on `/admin/tenants`, tenant detail, `/admin/provider-connections`, provider detail, and provider edit
- verified out-of-scope tenant and provider URLs return `404 Not Found` with the current session
## Notes
- branch: `179-provider-truth-cleanup`
- commit: `e54c6632`
- target: `dev`
Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #207
8.5 KiB
Phase 0 Research: Provider Readiness Source-of-Truth Cleanup
Decision: Remove legacy app_status from tenant list primary truth and do not replace it with a new row-level readiness badge in this slice
Rationale: The current tenant list exposes app_status as a default-visible badge and filter even though the feature goal is to stop projecting frozen or semantically stale provider truth from tenant-level legacy fields. A new per-row provider readiness badge would require choosing one provider connection, compressing multi-connection state, and implicitly inventing a readiness model. The narrowest safe move is to let the tenant list remain lifecycle-led and to consciously omit provider readiness from the row when it cannot be stated without semantic overreach.
Alternatives considered:
- Introduce a new tenant readiness badge on the list: rejected because it would create a new semantic layer before the later readiness spec.
- Keep
app_statusbut toggle it off by default: rejected because the spec explicitly warns that hidden-by-default legacy truth still leaks back into operator interpretation.
Decision: Reuse the existing tenant Provider section, but repoint it from legacy status and health_status to consent and verification
Rationale: Tenant detail already has a dedicated Provider section backed by TenantResource::providerConnectionState() and the shared Blade entry provider-connection-state.blade.php. That section is the narrowest place to present current provider truth without inventing a new widget or presenter. The helper should continue to resolve the default Microsoft provider connection or a missing-connection state, but its leading fields should become consent_status and verification_status, with legacy status and health demoted to optional diagnostics.
Alternatives considered:
- Build a new tenant provider summary widget: rejected because the current section already exists and the spec does not justify another surface layer.
- Remove the Provider section entirely and rely only on the verification widget: rejected because the spec requires clear consent and verification semantics, and the verification widget is a deep-dive report, not the only summary contract.
Decision: Keep TenantVerificationReport as the verification deep-dive and do not turn TenantOperability into provider truth
Rationale: TenantVerificationReport already gives tenant detail a DB-only verification deep-dive using the latest provider.connection.check run. TenantOperabilityService and verificationReadinessOutcome() answer whether a verification action may be started in a given lifecycle and capability context, not whether the provider is operationally healthy. The cleanup should continue to use the verification report for stored verification detail and must not promote operability gating into readiness truth.
Alternatives considered:
- Use
TenantOperabilityDecisionas a tenant provider readiness signal: rejected because it mixes lifecycle and permission gating with provider-state truth. - Collapse verification detail into the Provider section and remove the verification widget: rejected because the widget already provides the deeper stored-report surface and no new readiness model is needed.
Decision: Promote consent_status and verification_status to the default-visible provider connection list axes
Rationale: The provider connections list currently defaults to status and health_status, even though the model already stores consent_status and verification_status and the resource already has label helpers for them. Surfacing consent and verification in the table is the clearest way to answer whether a connection is consented and whether it has been checked. Legacy status and health_status may remain available as hidden diagnostics for compatibility, but they should stop being the primary scan path.
Alternatives considered:
- Keep
statusandhealth_statusvisible because they are shorter to scan: rejected because that preserves the competing-truth problem the spec exists to remove. - Remove legacy columns completely from the list: rejected because the fields may still help diagnostics and internal compatibility during the transition.
Decision: Split provider connection detail and edit surfaces into current state versus diagnostics instead of one equal-weight Status section
Rationale: The current provider connection infolist and form both render consent, verification, legacy status, and health at the same hierarchy level in one Status section. The cleanup should create a primary current-state section for consent and verification, and a separate diagnostics section for legacy status, health, migration review, and last-error metadata. This keeps the existing resource and existing fields while making the leading truth explicit.
Alternatives considered:
- Keep one Status section and merely reorder the fields: rejected because equal visual grouping would still overstate the legacy fields.
- Delete legacy fields from detail and edit immediately: rejected because the spec allows the fields to remain as long as they stop acting like leading truth.
Decision: Extend the existing badge catalog with narrow provider consent and provider verification mappings while keeping legacy badge domains diagnostic-only
Rationale: BADGE-001 requires status-like values to render through BadgeCatalog and BadgeRenderer. BadgeCatalog already centralizes legacy TenantAppStatus, ProviderConnectionStatus, and ProviderConnectionHealth mappings, so the narrowest compliant move is to add centralized provider consent and provider verification mappings inside the existing badge system. This keeps lifecycle, consent, verification, and legacy diagnostics on one central semantic path without creating a synthetic Ready or ProviderReadiness domain.
Alternatives considered:
- Continue using plain labels or local helper output for consent and verification: rejected because BADGE-001 requires centralized status-like badge semantics.
- Add a new
ProviderReadinessbadge domain: rejected because it would front-run the later readiness spec and violate proportionality. - Remove legacy badge mappers entirely: rejected because diagnostic surfaces and internal compatibility still reference them.
Decision: Keep legacy persisted fields and current projection writers intact in this slice
Rationale: ProviderConnection still projects status and health_status during classification and enable or disable flows, and tenant records still persist app_status. The spec explicitly forbids a schema change or aggressive technical removal. The correct scope is presentation cleanup first, not domain-field deletion.
Alternatives considered:
- Drop legacy columns now: rejected because existing jobs, projections, audit metadata, and compatibility paths still write or reference them.
- Stop writing the fields immediately as part of this spec: rejected because it would turn a surface-truth cleanup into a broader behavioral change.
Decision: Update provider list filters to follow the leading truth hierarchy
Rationale: The current provider list filter set centers status and health_status. If the table’s leading truth becomes consent and verification, the list’s core filters should follow the same hierarchy. Legacy filters may remain only as explicitly diagnostic filters, not as the primary filter language for current provider state.
Alternatives considered:
- Leave legacy filters untouched while changing only columns: rejected because that keeps the old semantics in the main operator interaction path.
- Remove all legacy filters outright: rejected because diagnostic filtering may still be useful while the fields remain persisted.
Decision: Extend existing truth-separation tests and add one focused provider-surface truth test instead of inventing a large new test harness
Rationale: The repo already has TenantLifecycleStatusDomainSeparationTest, ProviderConnectionsDbOnlyTest, RequiredFiltersTest, and badge tests. The spec’s main risk is business-truth regression on a few surfaces. Focused feature tests that assert default-visible fields, section hierarchy, and filter names are enough to protect this contract without creating a broad new testing abstraction.
Alternatives considered:
- Rely on manual browser inspection only: rejected because truth regressions on default-visible surfaces are easy to reintroduce.
- Create a large generic presenter test framework first: rejected because the cleanup intentionally avoids adding a new presenter layer.