Automated PR created by Codex via Gitea API. Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de> Reviewed-on: #465
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
- Inventory all provider readiness, provider health, verification freshness, required permission count, grant, capability, and downstream readiness selectors.
- Define one canonical provider readiness resolver/result contract over existing provider connection, permission, and verification evidence.
- Prove resolver behavior first with unit tests for fresh, stale, failed, unknown, missing, blocked, wrong-scope, stale-grant, and count-invariant cases.
- Replace local visible state and count derivation in affected product surfaces.
- Remove or delegate old local provider health/readiness helpers.
- Replace ambiguous UI labels such as
Presentwith canonical labels. - Enforce RBAC and technical-detail gating through existing capability/policy patterns.
- Update affected fixtures/tests to assert canonical behavior.
- 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.phpapps/platform/app/Models/ManagedEnvironmentPermission.phpapps/platform/app/Services/Intune/ManagedEnvironmentPermissionService.phpapps/platform/app/Services/Intune/ManagedEnvironmentRequiredPermissionsViewModelBuilder.phpapps/platform/app/Support/Providers/TargetScope/ProviderConnectionSurfaceSummary.phpapps/platform/app/Support/ResolutionGuidance/Adapters/ProviderReadinessResolutionAdapter.phpapps/platform/app/Support/Providers/Capabilities/ProviderCapabilityEvaluator.phpapps/platform/app/Support/Providers/Capabilities/ProviderCapabilityResult.phpapps/platform/app/Support/Providers/Capabilities/ProviderCapabilityStatus.phpapps/platform/app/Support/Providers/ProviderVerificationStatus.phpapps/platform/app/Support/Badges/Domains/ProviderVerificationStatusBadge.phpapps/platform/app/Filament/Resources/ProviderConnectionResource.phpapps/platform/app/Filament/Resources/ProviderConnectionResource/Pages/ListProviderConnections.phpapps/platform/app/Filament/Resources/ProviderConnectionResource/Pages/ViewProviderConnection.phpapps/platform/app/Filament/Resources/ProviderConnectionResource/Pages/EditProviderConnection.phpapps/platform/app/Filament/Pages/EnvironmentRequiredPermissions.phpapps/platform/resources/views/filament/pages/environment-required-permissions.blade.phpapps/platform/resources/views/filament/partials/provider-readiness-guidance-card.blade.phpapps/platform/app/Support/EnvironmentDashboard/EnvironmentDashboardSummaryBuilder.phpapps/platform/app/Support/Workspaces/WorkspaceOverviewBuilder.phpapps/platform/app/Support/GovernanceInbox/GovernanceInboxSectionBuilder.phpapps/platform/app/Services/EnvironmentReviews/EnvironmentReviewReadinessGate.phpapps/platform/app/Support/ReviewPublicationResolution/ReviewPublicationReadinessEvaluator.phpapps/platform/app/Support/ReviewPacks/ReviewPackOutputReadiness.phpapps/platform/app/Support/RestoreReadinessResolution/RestoreReadinessResolver.phpapps/platform/app/Support/Baselines/Readiness/BaselineEvidenceReadinessDeriver.php- provider/required-permission/verification tests under
apps/platform/tests/Unit,apps/platform/tests/Feature, andapps/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 0is 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.mdandui-077-required-permissions.mdif 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:
ProviderReadinessResolutionAdapterproduces guidance but not canonical count rows and not all health/readiness consumers.ProviderCapabilityEvaluatorevaluates capabilities but does not own all product readiness states/actions/counts.ManagedEnvironmentRequiredPermissionsViewModelBuilderowns 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, andOperationRunService. - 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_idprovider_connections.managed_environment_idprovider_connections.providerprovider_connections.is_enabledprovider_connections.consent_statusprovider_connections.verification_statusprovider_connections.last_health_check_atprovider_connections.last_error_reason_codeprovider_connections.last_error_messagemanaged_environment_permissions.workspace_idmanaged_environment_permissions.managed_environment_idmanaged_environment_permissions.permission_keymanaged_environment_permissions.statusmanaged_environment_permissions.detailsmanaged_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:
- Not configured if provider connection is absent/disconnected.
- Failed if latest verification failed.
- Expired if latest verification or permission evidence is stale.
- Blocked if provider or admin consent blocks required permissions.
- Needs attention if required permissions are missing.
- Unknown if sufficient verification data is unavailable.
- Ready only when connection is valid, verification is fresh, and all required permissions are effective.
Grant/row matching order:
- Correct workspace.
- Correct managed environment.
- Correct provider connection and provider account/context as represented by the current
ProviderConnectionidentity, provider key, managed-environment binding, and same-provider verification context. - Correct provider namespace/provider key.
- Fresh verification basis.
- Non-revoked/non-error grant.
- Most recent verified timestamp.
- 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. ProviderConnectionResourcecurrently 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 ->
UnknownorNeeds 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 0when grants are effective - Provider Connection page does not show
Healthyfor 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 0is 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:
- Livewire v4.0+ compliance.
- Provider registration location:
apps/platform/bootstrap/providers.php; no panel-provider change expected. - Global search posture:
ProviderConnectionResourceremains non-globally-searchable unless spec/plan are updated first. - Destructive/high-impact actions: no new destructive action expected; provider-changing actions retain existing action handlers, confirmation where required, authorization, and audit.
- Asset strategy: no new assets expected;
filament:assetsonly if implementation registers assets. - Testing plan and actual tests run.
- 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
ProviderConnectionidentity 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.