TenantAtlas/specs/394-provider-freshness-permission-semantics/plan.md
ahmido a6c064cbf1 feat: improve provider readiness semantics and freshness guidance (#465)
Automated PR created by Codex via Gitea API.

Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #465
2026-06-21 17:20:10 +00:00

27 KiB

Implementation Plan: Spec 394 - Provider Freshness & Permission Semantics v1

Branch: 394-provider-freshness-permission-semantics | Date: 2026-06-21 | Spec: specs/394-provider-freshness-permission-semantics/spec.md Input: Feature specification from specs/394-provider-freshness-permission-semantics/spec.md

Summary

Create or consolidate one canonical provider readiness resolver that owns provider connection state, verification freshness, required permission coverage, row state, count invariants, recommended action, and capability-aware action visibility. Replace visible local provider health/readiness/count logic in Provider Connections, Required Permissions, Environment Dashboard, Workspace Overview, Governance Inbox, and direct downstream readiness consumers where repo-real.

This is a clean-cut correctness fix. Do not preserve Healthy while stale, Present 0, connected-as-ready, wrong-scope grants, stale-grant coverage, or compatibility shims.

Technical Context

Language/Version: PHP 8.4.15 Primary Dependencies: Laravel 12.52, Filament 5.2.1, Livewire 4.1.4, Pest 4.3.1 Storage: PostgreSQL. No migration expected by default; use existing provider_connections, managed_environment_permissions, and verification OperationRun/report context first. Testing: Pest 4 Unit, Feature, Filament/Livewire, bounded Browser smoke Validation Lanes: fast-feedback, confidence, browser; PostgreSQL only if schema/query features require it Target Platform: Laravel Sail local; Dokploy container deployment for staging/production Project Type: Laravel monolith under apps/platform Performance Goals: Resolver decisions must be DB-only during render, deterministic, scoped, and avoid external calls Constraints: no Graph calls during render, no UI expansion, no raw provider payload in default UI, no legacy aliases/shims, no new dashboard/onboarding wizard Scale/Scope: existing provider, permission, dashboard, overview, inbox, and readiness consumers

Technical Approach

  1. Inventory all provider readiness, provider health, verification freshness, required permission count, grant, capability, and downstream readiness selectors.
  2. Define one canonical provider readiness resolver/result contract over existing provider connection, permission, and verification evidence.
  3. Prove resolver behavior first with unit tests for fresh, stale, failed, unknown, missing, blocked, wrong-scope, stale-grant, and count-invariant cases.
  4. Replace local visible state and count derivation in affected product surfaces.
  5. Remove or delegate old local provider health/readiness helpers.
  6. Replace ambiguous UI labels such as Present with canonical labels.
  7. Enforce RBAC and technical-detail gating through existing capability/policy patterns.
  8. Update affected fixtures/tests to assert canonical behavior.
  9. Run focused browser smoke and artifact close-out checks.

Preferred shape:

ProviderConnection + ManagedEnvironmentPermission + verification OperationRun/report
    -> ProviderReadinessResolver
    -> ProviderReadinessResult + PermissionCoverageRows
    -> Filament pages/resources, dashboard/overview/inbox summaries, tests

Do not create a generalized multi-provider framework, onboarding workflow, or persisted readiness ledger.

Likely Affected Repository Surfaces

Implementation must re-verify exact current code before editing. Likely surfaces are:

  • apps/platform/app/Models/ProviderConnection.php
  • apps/platform/app/Models/ManagedEnvironmentPermission.php
  • apps/platform/app/Services/Intune/ManagedEnvironmentPermissionService.php
  • apps/platform/app/Services/Intune/ManagedEnvironmentRequiredPermissionsViewModelBuilder.php
  • apps/platform/app/Support/Providers/TargetScope/ProviderConnectionSurfaceSummary.php
  • apps/platform/app/Support/ResolutionGuidance/Adapters/ProviderReadinessResolutionAdapter.php
  • apps/platform/app/Support/Providers/Capabilities/ProviderCapabilityEvaluator.php
  • apps/platform/app/Support/Providers/Capabilities/ProviderCapabilityResult.php
  • apps/platform/app/Support/Providers/Capabilities/ProviderCapabilityStatus.php
  • apps/platform/app/Support/Providers/ProviderVerificationStatus.php
  • apps/platform/app/Support/Badges/Domains/ProviderVerificationStatusBadge.php
  • apps/platform/app/Filament/Resources/ProviderConnectionResource.php
  • apps/platform/app/Filament/Resources/ProviderConnectionResource/Pages/ListProviderConnections.php
  • apps/platform/app/Filament/Resources/ProviderConnectionResource/Pages/ViewProviderConnection.php
  • apps/platform/app/Filament/Resources/ProviderConnectionResource/Pages/EditProviderConnection.php
  • apps/platform/app/Filament/Pages/EnvironmentRequiredPermissions.php
  • apps/platform/resources/views/filament/pages/environment-required-permissions.blade.php
  • apps/platform/resources/views/filament/partials/provider-readiness-guidance-card.blade.php
  • apps/platform/app/Support/EnvironmentDashboard/EnvironmentDashboardSummaryBuilder.php
  • apps/platform/app/Support/Workspaces/WorkspaceOverviewBuilder.php
  • apps/platform/app/Support/GovernanceInbox/GovernanceInboxSectionBuilder.php
  • apps/platform/app/Services/EnvironmentReviews/EnvironmentReviewReadinessGate.php
  • apps/platform/app/Support/ReviewPublicationResolution/ReviewPublicationReadinessEvaluator.php
  • apps/platform/app/Support/ReviewPacks/ReviewPackOutputReadiness.php
  • apps/platform/app/Support/RestoreReadinessResolution/RestoreReadinessResolver.php
  • apps/platform/app/Support/Baselines/Readiness/BaselineEvidenceReadinessDeriver.php
  • provider/required-permission/verification tests under apps/platform/tests/Unit, apps/platform/tests/Feature, and apps/platform/tests/Browser

Implementation may remove items from the touched list if repo truth proves they are not product-facing or already consume the canonical resolver safely.

UI / Surface Guardrail Plan

  • Guardrail scope: existing provider readiness, permission counts/rows, and readiness-blocker displays.
  • Affected routes/pages/actions/states/navigation/panel/provider surfaces: Provider Connections list/view/edit, Required Permissions, Environment Dashboard provider guidance, Workspace Overview provider/readiness signals, Governance Inbox provider-related items if present, direct downstream readiness summaries.
  • No-impact class, if applicable: N/A.
  • Native vs custom classification summary: mixed existing native Filament resources/pages plus existing Blade summary components.
  • Shared-family relevance: status messaging, action links, dashboard signals, readiness cards/summaries, diagnostic disclosure, provider operation start links.
  • State layers in scope: page, table, detail, action visibility/disabled state, dashboard summary, overview/inbox aggregation.
  • Audience modes in scope: operator-MSP, workspace owner/manager, readonly authorized users, support/platform where authorized.
  • Decision/diagnostic/raw hierarchy plan: readiness decision first, diagnostics second, raw/support detail gated and collapsed.
  • Raw/support gating plan: no raw provider payload or raw permission JSON by default; technical detail requires existing technical/manage capability.
  • One-primary-action / duplicate-truth control: one resolver state and one primary action per focused provider readiness area.
  • Handling modes by drift class or surface: stale-as-healthy is hard-stop; Present 0 is hard-stop; wrong-scope grant coverage is hard-stop; secondary-copy polish is review-mandatory.
  • Repository-signal treatment: completed Specs 281, 283, 294, 339, 353, and 381 are context only and must not be rewritten.
  • Special surface test profiles: provider/integration strategic surface, diagnostic matrix page, dashboard/overview readiness signal, queue summary.
  • Required tests or manual smoke: Unit resolver tests, Feature/Filament tests, one focused Browser smoke.
  • Exception path and spread control: any remaining local provider logic must be documented in the implementation report with reason and follow-up.
  • Active feature PR close-out entry: Provider Freshness / Permission Semantics Guardrail.
  • UI/Productization coverage decision: update existing page reports only when visible behavior materially changes. No new route/archetype expected.
  • Coverage artifacts to update: likely ui-009-provider-connections.md and ui-077-required-permissions.md if labels/counts/actions visibly change; dashboard/workspace/inbox page reports only if default-visible behavior materially changes.
  • No-impact rationale: N/A.
  • Navigation / Filament provider-panel handling: no panel provider or path change. Provider registration remains apps/platform/bootstrap/providers.php.
  • Screenshot or page-report need: focused screenshots for changed Provider Connections/Required Permissions states if browser smoke is added.

Shared Pattern & System Fit

  • Cross-cutting feature marker: yes.
  • Systems touched: provider connection summaries, permission diagnostics, provider capability evaluation, readiness guidance, dashboard/overview/inbox readiness, provider operation start links, badges.
  • Shared abstractions reused: BadgeCatalog/BadgeRenderer, RequiredPermissionsLinks, OperationRunLinks, ProviderOperationStartResultPresenter, OperationUxPresenter, UiEnforcement, WorkspaceUiEnforcement, provider capability registry/evaluator as input.
  • New abstraction introduced? why?: one provider readiness resolver/result contract because current local truth can already diverge across real surfaces and create false readiness.
  • Why the existing abstraction was sufficient or insufficient: ProviderReadinessResolutionAdapter produces guidance but not canonical count rows and not all health/readiness consumers. ProviderCapabilityEvaluator evaluates capabilities but does not own all product readiness states/actions/counts. ManagedEnvironmentRequiredPermissionsViewModelBuilder owns the page matrix but not provider connection health.
  • Bounded deviation / spread control: resolver owns provider readiness only. It must not become a generic proof framework, onboarding workflow, or multi-provider engine.

OperationRun UX Impact

  • Touches OperationRun start/completion/link UX?: yes, existing provider verification start and proof links only.
  • Central contract reused: existing OperationRun start UX path through StartVerification, ProviderOperationStartResultPresenter, OperationUxPresenter, OperationRunLinks, and OperationRunService.
  • Delegated UX behaviors: queued toast, dedupe, blocked/failure messaging, run link, terminal notification, and operation URL resolution remain on existing shared paths.
  • Surface-owned behavior kept local: render resolver-recommended action if actor can perform it; otherwise render disabled/hidden state through existing UI enforcement.
  • Queued DB-notification policy: unchanged.
  • Terminal notification path: unchanged central lifecycle mechanism.
  • Exception path: none.

Provider Boundary & Portability Fit

  • Shared provider/platform boundary touched?: yes.
  • Provider-owned seams: Microsoft Graph permission names, admin consent URL, provider-specific verification details, provider-owned error reason detail.
  • Platform-core seams: readiness state, freshness state, required/effective permission coverage, row/count semantics, recommended action, scope matching.
  • Neutral platform terms / contracts preserved: provider connection, verification, freshness, required permission, granted permission, effective permission, permission coverage, readiness, scope, next action.
  • Retained provider-specific semantics and why: Microsoft permission names remain secondary technical detail because current Microsoft provider remediation still needs them.
  • Bounded extraction or follow-up path: Provider Onboarding & Permissions Resolution Adapter can build on this resolver later.

Constitution Check

  • Inventory-first / snapshots-second: no inventory truth change.
  • Read/write separation: resolver/render paths are read-only. Verification action continues existing OperationRun start path.
  • Graph contract path: no Graph calls during render. Any verification work remains through existing services/jobs and Graph client boundaries.
  • Deterministic capabilities: resolver output is testable and deterministic.
  • RBAC-UX: workspace/environment membership is 404 boundary; missing capability is 403 or disabled/hidden per existing UI pattern.
  • Workspace isolation: resolver scopes by workspace and managed environment before returning readiness/action data.
  • Tenant/managed-environment isolation: wrong-scope grants and provider connections cannot satisfy current permission coverage.
  • OperationRun start UX: verify-provider action reuses central OperationRun UX. No local toast/link composition.
  • Test governance: Unit, Feature/Filament, and Browser proof are explicitly scoped.
  • Proportionality: new resolver is justified by current false provider readiness and multiple existing consumers.
  • No premature abstraction: resolver replaces duplicated local truth and is not a multi-provider framework.
  • Persisted truth: no new table expected by default; existing schema is used first.
  • Behavioral state: new derived states change next action and readiness gating.
  • UI semantics: no new badge framework; use existing badge/status helpers.
  • Shared pattern first: existing Spec 353 guidance and provider capability systems are reused or delegated to the resolver.
  • Provider boundary: platform-core readiness vocabulary stays provider-neutral.
  • Filament-native UI: use existing Filament surfaces/components; no custom styling expansion.
  • UI/Productization coverage: changed visible surfaces update existing UI audit artifacts or document explicit no-route/no-archetype impact.

Domain And Data Implications

No migration is expected by default. Existing fields appear sufficient for v1:

  • provider_connections.workspace_id
  • provider_connections.managed_environment_id
  • provider_connections.provider
  • provider_connections.is_enabled
  • provider_connections.consent_status
  • provider_connections.verification_status
  • provider_connections.last_health_check_at
  • provider_connections.last_error_reason_code
  • provider_connections.last_error_message
  • managed_environment_permissions.workspace_id
  • managed_environment_permissions.managed_environment_id
  • managed_environment_permissions.permission_key
  • managed_environment_permissions.status
  • managed_environment_permissions.details
  • managed_environment_permissions.last_checked_at
  • verification OperationRun.context.provider_connection_id
  • verification report payloads where current code already uses them

If implementation cannot distinguish current/fresh verification batch scope from stale stored grants using existing fields, stop and update spec/plan before adding schema. Any schema change must be minimal and clean-cut under the pre-production doctrine.

Resolver Design Notes

Expected result fields:

provider_connection_id: int|null
scope_type: string
scope_id: int|null
readiness_state: string
connection_state: string
verification_state: string
verification_checked_at: string|null
verification_expires_at: string|null
is_verification_fresh: bool
required_count: int
granted_required_count: int
missing_required_count: int
blocked_required_count: int
expired_required_count: int
unknown_required_count: int
not_applicable_count: int
permission_rows: list<array>
primary_reason: string
blocking_reasons: list<string>
recommended_action: array|null
can_view_technical_detail: bool
can_manage_provider: bool
child_results: list<ProviderReadinessResult>|null

Entry-point shapes:

  • Provider-connection, required-permission, and freshness entry points return a single provider-scoped result.
  • Environment entry points return an aggregate result with provider-scoped child results for actor-authorized provider connections in the environment.
  • Workspace entry points return an aggregate result with environment/provider-scoped child results for actor-authorized environments and provider connections.
  • Aggregate results sum canonical count fields from children, use the same readiness-state precedence as provider-scoped results, and choose the recommended action from the highest-severity child blocker.

Permission rows:

permission_key: string
product_label: string
provider_permission_name: string|null
state: Granted|Missing|Blocked|Expired|Unknown|Not applicable
required_for: list<string>
is_required: bool
is_effective: bool
matched_grant_id: int|null
last_verified_at: string|null
reason: string
recommended_action: array|null
is_technical_only: bool

State ordering:

  1. Not configured if provider connection is absent/disconnected.
  2. Failed if latest verification failed.
  3. Expired if latest verification or permission evidence is stale.
  4. Blocked if provider or admin consent blocks required permissions.
  5. Needs attention if required permissions are missing.
  6. Unknown if sufficient verification data is unavailable.
  7. Ready only when connection is valid, verification is fresh, and all required permissions are effective.

Grant/row matching order:

  1. Correct workspace.
  2. Correct managed environment.
  3. Correct provider connection and provider account/context as represented by the current ProviderConnection identity, provider key, managed-environment binding, and same-provider verification context.
  4. Correct provider namespace/provider key.
  5. Fresh verification basis.
  6. Non-revoked/non-error grant.
  7. Most recent verified timestamp.
  8. Deterministic ID tie-breaker.

Route And Authorization Plan

  • Resolver must not be the only security boundary. Direct routes/actions remain policy/gate protected.
  • Product-safe readiness summaries are returned only after workspace/environment entitlement.
  • Provider-changing actions are returned as enabled only for actors with existing provider manage/run capability.
  • Readonly actors may see product-safe summaries but get no provider-changing action.
  • Technical detail links/actions are omitted unless actor has technical/manage capability.
  • Wrong-scope provider connections and permission rows are ignored or denied-as-not-found by surrounding policies.
  • If existing data cannot distinguish provider account/context for a permission that requires that distinction, implementation must stop and update spec/plan before adding schema. It must not assume the account matches.

Filament And Livewire Plan

  • Filament v5 / Livewire v4.0+ compliance is required; the app currently uses Livewire 4.1.4.
  • Panel provider registration remains apps/platform/bootstrap/providers.php; no provider registration changes are expected.
  • ProviderConnectionResource currently disables global search and must remain safe. Do not add global search. If implementation touches any globally searchable resource, verify it has View/Edit page and scoped query.
  • No destructive action is introduced. Existing credential/provider-changing actions must keep ->action(...), confirmation where high-impact/destructive, server-side authorization, audit logging, and tests.
  • No new Filament assets are expected. If implementation registers assets unexpectedly, deployment must include cd apps/platform && php artisan filament:assets.

Test Strategy

Unit Tests

Add or update focused resolver tests for:

  • fresh verification + all required permissions -> Ready
  • stale verification + old grants -> Expired, not ready
  • verification failed -> Failed, not ready
  • no verification -> Unknown, not ready
  • connected provider without verification -> Unknown or Needs attention, not ready
  • missing required permission -> Needs attention, not ready
  • blocked/admin consent denied -> Blocked
  • wrong workspace/environment/provider connection grant ignored
  • stale grant -> Expired
  • count invariant
  • deterministic ordering
  • actor without manage capability has no provider-changing action
  • actor without technical permission has no raw technical detail

Feature / Filament Tests

Add or update tests for:

  • Required Permissions page canonical counts and labels
  • no Present 0 when grants are effective
  • Provider Connection page does not show Healthy for stale verification
  • Environment Dashboard consumes resolver output
  • Workspace Overview consumes resolver output where repo-real
  • Governance Inbox provider-related items consume resolver output where repo-real
  • readonly users cannot run verify/reconnect/manage actions
  • manage-capable users can access existing verify/reconnect where supported
  • wrong-scope grants are not counted
  • raw provider payload absent by default

Browser Smoke

Add or update one focused browser smoke:

  • stale provider is not Healthy/Ready
  • fresh fully-permissioned provider is Ready
  • counts match rows
  • Present 0 is absent
  • missing permissions show actionable copy
  • readonly user cannot trigger provider management actions
  • manage-capable user can access verify/reconnect where supported
  • no raw provider payload/default technical dump
  • no 500/Livewire/Filament/console errors

Validation Commands

Preferred targeted commands:

cd apps/platform && ./vendor/bin/sail artisan test --filter=Spec394
cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/ProviderConnections tests/Feature/RequiredPermissions tests/Feature/Verification
cd apps/platform && ./vendor/bin/sail php vendor/bin/pest tests/Browser/Spec394ProviderFreshnessPermissionSmokeTest.php
cd apps/platform && ./vendor/bin/sail bin pint --dirty --format agent
git diff --check

Use exact existing file names discovered during implementation. If browser suite failures are unrelated legacy/global smokes, document full command, pass/fail count, non-Spec394 failures, and why non-blocking.

Rollout And Deployment Considerations

  • Env vars: none expected.
  • Migrations: none expected by default. If added, use safe clean-cut pre-production migration and update spec/plan first.
  • Queues: no new queue family. Existing provider verification jobs/OperationRun behavior unchanged.
  • Scheduler/cron: none expected.
  • Storage/volumes: none expected.
  • Graph scopes: none expected.
  • Filament assets: none expected.
  • Staging/production: staging must validate changed provider/readiness UI before production. Because there is no production deployment requirement for legacy data, remove incorrect semantics rather than preserving them.

Implementation Phases

Phase 1 - Inventory And Current Truth

Map every local provider health/readiness/freshness/permission count path and record its source, visible label, count source, freshness source, scope source, authorization rule, stale-as-healthy risk, and row/count mismatch risk.

Phase 2 - Resolver Contract And Unit Proof

Create or consolidate the canonical resolver/result shape and prove canonical scenarios with unit tests before broad surface changes.

Phase 3 - Required Permissions Count And Row Semantics

Replace Present and page-local count behavior with canonical count and row states. Keep raw provider names secondary.

Phase 4 - Provider Connections And Guidance Consumers

Replace local readiness/health logic in ProviderConnectionSurfaceSummary, provider resource pages, and Spec 353 guidance adapter with resolver output or delegation.

Phase 5 - Dashboard, Workspace, Inbox, And Downstream Consumers

Adopt resolver output in Environment Dashboard, Workspace Overview, Governance Inbox, and direct restore/evidence/review readiness consumers where repo-real. Document any remaining local path.

Phase 6 - RBAC, Technical Detail, And Raw Payload Gating

Enforce action capability boundaries and ensure raw provider/permission detail is hidden or capability-gated.

Phase 7 - Fixture, Label, And Legacy Cleanup

Update fixtures, tests, localization keys, old action/copy labels, and guard tests so misleading legacy behavior is removed, not aliased.

Phase 8 - Browser Smoke And Human Sanity Check

Run focused browser smoke and prepare human product sanity checklist. Human reviewer must inspect the main affected screens before marking implementation complete.

Phase 9 - Close-Out

Run validation commands, pint --dirty, git diff --check, and produce the required implementation report.

Risks And Controls

Risk Likelihood Impact Control
Existing fixtures assume Present or old health states High Medium Update fixtures to canonical model; no compatibility adapter
Resolver becomes too broad Medium Medium Keep it provider readiness only; no onboarding/dashboard expansion
Schema cannot represent verification batch freshness Medium High Stop, update spec/plan, add minimal clean schema only if required
Browser scope expands too far Medium Medium One focused Spec394 smoke plus named existing affected tests only
Readonly technical detail behavior changes unexpectedly Medium Medium Product-safe summary remains visible; raw details gated by manage/technical capability
Downstream readiness starts surfacing more blockers High Medium Correct by design if provider is stale/missing; document direct consumers migrated

Filament v5 Output Contract For Implementation Close-Out

Later implementation response must explicitly state:

  1. Livewire v4.0+ compliance.
  2. Provider registration location: apps/platform/bootstrap/providers.php; no panel-provider change expected.
  3. Global search posture: ProviderConnectionResource remains non-globally-searchable unless spec/plan are updated first.
  4. Destructive/high-impact actions: no new destructive action expected; provider-changing actions retain existing action handlers, confirmation where required, authorization, and audit.
  5. Asset strategy: no new assets expected; filament:assets only if implementation registers assets.
  6. Testing plan and actual tests run.
  7. Deployment impact: env, migrations, queues, scheduler, storage, and Graph scopes.

Stop Conditions

  • Stop if implementation needs a new table or verification batch model; update spec/plan first.
  • Stop if implementation needs a distinct provider-account identifier beyond the current ProviderConnection identity and verification context; update spec/plan first.
  • Stop if a page-local provider readiness path appears necessary for product UI; fold it into the resolver or document a bounded exception.
  • Stop if old tests can only pass by preserving stale-as-healthy, Present, wrong-scope grant, or stale-grant behavior.
  • Stop if scope expands into provider onboarding wizard, new dashboard/card, new evidence/proof links, customer-facing provider internals, OAuth redesign, or new provider integration.
  • Do not rewrite, normalize, uncheck, or remove implementation history from completed Specs 281, 283, 294, 339, 353, or 381.