40 KiB
Feature Specification: Spec 363 - Explicit UiActionContext Contract for Scoped No-Record Actions
Feature Branch: feat/363-explicit-uiactioncontext-contract
Preparation Branch: 363-explicit-uiactioncontext-contract
Created: 2026-06-07
Status: Implemented
Type: Filament/Livewire action-context safety / RBAC UX hardening / no new persistence
Runtime posture: Harden workspace- and environment-scoped no-record Filament actions so render, first-click modal mount, and submit execution use explicit product scope instead of implicit Filament tenant fallback.
Input: Direct user-provided Spec 363 draft in /Users/ahmeddarrazi/.codex/attachments/36f3aea8-0303-4548-a83c-9f1cdd15f527/pasted-text.txt plus repo inspection of Spec 362 root-cause audit and current action/RBAC seams.
Dependencies And Historical Context
This package follows the action-context root-cause audit produced during Spec 362:
specs/362-sync-capture-backup-operation-semantics/artifacts/action-context-root-cause-audit.mdclassifies the issue as Case C - Systemic Action Context Contract Gap.- The audit shows that Inventory Sync and Policy Sync already needed an explicit page resolver pattern to make first-click modal mounting reliable under
/livewire/update. - The same latent class remains on no-record environment/workspace actions that can open modals, create
OperationRunrecords, dispatch jobs, generate artifacts, or mutate support/diagnostics state.
Current repo truth already provides useful foundations:
App\Support\Rbac\UiEnforcementcentralizes environment-scoped UI affordance rules.App\Support\Rbac\WorkspaceUiEnforcementalready uses a stricter explicit workspace source pattern.App\Filament\Concerns\ResolvesPanelTenantContextcan resolve panel context across Livewire referer requests.OperationRunand OperationRun UX helpers already make queued work observable.- Existing Filament/Livewire tests already use
mountAction(),callAction(), queue fakes, and no-run assertions for Inventory and Policy Sync.
This spec turns those local fixes into an explicit product contract.
Spec Candidate Check (mandatory - SPEC-GATE-001)
- Problem: Workspace- and environment-scoped no-record Filament actions can lose their original route/page context on Livewire update requests. When
UiEnforcementfalls back toFilament::getTenant()as product scope truth, an authorized operator can see silent disabled/hidden actions, modals that do not open, or render/submit scope divergence. - Today's failure:
- Initial
/admin/...page render can have enough context, while first-click/livewire/updateaction mounting does not. - Missing product context is collapsed into ordinary non-membership or missing-capability state.
- Modal-open tests and "no OperationRun/job on modal mount" tests exist only for selected local fixes.
- Latent no-record actions such as Entra Group Sync, Evidence Snapshot create, Review Pack generate, Environment Review create, Environment Diagnostics repair, Restore create entry, and Environment Dashboard support actions still need the same contract.
- Initial
- User-visible improvement: Authorized operators get reliable first-click modals and truthful disabled/missing-context states. High-impact actions create runs, dispatch jobs, generate artifacts, or mutate records only on submit after explicit reauthorization with the same workspace/environment context.
- Smallest enterprise-capable version:
- introduce a narrow
UiActionContextcontract for action product scope - make scoped no-record action enforcement require explicit context
- preserve record-backed action behavior where scope comes from the record
- retrofit representative latent-risk no-record actions
- add reusable first-click modal/no-run test support
- add a static guard for risky scoped no-record actions without explicit context
- introduce a narrow
- Explicit non-goals:
- no migrations or persisted action-context table
- no new product feature
- no UI redesign or new route family
- no OperationRun model redesign
- no RBAC architecture rewrite
- no broad rewrite of record-backed row/table/detail actions
- no compatibility mode for old scoped no-record actions
- no Filament panel/provider change
- no global-search behavior change
- no asset pipeline change
- no broad support desk, billing, promotion, AI, or governance-inbox feature work
- Permanent complexity imported: one value object, two small enums, one resolver trait or equivalent helper, one explicit scoped-action API on
UiEnforcement, one reusable test helper, and one static architecture/guard test. No new persistence, operation type, capability string, or cross-domain UI framework is introduced. - Why now: Spec 362 proved this is not a local Inventory/Policy bug. New roadmap/productization work is likely to add modal-first, approval, support, entitlement, and OperationRun-starting actions; those should not inherit an implicit context fallback.
- Why not local: A per-page resolver fix already repeated once. Continuing local fixes would keep every future no-record action responsible for rediscovering the same Livewire transport edge case and would leave no static guard against recurrence.
- Approval class: Core Enterprise.
- Red flags triggered: new abstraction and enum family, static guard, cross-cutting action-contract change. Defense: the abstraction is required for RBAC, tenant/workspace isolation, queue/job legitimacy, and operator trust; scope is limited to no-record scoped actions first and preserves record-backed paths.
- Score: Nutzen: 2 | Dringlichkeit: 2 | Scope: 2 | Komplexitaet: 1 | Produktnaehe: 2 | Wiederverwendung: 2 | Gesamt: 11/12
- Decision: approve.
Candidate Source And Completed-Spec Guardrail
- Candidate source:
- direct user-provided Spec 363 draft in
pasted-text.txt - Spec 362 root-cause audit artifact under
specs/362-sync-capture-backup-operation-semantics/artifacts/action-context-root-cause-audit.md - repo-verified current action patterns under
apps/platform/app/Filamentandapps/platform/app/Support/Rbac
- direct user-provided Spec 363 draft in
- Queue boundary:
docs/product/spec-candidates.mdstates that no safe automatic next-best-prep target remains in the active queue. This package is an intentional manual promotion from direct user input, not an auto-selected backlog item. - Completed-spec check result:
- no
specs/363-*package existed before this prep - no
363-*branch existed before this prep - runtime implementation must run from
feat/363-explicit-uiactioncontext-contractbased ondev; the current prep branch is not the final PR branch shape - Spec 362 is treated as implemented/root-cause context and is not modified
- Specs 338, 340, 358, 359, 360, 361, and 362 are dependency/history context only and must not be rewritten or normalized here
- completed implementation close-out, validation, smoke, and completed task markers in related specs remain historical evidence
- no
- Close alternatives deferred:
OperationRun Start Authorization Contract Hardeningis deferred unless implementation proves execution authorization is weaker than the UI gate across more than this context class- full migration of every record-backed row/table/detail action is deferred because record-derived scope is a different, currently valid context source
- customer-review, governance-inbox, promotion, commercial-entitlement, support-desk, and AI runtime productization remain future consumers of this contract, not hidden scope
- Smallest viable implementation slice: explicit scoped-action context contract, representative no-record action retrofit, reusable tests, and static recurrence guard.
Summary
This feature makes product scope explicit for workspace- and environment-scoped no-record Filament actions.
It does not replace Filament tenancy or record-backed authorization. It says that a no-record action that depends on workspace/environment scope must be given that scope explicitly at render, modal mount, and submit time. Missing context is a distinct fail-closed state, not an ordinary "you are not a member" or "you lack permission" state.
Business/Product Value
- Prevents trust-damaging admin UX failures such as first-click modal misses, mysteriously disabled actions, and context-dependent action state drift.
- Makes high-impact sync, evidence, review, restore, diagnostics, and support actions safer before broader productization work adds more modal-first workflows.
- Reduces repeated implementation drift by making explicit action context a shared contract and a testable guardrail.
Primary Users / Operators
- Tenant/MSP operators who start sync, evidence, review, restore, diagnostics, and support workflows from Filament admin surfaces.
- Workspace owners/managers who expect capability-gated actions to be stable and honest.
- Platform/support operators who need missing-context states to be diagnosable without exposing low-level transport details in the UI.
Roadmap Relationship
This is governance and architecture hardening for the current sellable platform path.
It directly protects future features that will add modal-first and OperationRun-starting actions:
- Customer Review Workspace completion
- Decision-Based Governance Inbox
- Cross-Tenant Compare and Promotion
- Commercial entitlement and billing-state gates
- External Support Desk / PSA Handoff
- governed AI execution surfaces
Those features should build on explicit action context rather than repeating the current local resolver pattern.
Spec Scope Fields (mandatory)
- Scope: canonical-view plus workspace/environment action scope
- Primary Routes:
- existing
/admin/...workspace-owned and environment-scoped Filament pages that expose no-record header/page/empty-state actions - existing environment resource pages for Inventory, Policies, Entra Groups, Evidence Snapshots, Review Packs, Environment Reviews, Restore Runs, Environment Diagnostics, and Environment Dashboard
- existing
- Data Ownership:
- no new table, column, or persisted truth
Workspaceremains primary SaaS contextManagedEnvironmentremains managed target context inside a workspace- existing
OperationRun, audit, evidence, review, backup, restore, and support records keep their current ownership
- RBAC:
- UI visibility/disabled state continues to use
UiEnforcementorWorkspaceUiEnforcement - execution handlers must reauthorize server-side using the explicit workspace/environment context
- non-members and wrong-scope actors remain deny-as-not-found where policy/route semantics require it
- readonly and entitlement states must use the same explicit context used by modal and submit paths
- UI visibility/disabled state continues to use
For canonical-view specs:
- Default filter behavior when tenant-context is active: no-record action scope must come from an explicit page, workspace, environment, or record context resolver. It must not silently use remembered environment state or
Filament::getTenant()as product scope truth. - Explicit entitlement checks preventing cross-tenant leakage: every submitted action must resolve a workspace/environment context that belongs to the current workspace and actor entitlement before creating runs, dispatching jobs, generating artifacts, or mutating records.
UI Surface Impact (mandatory - UI-COV-001)
Does this spec add, remove, rename, or materially change any reachable UI surface?
- No UI surface impact
- Existing page changed
- New page/route added
- Navigation changed
- Filament panel/provider surface changed
- New modal/drawer/wizard/action added
- New table/form/state added
- Customer-facing surface changed
- Dangerous action changed
- Status/evidence/review presentation changed
- Workspace/environment context presentation changed
UI/Productization Coverage (mandatory when UI Surface Impact is not "No UI surface impact")
- Route/page/surface:
App\Filament\Resources\InventoryItemResource\Pages\ListInventoryItemsApp\Filament\Resources\PolicyResourceApp\Filament\Resources\PolicyResource\Pages\ListPoliciesApp\Filament\Resources\EntraGroupResource\Pages\ListEntraGroupsApp\Filament\Resources\EvidenceSnapshotResourceandPages\ListEvidenceSnapshotsApp\Filament\Resources\ReviewPackResourceApp\Filament\Resources\EnvironmentReviewResourceApp\Filament\Pages\EnvironmentDiagnosticsApp\Filament\Resources\RestoreRunResourceApp\Filament\Pages\EnvironmentDashboard
- Current or new page archetype: existing native Filament resource/list/page/action surfaces
- Design depth: Domain Pattern Surface
- Repo-truth level: repo-verified
- Existing pattern reused:
UiEnforcement,WorkspaceUiEnforcement,ResolvesPanelTenantContext, existing Filament action/modal testing patterns, existing OperationRun UX helpers - New pattern required: small action-context contract and static guard; no new page pattern or visual redesign
- Screenshot required: no by default; one bounded Browser smoke only if implementation changes visible hierarchy or missing-context copy beyond existing tooltips/notifications
- Page audit required: no new page-report identity is required by default
- Customer-safe review required: no; affected surfaces are operator/admin surfaces
- Dangerous-action review required: yes for high-impact and destructive-adjacent actions. Existing destructive actions must keep
->action(...),->requiresConfirmation(), server-side authorization, audit logging, and tests. Modal open must not createOperationRunrecords or dispatch jobs. - Coverage files updated or explicitly not needed:
docs/ui-ux-enterprise-audit/route-inventory.mddocs/ui-ux-enterprise-audit/design-coverage-matrix.mddocs/ui-ux-enterprise-audit/page-reports/...docs/ui-ux-enterprise-audit/strategic-surfaces.mddocs/ui-ux-enterprise-audit/grouped-follow-up-candidates.mddocs/ui-ux-enterprise-audit/unresolved-pages.mdN/A - existing reachable UI surface families only
- No-impact rationale when applicable: N/A. This feature changes existing action behavior and workspace/environment context presentation, but does not add a new reachable route/page family.
Cross-Cutting / Shared Pattern Reuse (mandatory)
- Cross-cutting feature?: yes
- Interaction class(es): header actions, page actions, empty-state actions, modal actions, OperationRun-starting actions, support/diagnostic actions, status/tooltip copy
- Systems touched:
App\Support\Rbac\UiEnforcementApp\Support\Rbac\WorkspaceUiEnforcementas comparison context only unless implementation chooses to share a small value objectApp\Filament\Concerns\ResolvesPanelTenantContext- existing Filament resource/page action definitions
- existing action tests and guard tests
- existing
OperationRuncreation/dispatch paths only through current handlers
- Existing pattern(s) to extend:
UiEnforcement,WorkspaceUiEnforcement, current page context resolvers, current Filament action tests - Shared contract / presenter / builder / renderer to reuse:
UiEnforcementand page/record resolvers; no presenter or renderer is introduced - Why the existing shared path is sufficient or insufficient: the shared path exists, but
UiEnforcementstill allows ambiguous no-record scoped actions to fall back to implicit Filament tenant context. The contract must make scoped action context explicit. - Allowed deviation and why: a small new
App\Support\Rbac\Actionsnamespace is allowed if it keeps action context isolated and avoids bloatingUiEnforcementwith unrelated UI framework semantics. - Consistency impact: missing-context handling, readonly/capability disabled state, modal mount behavior, OperationRun dispatch timing, and server reauthorization must stay consistent across all retrofitted actions.
- Review focus: no hidden
Filament::getTenant()fallback for no-record scoped actions, no modal-open side effects, no compatibility shim, no broad record-backed rewrite.
OperationRun UX Impact (mandatory)
- Touches OperationRun start/completion/link UX?: yes, start semantics only
- Shared OperationRun UX contract/layer reused: existing
OperationRunService, action handlers,OperationRunLinks,OperationUxPresenter, andOpsUxBrowserEvents - Delegated start/completion UX behaviors: queued toast,
Open operation/View runlinks, run-enqueued browser events, and terminal notification paths remain unchanged - Local surface-owned behavior that remains: action form inputs, confirmation wording, and missing-context notification/tooltip copy
- Queued DB-notification policy: unchanged
- Terminal notification path: unchanged
- Exception required?: none. If execution authorization is found weaker than the UI gate beyond this context class, stop and create a follow-up instead of widening this spec.
Provider Boundary / Platform Core Check (mandatory)
- Shared provider/platform boundary touched?: no new provider boundary is introduced
- Boundary classification: platform-core UI/RBAC action context over current provider-backed operations
- Seams affected: Filament action scope, workspace/environment context, run/job dispatch inputs, support/diagnostic mutation inputs
- Neutral platform terms preserved or introduced:
workspace,environment,record,system,product scope,action context - Provider-specific semantics retained and why: Microsoft/Graph semantics remain inside existing job/service handlers; this spec does not add provider-specific endpoints or contracts
- Why this does not deepen provider coupling accidentally: the new contract names workspace/environment product scope rather than Microsoft tenant transport state, and it forbids treating Filament tenant context as platform truth for no-record scoped actions
- Follow-up path: only
OperationRun Start Authorization Contract Hardeningif broader execution weakness is repo-verified during implementation
UI / Surface Guardrail Impact
| Surface / Change | Operator-facing surface change? | Native vs Custom | Shared-Family Relevance | State Layers Touched | Exception Needed? | Low-Impact / N/A Note |
|---|---|---|---|---|---|---|
| Scoped no-record header/page actions | yes | Native Filament actions | header/page/modal action family | page, modal, Livewire action mount | no | existing pages only |
| Missing action context state | yes | Native disabled/hidden/notification affordances | RBAC UX family | page, action state | no | copy must remain enterprise-safe |
| OperationRun-starting action submit path | yes | Existing services/jobs | OperationRun start family | action submit, queue/job dispatch | no | completion UX unchanged |
Decision-First Surface Role
| Surface | Decision Role | Human-in-the-loop Moment | Immediately Visible for First Decision | On-Demand Detail / Evidence | Why This Is Primary or Why Not | Workflow Alignment | Attention-load Reduction |
|---|---|---|---|---|---|---|---|
| Scoped action button/modal | Primary Action Surface | Operator decides whether to start sync, generate artifact, create review, repair diagnostics, restore, or request support | action enabled/disabled state and confirmation/modal availability | existing form fields, modal text, run link after submit | primary because it is where work begins | keeps scope decision attached to the action | avoids "button does nothing" and wrong permission interpretation |
| Missing-context tooltip/notification | Secondary Explanation Surface | Operator needs to understand why a scoped action is unavailable | concise "environment context unavailable" explanation | none by default | secondary because it explains a blocked action | prevents false permission framing | avoids support/debug reconstruction |
| OperationRun link after submit | Secondary Follow-through Surface | Operator inspects queued work | existing run link/notification | existing operation detail diagnostics | unchanged existing pattern | same OperationRun workflow | avoids duplicating result truth in modal |
Audience-Aware Disclosure
| Surface | Audience Modes In Scope | Decision-First Default-Visible Content | Operator Diagnostics | Support / Raw Evidence | One Dominant Next Action | Hidden / Gated By Default | Duplicate-Truth Prevention |
|---|---|---|---|---|---|---|---|
| Scoped action button/modal | operator-MSP, support-platform | action state, form, confirmation, safe context wording | context source only in tests/log-safe diagnostics unless already appropriate | no raw Livewire/request detail in UI | run/submit the action or reload/select environment | technical transport details such as getTenant null |
modal state and submit handler use one explicit context |
| Missing-context state | operator-MSP, support-platform | "Environment context unavailable" style copy | no default raw IDs | raw request/referer data remains outside UI | reload workspace or select environment | low-level Livewire/Filament internals | missing context is distinct from permission denial |
UI/UX Surface Classification
| Surface | Action Surface Class | Surface Type | Likely Next Operator Action | Primary Inspect/Open Model | Row Click | Secondary Actions Placement | Destructive Actions Placement | Canonical Collection Route | Canonical Detail Route | Scope Signals | Canonical Noun | Critical Truth Visible by Default | Exception Type / Justification |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Existing resource/page header actions | Action / Modal / Workbench | High-impact no-record action | open modal, confirm, submit, or reload/select context | action/modal | N/A | existing page actions | existing confirmation/modal patterns | existing route family | existing route family | workspace/environment action context | Action / Operation | whether context and capability allow the action | none |
| Existing empty-state actions | Action / Empty State | No-record generation action | create first snapshot/pack/review where allowed | action/modal | N/A | empty state | existing confirmation/modal patterns | existing route family | existing route family | explicit environment context | Action / Artifact | context and authorization are consistent | none |
Operator Surface Contract
| Surface | Primary Persona | Decision / Operator Action Supported | Surface Type | Primary Operator Question | Default-visible Information | Diagnostics-only Information | Status Dimensions Used | Mutation Scope | Primary Actions | Dangerous Actions |
|---|---|---|---|---|---|---|---|---|---|---|
| Scoped no-record actions | Tenant operator / MSP operator | Start or generate environment/workspace work safely | existing Filament action/modal | Can I run this action for this exact workspace/environment now? | enabled/disabled/missing-context state and modal inputs | context source and technical request details only in tests/log-safe diagnostics | context present, membership, capability, entitlement, readonly | TenantPilot and queued provider work depending action | sync, generate, create, repair, request support | restore, diagnostics repair, delete/expire-adjacent actions keep confirmation and server authorization |
Proportionality Review (mandatory when structural complexity is introduced)
- New source of truth?: no persisted source of truth.
UiActionContextis a request-time product-scope contract. - New persisted entity/table/artifact?: no.
- New abstraction?: yes, a narrow action-context value object/helper contract.
- New enum/state/reason family?: yes, small request-time scope/source enums and missing-context reason classification.
- New cross-domain UI framework/taxonomy?: no.
- Current operator problem: trusted admin actions can silently fail, hide, disable, or mount inconsistently because product scope is implicit on Livewire update requests.
- Existing structure is insufficient because:
UiEnforcementaccepts nullable no-record context and can fall back toFilament::getTenant(), while workspace-first admin surfaces need page/record/workspace/environment product context. - Narrowest correct implementation: make scoped no-record action context explicit, preserve valid record-backed context resolution, and guard only the risky class first.
- Ownership cost: one reusable contract, focused action retrofits, static guard maintenance, and action lifecycle tests for high-impact no-record actions.
- Alternative intentionally rejected: keep adding local page resolver closures without a central contract. Rejected because it leaves recurrence likely and missing context indistinguishable from authorization denial.
- Release truth: current-release truth. The issue is repo-verified and already required local fixes.
Compatibility posture
This feature assumes a pre-production environment.
Backward compatibility, legacy aliases, migration shims, historical fixtures, and compatibility-specific tests are out of scope. No compatibility fallback is allowed for scoped no-record actions that should use explicit context.
Record-backed actions are not legacy exceptions; they are a different valid context source.
Testing / Lane / Runtime Impact (mandatory for runtime behavior changes)
- Test purpose / classification: Unit for context value objects and
UiEnforcementcontext handling; Feature/Filament-Livewire for action visibility, first-click modal mount, submit reauthorization, and no-run/no-job-on-modal-open; Architecture/Feature guard for static source scanning; Browser optional smoke only if visible hierarchy/copy changes materially. - Validation lane(s): fast-feedback, confidence, browser optional
- Why this classification and these lanes are sufficient: the risk is request-time action context and Filament/Livewire lifecycle behavior, not schema or PostgreSQL-specific persistence.
- New or expanded test families: one explicit Spec 363 Unit family, one explicit Spec 363 Feature/Architecture guard, and targeted extensions to existing action tests.
- Fixture / helper cost impact: existing workspace/environment/user/capability factories plus current queue fakes; no new global seed or heavy default context.
- Heavy-family visibility / justification: no heavy-governance family by default; Browser only if UI copy/hierarchy changes require visual smoke.
- Special surface test profile: standard-native-filament plus action-lifecycle-contract.
- Standard-native relief or required special coverage: use Filament action testing helpers for pages/actions. Header actions use
mountAction()/callAction()and table header actions may useFilament\Actions\Testing\TestAction::make(...)->table()where appropriate. - Reviewer handoff: reviewers must confirm no modal-open side effects, no implicit
Filament::getTenant()product-scope fallback for scoped no-record actions, no compatibility shim, and no broad record-backed churn. - Budget / baseline / trend impact: none expected beyond targeted action lifecycle tests.
- Escalation needed: document-in-feature if a single false positive needs a bounded static-guard exception; follow-up-spec if execution authorization is broadly weaker than UI gating.
- Active feature PR close-out entry: Guardrail / Smoke Coverage.
- Planned validation commands:
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.phpcd apps/platform && ./vendor/bin/sail bin pint --dirty --format agentgit diff --check
User Scenarios & Testing (mandatory)
User Story 1 - Explicit Context Contract (Priority: P1)
As a tenant operator, I need scoped actions to evaluate against the same workspace/environment context during render, first click, and submit, so that action state is reliable and not dependent on Livewire transport details.
Why this priority: This is the foundation that prevents first-click modal failures and render/submit drift across all retrofitted actions.
Independent Test: Unit tests prove UiActionContext can represent workspace, environment, record, system, and missing context, and UiEnforcement fails closed for scoped no-record actions without explicit context while preserving record-backed behavior.
Acceptance Scenarios:
- Given a no-record environment-scoped action with explicit environment context, When Livewire evaluates visibility and disabled state, Then it uses that context instead of implicit Filament tenant fallback.
- Given a no-record environment-scoped action with missing context, When action state is evaluated, Then the action fails closed with missing-context reason distinct from non-membership and missing capability.
- Given a record-backed table/detail action, When action state is evaluated, Then the existing record-derived scope remains valid without broad retrofit churn.
User Story 2 - Stable First-Click Modal Lifecycle (Priority: P1)
As an operator starting sync, evidence, review, restore, diagnostics, or support work, I need the modal or confirmation to open on first click without creating a run, dispatching a job, or mutating records until I submit.
Why this priority: The repo-verified failure mode is user-visible trust damage: buttons appear broken or create inconsistent action lifecycle behavior.
Independent Test: Filament/Livewire action tests mount each representative no-record action under workspace-scoped Livewire referer context, assert the action is mounted/open, and assert no OperationRun, queued provider job, audit side effect, or domain mutation happens on modal open unless explicitly intended and tested.
Acceptance Scenarios:
- Given an authorized operator on the Inventory list with no Filament tenant on the Livewire update request, When they first click Run Inventory Sync, Then the modal mounts and no
inventory.syncrun or job is created. - Given an authorized operator on Evidence Snapshot, Review Pack, Environment Review, Restore, Diagnostics, or Environment Dashboard surfaces, When they first click the scoped action, Then modal/confirmation state uses explicit action context and creates no run/job/mutation until submit.
- Given a readonly or entitlement-blocked operator, When action state is evaluated and submitted, Then disabled state and server-side denial use the same explicit context and no work is queued.
User Story 3 - Recurrence Guard For Risky Actions (Priority: P1)
As a reviewer, I need a static guard that flags risky no-record scoped actions without explicit context so future feature work cannot reintroduce the same pattern.
Why this priority: Without a guard, the next modal-first or OperationRun-starting feature can repeat the bug even if current actions are fixed.
Independent Test: A Pest architecture/guard test scans apps/platform/app/Filament for risky scoped no-record action patterns and fails with actionable output when a candidate lacks UiActionContext / forScopedAction / canonical resolver markers.
Acceptance Scenarios:
- Given a new no-record scoped action that uses
UiEnforcement::forAction()and can dispatch work, open a modal, or create anOperationRun, When it lacks explicit context, Then the guard fails with file/action/fix guidance. - Given a record-backed row/table/detail action, When it safely derives scope from the record, Then the guard does not require unnecessary
UiActionContextchurn. - Given an unavoidable false positive, When a bounded exception is needed, Then it must be documented in this feature or a follow-up with a specific reason, not hidden in a permanent legacy allowlist.
User Story 4 - Operator-Safe Missing Context Copy (Priority: P2)
As an operator, I need missing context to be explained as context unavailability, not as a false permission denial or low-level Livewire/Filament error.
Why this priority: The primary trust issue is silent or misleading UX. Copy does not need a full localization project, but it must be localizable later and not expose transport internals.
Independent Test: Feature tests assert missing-context state produces a distinct reason and, where visible, enterprise-safe copy such as "Environment context unavailable."
Acceptance Scenarios:
- Given an action should remain visible but context cannot be resolved, When the operator sees the disabled state, Then the tooltip/notification says the environment/workspace context is unavailable rather than "you lack permission."
- Given missing context is logged or asserted in tests, When diagnostics are inspected, Then no secret, token, raw Graph payload, or unsupported request internals are exposed in UI copy.
Functional Requirements
- FR-363-001: The implementation MUST introduce an explicit action-context contract that can represent workspace, environment, record, system, and missing scoped action context without persistence.
- FR-363-002: The implementation MUST support a distinct missing-context reason such as
context_missing,workspace_missing, orenvironment_missing. - FR-363-003:
UiEnforcementMUST accept explicit scoped action context for no-record workspace/environment actions. - FR-363-004: Scoped no-record actions MUST NOT silently rely on
Filament::getTenant()as product scope truth. - FR-363-005: Record-backed actions MAY continue to resolve context from their record when that record is the product scope source.
- FR-363-006: Missing context MUST fail closed and MUST be distinguishable from non-membership and missing capability in tests.
- FR-363-007: First-click modal/confirmation mount MUST NOT create an
OperationRun, dispatch provider/write jobs, or mutate domain state unless a specific action intentionally audits open and tests that behavior. - FR-363-008: Submit/execute handlers MUST reauthorize server-side using the explicit context before creating runs, dispatching jobs, generating artifacts, or mutating data.
- FR-363-009: Representative no-record actions MUST be retrofitted:
run_inventory_sync, Policy Sync,sync_groups,create_snapshot,create_first_snapshot,generate_pack,generate_first,create_review,bootstrapOwner,mergeDuplicateMemberships, Restore create entry, and Environment Dashboard support actions. - FR-363-010: A static guard MUST flag risky scoped no-record action patterns without explicit context and provide actionable failure output.
- FR-363-011: The implementation MUST keep destructive and high-impact actions on
Action::make(...)->action(...)with->requiresConfirmation()where currently required. - FR-363-012: The implementation MUST NOT add new capabilities, operation types, routes, panels, assets, migrations, or global-search behavior.
Non-Functional Requirements
- NFR-363-001: Filament remains v5 and Livewire remains v4.0+ compliant.
- NFR-363-002: Laravel 12 panel providers remain registered in
apps/platform/bootstrap/providers.php; no provider registration change is planned. - NFR-363-003: No Graph call may occur during render, action visibility, disabled-state evaluation, or modal mount.
- NFR-363-004: Tests must stay focused: Unit + Feature/Filament-Livewire + static guard, with Browser only if visible behavior changes materially.
- NFR-363-005: Missing-context copy must be enterprise-safe and localizable later; it must not expose low-level Livewire request state.
- NFR-363-006: Static guard false positives must be resolved by narrowing the guard or documenting bounded exceptions, not by a broad legacy allowlist.
Out Of Scope
- OperationRun status/outcome redesign.
- New persisted context/audit table.
- Full action-surface discovery framework rewrite.
- Full migration of every record-backed action.
- UI redesign or navigation changes.
- Customer-facing portal/review productization.
- Commercial billing-state implementation.
- Cross-tenant promotion execution.
- Support desk/PSA integration.
- AI execution governance implementation.
Acceptance Criteria
UiActionContextor an equivalent explicit request-time context contract exists and is covered by Unit tests.UiEnforcementsupports explicit scoped action context and no scoped no-record action silently relies onFilament::getTenant()as product scope truth.- Missing workspace/environment context fails closed and is test-observable as missing context, not only as non-membership or missing capability.
- Representative no-record actions listed in this spec use explicit context or have documented repo-verified reasons for deferral.
- First-click modal/confirmation mount tests prove no
OperationRun, provider/write job, or domain mutation occurs before submit. - Submit/execute tests prove server-side reauthorization with explicit context.
- A static guard fails future risky scoped no-record actions without explicit context and reports actionable fix guidance.
- No migrations, assets, panel/provider changes, route/navigation changes, capability strings, operation types, or global-search changes are introduced.
Data And Truth Source Requirements
UiActionContextis request-time derived truth, not persistence.- Workspace/environment product scope is authoritative for no-record scoped actions.
- Filament tenant context remains a framework transport/detail and may be used only when explicitly resolved into product context by a repo-approved resolver.
OperationRunremains the run/dispatch truth after submit.- Existing audit logs remain action-side-effect truth where handlers already audit.
Auditability And Observability Requirements
- Existing audit events for support, diagnostics, restore, generation, sync, and run-start actions must remain.
- Modal open must not create audit records unless the existing action intentionally records a safe open/read event and tests it.
- OperationRun creation must include explicit workspace/environment scope as it does today through the handler's run-start path.
- Static guard failures must name the file/action/reason/fix so reviewers can act without reverse-engineering the scan.
Assumptions
- Current application versions are PHP 8.4.15, Laravel 12.52, Filament 5.2.1, Livewire 4.1.4, Pest 4.3.1, PostgreSQL via Sail.
- The product remains pre-production, so no legacy compatibility shim is required for old scoped no-record behavior.
ManagedEnvironmentis still the environment/tenant model used by current runtime code.- Existing policies, capability resolvers, and domain services remain the server-side authorization owners.
Risks
- The static guard may initially produce false positives for record-backed or safe URL-only actions.
- Retrofitting many action surfaces can conflict with active feature work if done as one broad PR.
- Missing-context copy could become noisy if visible for actions that should be hidden entirely.
- If implementation reveals broader execution authorization weakness, that must become a follow-up spec rather than expanding this package silently.
Open Questions
- None blocking preparation. During implementation, exact action names and file names must be reverified against current repo truth before edits.
Success Criteria
- Representative no-record scoped actions mount reliably on first click under workspace-scoped Livewire requests.
- Missing context is test-observable and distinct from permission denial.
- Modal open creates no run/job/mutation for retrofitted actions.
- Submit paths reauthorize and use explicit context.
- Static guard catches new risky scoped no-record actions without explicit context.
- No migrations, assets, panel provider changes, or global-search changes are introduced.
Follow-Up Spec Candidates
Only create a follow-up if implementation discovers repo-verified broader weakness:
OperationRun Start Authorization Contract Hardening: required only if execution-level authorization is inconsistent beyond UI action context.