# Implementation Plan: Spec 363 - Explicit UiActionContext Contract for Scoped No-Record Actions **Branch**: `feat/363-explicit-uiactioncontext-contract` | **Preparation Branch**: `363-explicit-uiactioncontext-contract` | **Date**: 2026-06-07 | **Spec**: `/Users/ahmeddarrazi/Documents/projects/wt-plattform/specs/363-explicit-uiactioncontext-contract/spec.md` **Input**: Feature specification from `/Users/ahmeddarrazi/Documents/projects/wt-plattform/specs/363-explicit-uiactioncontext-contract/spec.md` **Note**: This plan is repo-aware and preparation-only. No application implementation is performed in this step. ## Summary Add an explicit action-context contract for workspace- and environment-scoped no-record Filament actions. The implementation should: - introduce a narrow request-time `UiActionContext` contract - harden `UiEnforcement` so scoped no-record actions cannot silently use implicit `Filament::getTenant()` as product scope truth - preserve record-backed action behavior where scope comes from the record - retrofit representative latent-risk no-record actions - add first-click modal/no-run test helpers and action lifecycle tests - add a static guard that fails future risky scoped no-record actions without explicit context No migrations, panel/provider changes, global-search changes, asset strategy changes, or new product workflows are planned. ## 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 via Sail, but no schema changes are planned **Testing**: Pest Unit + Feature/Filament-Livewire + Architecture/guard; Browser optional only if visible UI hierarchy/copy changes materially **Validation Lanes**: fast-feedback, confidence, browser optional **Target Platform**: Laravel monolith in Sail / Dokploy container workflow **Project Type**: single web application (`apps/platform`) **Performance Goals**: no Graph calls during render/action-state/modal mount; static scan remains bounded and deterministic **Constraints**: no new persistence, no operation type, no capability string, no panel/provider, no asset registration, no global-search change, no compatibility shim, no broad record-backed action rewrite **Scale/Scope**: no-record scoped actions first, plus representative action retrofits and static recurrence guard ## UI / Surface Guardrail Plan - **Guardrail scope**: changed existing actions on existing pages - **Affected routes/pages/actions/states/navigation/panel/provider surfaces**: - Inventory list `run_inventory_sync` - Policy list/header sync and related Policy Sync start surface - Entra Groups `sync_groups` - Evidence Snapshot `create_snapshot` and `create_first_snapshot` - Review Pack `generate_pack` and `generate_first` - Environment Review `create_review` - Environment Diagnostics `bootstrapOwner` and `mergeDuplicateMemberships` - Restore Run create entrypoint and create wizard - Environment Dashboard support request / support diagnostics actions - **No-impact class, if applicable**: N/A, because existing high-impact actions change context behavior - **Native vs custom classification summary**: native Filament pages/resources/actions with shared RBAC enforcement - **Shared-family relevance**: header/page/empty-state action family, RBAC UI enforcement family, OperationRun start family - **State layers in scope**: page render, action visibility, action disabled state, first-click modal mount, submit/execute handler - **Audience modes in scope**: operator-MSP and support-platform - **Decision/diagnostic/raw hierarchy plan**: show safe action state and missing-context copy only; keep low-level Livewire/Filament request details out of UI - **Raw/support gating plan**: no raw request, referer, token, Graph, credential, or payload data in visible UI - **One-primary-action / duplicate-truth control**: do not duplicate run outcome truth in action modals; after submit reuse existing OperationRun links/toasts - **Handling modes by drift class or surface**: review-mandatory for any scoped no-record action that opens a modal, dispatches work, creates an `OperationRun`, or mutates state - **Repository-signal treatment**: record-backed actions are context only unless the static guard proves they behave like no-record scoped actions - **Special surface test profiles**: standard-native-filament plus action-lifecycle-contract - **Required tests or manual smoke**: Unit + Feature/Filament-Livewire + static guard; Browser only if implementation changes visible copy/hierarchy materially - **Exception path and spread control**: bounded static-guard exceptions must name the file/action and why record/page scope is safe - **Active feature PR close-out entry**: Guardrail / Smoke Coverage - **UI/Productization coverage decision**: no new UI coverage registry update is expected because no new route/page family is introduced - **Coverage artifacts to update**: none by default - **No-impact rationale**: N/A - **Navigation / Filament provider-panel handling**: no panel provider, panel path, navigation, or cluster change - **Screenshot or page-report need**: no by default ## Shared Pattern & System Fit - **Cross-cutting feature marker**: yes - **Systems touched**: - `apps/platform/app/Support/Rbac/UiEnforcement.php` - `apps/platform/app/Support/Rbac/WorkspaceUiEnforcement.php` as reference/comparison only unless small shared context support is justified - new `apps/platform/app/Support/Rbac/Actions/` contract classes if implementation confirms this namespace is still best - `apps/platform/app/Filament/Concerns/ResolvesPanelTenantContext.php` - selected existing Filament resources/pages/actions - selected existing action tests and new guard/test helper - **Shared abstractions reused**: existing RBAC helpers, capability resolvers, policies/gates, page context resolvers, OperationRun start helpers, Filament action testing helpers - **New abstraction introduced? why?**: yes, a narrow `UiActionContext` request-time contract is allowed because the problem is RBAC/isolation/queue legitimacy-critical and already repo-verified across multiple surfaces - **Why the existing abstraction was sufficient or insufficient**: `UiEnforcement` is the right shared path, but it currently allows ambiguous nullable no-record context and implicit Filament tenant fallback - **Bounded deviation / spread control**: keep new classes under an RBAC/action namespace; do not create a generic UI framework, presenter, workflow engine, or action catalog rewrite ## OperationRun UX Impact - **Touches OperationRun start/completion/link UX?**: yes, start/mount/submit lifecycle only - **Central contract reused**: existing `OperationRunService`, run dispatch handlers, `OperationRunLinks`, `OperationUxPresenter`, and `OpsUxBrowserEvents` - **Delegated UX behaviors**: queued toasts, run links, run-enqueued browser events, and terminal lifecycle notifications remain unchanged - **Surface-owned behavior kept local**: action form values and confirmation/missing-context copy - **Queued DB-notification policy**: unchanged - **Terminal notification path**: unchanged - **Exception path**: if broader execution reauthorization is weaker than UI gating, stop and propose `OperationRun Start Authorization Contract Hardening` ## Provider Boundary & Portability Fit - **Shared provider/platform boundary touched?**: no new provider seam - **Provider-owned seams**: existing Graph/provider jobs and services remain provider-owned and are not modified beyond receiving explicit, reauthorized scope - **Platform-core seams**: workspace/environment action context and RBAC UI enforcement - **Neutral platform terms / contracts preserved**: workspace, environment, record, system, product scope, action context - **Retained provider-specific semantics and why**: only current Microsoft/Graph semantics inside existing actions/jobs remain - **Bounded extraction or follow-up path**: none unless broader OperationRun execution authorization weakness is discovered ## Constitution Check *GATE: Must pass before implementation starts. Re-check if scope changes.* - Inventory-first: PASS. No inventory truth changes; Inventory Sync action context becomes explicit only. - Read/write separation: PASS. Modal open remains non-mutating; submit/execute must reauthorize before writes/jobs. - Graph contract path: PASS. No new Graph calls; render/mount must remain no-Graph. - Deterministic capabilities: PASS. Existing capabilities remain authoritative. - Workspace and tenant isolation: PASS. Explicit context improves workspace/environment scope safety. - RBAC-UX: PASS. UI state remains affordance only; server-side policies/gates remain execution truth. - TEST-GOV-001: PASS. Unit + Feature/action + static guard are the narrowest honest proof. - PROP-001 / ABSTR-001: PASS only because the new abstraction is security/isolation/action-lifecycle critical and already justified by repeated repo evidence. - PERSIST-001 / STATE-001: PASS. No new persisted truth or persisted status family is introduced. - XCUT-001 / LAYER-001: PASS. Extend the existing shared `UiEnforcement` path instead of adding local one-off action rules. - UI-COV-001: PASS. Existing reachable surfaces are classified; no new page family is expected. - LEAN-001: PASS. No compatibility shim for old no-record fallback behavior. ## Test Governance Check - **Test purpose / classification by changed surface**: - Unit: `UiActionContext`, scope/source enums, resolver helpers, missing-context behavior, `UiEnforcement` context resolution - Feature/Filament-Livewire: selected page/resource actions, first-click modal mount, no-run/no-job on modal open, submit reauthorization, wrong-context/readonly behavior - Architecture/Feature guard: static source scan for risky scoped no-record actions without explicit context - Browser: optional smoke only if visible hierarchy/copy changes materially - **Affected validation lanes**: fast-feedback, confidence, browser optional - **Why this lane mix is the narrowest sufficient proof**: the behavior is request-time action lifecycle and static source guard behavior, not DB schema or provider API behavior - **Narrowest proving command(s)**: - `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Unit/Support/Rbac/Actions tests/Unit/Support/Rbac/UiEnforcementScopedActionContextTest.php` - `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Architecture/ScopedUiActionContextContractTest.php tests/Feature/Filament/InventoryItemResourceTest.php tests/Feature/PolicySyncStartSurfaceTest.php tests/Feature/RunStartAuthorizationTest.php` - `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Filament/EntraGroupAdminScopeTest.php tests/Feature/Evidence/EvidenceSnapshotResourceTest.php tests/Feature/ReviewPack/ReviewPackResourceTest.php tests/Feature/Filament/RestoreRunUiEnforcementTest.php` - **Fixture / helper / factory / seed / context cost risks**: keep workspace/environment/member/capability setup explicit; do not introduce full-context defaults in shared helpers - **Expensive defaults or shared helper growth introduced?**: no - **Heavy-family additions, promotions, or visibility changes**: none by default - **Surface-class relief / special coverage rule**: standard-native-filament plus action-lifecycle-contract - **Closing validation and reviewer handoff**: reviewers should verify no hidden `Filament::getTenant()` product-scope fallback for scoped no-record actions, no modal-open side effects, and no compatibility path - **Budget / baseline / trend follow-up**: none expected - **Review-stop questions**: did implementation broaden into record-backed action migration, OperationRun redesign, UI redesign, or product features? - **Escalation path**: document-in-feature for contained static-guard false positives; follow-up-spec for broader execution authorization weakness; reject-or-split for scope creep - **Active feature PR close-out entry**: Guardrail / Smoke Coverage - **Why no dedicated follow-up spec is needed now**: the root issue is explicit enough to solve as one bounded action-context contract ## Repo-Verified Runtime Surfaces Likely Affected ### Core RBAC / Context - `apps/platform/app/Support/Rbac/UiEnforcement.php` - `apps/platform/app/Support/Rbac/WorkspaceUiEnforcement.php` (context/reference only unless sharing is justified) - `apps/platform/app/Filament/Concerns/ResolvesPanelTenantContext.php` - new `apps/platform/app/Support/Rbac/Actions/UiActionContext.php` - new `apps/platform/app/Support/Rbac/Actions/UiActionScope.php` - new `apps/platform/app/Support/Rbac/Actions/UiActionContextSource.php` - new `apps/platform/app/Support/Rbac/Actions/ResolvesUiActionContext.php` ### Representative Action Surfaces - `apps/platform/app/Filament/Resources/InventoryItemResource/Pages/ListInventoryItems.php` - `apps/platform/app/Filament/Resources/PolicyResource.php` - `apps/platform/app/Filament/Resources/PolicyResource/Pages/ListPolicies.php` - `apps/platform/app/Filament/Resources/EntraGroupResource/Pages/ListEntraGroups.php` - `apps/platform/app/Filament/Resources/EvidenceSnapshotResource.php` - `apps/platform/app/Filament/Resources/EvidenceSnapshotResource/Pages/ListEvidenceSnapshots.php` - `apps/platform/app/Filament/Resources/ReviewPackResource.php` - `apps/platform/app/Filament/Resources/EnvironmentReviewResource.php` - `apps/platform/app/Filament/Pages/EnvironmentDiagnostics.php` - `apps/platform/app/Filament/Resources/RestoreRunResource.php` - `apps/platform/app/Filament/Resources/RestoreRunResource/Pages/CreateRestoreRun.php` - `apps/platform/app/Filament/Pages/EnvironmentDashboard.php` ### Tests / Guards - `apps/platform/tests/Unit/Support/Rbac/Actions/*` - `apps/platform/tests/Unit/Support/Rbac/UiEnforcementScopedActionContextTest.php` - `apps/platform/tests/Feature/Architecture/ScopedUiActionContextContractTest.php` - `apps/platform/tests/Support/Filament/ScopedActionAssertions.php` - existing action tests under `apps/platform/tests/Feature/Filament`, `apps/platform/tests/Feature/Evidence`, `apps/platform/tests/Feature/ReviewPack`, and `apps/platform/tests/Feature/RunStartAuthorizationTest.php` - existing support action tests under `apps/platform/tests/Feature/SupportDiagnostics` and `apps/platform/tests/Feature/SupportRequests` ## Technical Approach 1. Re-read this spec, plan, tasks, checklist, the constitution, and relevant guidelines before runtime edits. 2. Re-verify the current action callsites and exact action names. 3. Add failing Unit coverage for context value objects and `UiEnforcement` scoped behavior. 4. Add failing Feature/Filament coverage for the known latent-risk actions. 5. Add the narrow `UiActionContext` contract and helper/trait. 6. Harden `UiEnforcement` to require explicit context for scoped no-record actions while preserving record-backed paths. 7. Retrofit representative actions to pass explicit context and reauthorize on submit. 8. Add reusable first-click modal/no-run helper only where it reduces duplication. 9. Add the static guard with actionable failures. 10. Run focused tests, Pint dirty, and diff checks. ## Risk Controls - Keep the first implementation scoped to no-record workspace/environment actions. - Do not remove valid record-backed context resolution. - Do not allow `Filament::getTenant()` as fallback product-scope truth for scoped no-record actions. - Keep modal open non-mutating. - Keep server-side authorization in handlers/services/policies. - Keep missing-context copy enterprise-safe and localizable later. - No new migrations, assets, global search, panel provider, navigation, capability, operation type, or route. - Stop if implementation needs persistence, broad OperationRun redesign, or many static-guard false positives across record-backed actions. ## Implementation Phases ### Phase 1: Baseline and Repo-Truth Inventory Confirm current branch/worktree, re-read dependencies, and inventory exact callsites/action names before edits. ### Phase 2: Contract and Unit Tests Add the action-context value object/enums/trait or equivalent and Unit coverage for workspace, environment, record, system, and missing context behavior. ### Phase 3: UiEnforcement Contract Add explicit scoped-action API or compatible signature changes, remove implicit scoped no-record fallback, preserve record-backed behavior, and cover missing-context classification. ### Phase 4: Representative Action Retrofits Retrofit Inventory Sync, Policy Sync, Entra Group Sync, Evidence Snapshot, Review Pack, Environment Review, Environment Diagnostics, Restore create, and Environment Dashboard support actions. ### Phase 5: Test Helper and Static Guard Add the reusable first-click modal/no-run helper and static architecture guard. ### Phase 6: Validation and Close-Out Run focused tests, optional browser smoke only if needed, Pint dirty, `git diff --check`, and record no-migration/no-asset/no-provider/global-search status. ## Project Structure ### Documentation (this feature) ```text specs/363-explicit-uiactioncontext-contract/ ├── spec.md ├── plan.md ├── tasks.md └── checklists/ └── requirements.md ``` ### Application (later implementation only) ```text apps/platform/app/Support/Rbac/Actions/ apps/platform/app/Support/Rbac/UiEnforcement.php apps/platform/app/Filament/... apps/platform/tests/... ``` No application files are changed during this preparation step.