TenantAtlas/specs/363-explicit-uiactioncontext-contract/spec.md
Ahmed Darrazi 9e435ea91f
Some checks failed
PR Fast Feedback / fast-feedback (pull_request) Failing after 1m2s
feat: implement explicit UiActionContext contract
2026-06-07 13:12:02 +02:00

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.md classifies 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 OperationRun records, dispatch jobs, generate artifacts, or mutate support/diagnostics state.

Current repo truth already provides useful foundations:

  • App\Support\Rbac\UiEnforcement centralizes environment-scoped UI affordance rules.
  • App\Support\Rbac\WorkspaceUiEnforcement already uses a stricter explicit workspace source pattern.
  • App\Filament\Concerns\ResolvesPanelTenantContext can resolve panel context across Livewire referer requests.
  • OperationRun and 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 UiEnforcement falls back to Filament::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/update action 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.
  • 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 UiActionContext contract 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
  • 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/Filament and apps/platform/app/Support/Rbac
  • Queue boundary: docs/product/spec-candidates.md states 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-contract based on dev; 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
  • Close alternatives deferred:
    • OperationRun Start Authorization Contract Hardening is 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
  • Data Ownership:
    • no new table, column, or persisted truth
    • Workspace remains primary SaaS context
    • ManagedEnvironment remains 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 UiEnforcement or WorkspaceUiEnforcement
    • 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

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\ListInventoryItems
    • App\Filament\Resources\PolicyResource
    • App\Filament\Resources\PolicyResource\Pages\ListPolicies
    • App\Filament\Resources\EntraGroupResource\Pages\ListEntraGroups
    • App\Filament\Resources\EvidenceSnapshotResource and Pages\ListEvidenceSnapshots
    • App\Filament\Resources\ReviewPackResource
    • App\Filament\Resources\EnvironmentReviewResource
    • App\Filament\Pages\EnvironmentDiagnostics
    • App\Filament\Resources\RestoreRunResource
    • App\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 create OperationRun records or dispatch jobs.
  • Coverage files updated or explicitly not needed:
    • docs/ui-ux-enterprise-audit/route-inventory.md
    • docs/ui-ux-enterprise-audit/design-coverage-matrix.md
    • docs/ui-ux-enterprise-audit/page-reports/...
    • docs/ui-ux-enterprise-audit/strategic-surfaces.md
    • docs/ui-ux-enterprise-audit/grouped-follow-up-candidates.md
    • docs/ui-ux-enterprise-audit/unresolved-pages.md
    • N/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\UiEnforcement
    • App\Support\Rbac\WorkspaceUiEnforcement as comparison context only unless implementation chooses to share a small value object
    • App\Filament\Concerns\ResolvesPanelTenantContext
    • existing Filament resource/page action definitions
    • existing action tests and guard tests
    • existing OperationRun creation/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: UiEnforcement and page/record resolvers; no presenter or renderer is introduced
  • Why the existing shared path is sufficient or insufficient: the shared path exists, but UiEnforcement still 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\Actions namespace is allowed if it keeps action context isolated and avoids bloating UiEnforcement with 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, and OpsUxBrowserEvents
  • Delegated start/completion UX behaviors: queued toast, Open operation / View run links, 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 Hardening if 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. UiActionContext is 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: UiEnforcement accepts nullable no-record context and can fall back to Filament::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 UiEnforcement context 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 use Filament\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.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
    • cd apps/platform && ./vendor/bin/sail bin pint --dirty --format agent
    • git 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:

  1. 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.
  2. 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.
  3. 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:

  1. 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.sync run or job is created.
  2. 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.
  3. 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:

  1. Given a new no-record scoped action that uses UiEnforcement::forAction() and can dispatch work, open a modal, or create an OperationRun, When it lacks explicit context, Then the guard fails with file/action/fix guidance.
  2. Given a record-backed row/table/detail action, When it safely derives scope from the record, Then the guard does not require unnecessary UiActionContext churn.
  3. 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:

  1. 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."
  2. 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, or environment_missing.
  • FR-363-003: UiEnforcement MUST 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

  • UiActionContext or an equivalent explicit request-time context contract exists and is covered by Unit tests.
  • UiEnforcement supports explicit scoped action context and no scoped no-record action silently relies on Filament::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

  • UiActionContext is 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.
  • OperationRun remains 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.
  • ManagedEnvironment is 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.