17 KiB
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
UiActionContextcontract - harden
UiEnforcementso scoped no-record actions cannot silently use implicitFilament::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_snapshotandcreate_first_snapshot - Review Pack
generate_packandgenerate_first - Environment Review
create_review - Environment Diagnostics
bootstrapOwnerandmergeDuplicateMemberships - Restore Run create entrypoint and create wizard
- Environment Dashboard support request / support diagnostics actions
- Inventory list
- 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.phpapps/platform/app/Support/Rbac/WorkspaceUiEnforcement.phpas 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
UiActionContextrequest-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:
UiEnforcementis 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, andOpsUxBrowserEvents - 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
UiEnforcementpath 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,UiEnforcementcontext 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
- Unit:
- 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.phpcd 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.phpcd 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.phpapps/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.phpapps/platform/app/Filament/Resources/PolicyResource.phpapps/platform/app/Filament/Resources/PolicyResource/Pages/ListPolicies.phpapps/platform/app/Filament/Resources/EntraGroupResource/Pages/ListEntraGroups.phpapps/platform/app/Filament/Resources/EvidenceSnapshotResource.phpapps/platform/app/Filament/Resources/EvidenceSnapshotResource/Pages/ListEvidenceSnapshots.phpapps/platform/app/Filament/Resources/ReviewPackResource.phpapps/platform/app/Filament/Resources/EnvironmentReviewResource.phpapps/platform/app/Filament/Pages/EnvironmentDiagnostics.phpapps/platform/app/Filament/Resources/RestoreRunResource.phpapps/platform/app/Filament/Resources/RestoreRunResource/Pages/CreateRestoreRun.phpapps/platform/app/Filament/Pages/EnvironmentDashboard.php
Tests / Guards
apps/platform/tests/Unit/Support/Rbac/Actions/*apps/platform/tests/Unit/Support/Rbac/UiEnforcementScopedActionContextTest.phpapps/platform/tests/Feature/Architecture/ScopedUiActionContextContractTest.phpapps/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, andapps/platform/tests/Feature/RunStartAuthorizationTest.php - existing support action tests under
apps/platform/tests/Feature/SupportDiagnosticsandapps/platform/tests/Feature/SupportRequests
Technical Approach
- Re-read this spec, plan, tasks, checklist, the constitution, and relevant guidelines before runtime edits.
- Re-verify the current action callsites and exact action names.
- Add failing Unit coverage for context value objects and
UiEnforcementscoped behavior. - Add failing Feature/Filament coverage for the known latent-risk actions.
- Add the narrow
UiActionContextcontract and helper/trait. - Harden
UiEnforcementto require explicit context for scoped no-record actions while preserving record-backed paths. - Retrofit representative actions to pass explicit context and reauthorize on submit.
- Add reusable first-click modal/no-run helper only where it reduces duplication.
- Add the static guard with actionable failures.
- 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)
specs/363-explicit-uiactioncontext-contract/
├── spec.md
├── plan.md
├── tasks.md
└── checklists/
└── requirements.md
Application (later implementation only)
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.