Implemented sync capture backup operation semantics as requested. Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de> Reviewed-on: #433
38 KiB
Livewire Action Context Contract Root-Cause Audit
Date: 2026-06-07
Branch: 362-sync-capture-backup-operation-semantics
Scope: read-only root-cause audit plus this report artifact. No runtime/source/test files were changed by this audit.
Executive Summary
- Final classification: Case C - Systemic Action Context Contract Gap (
derived, confidence: high). - Readiness impact: workspace/environment-scoped header and page actions remain fragile unless they pass explicit action context and have first-click modal regression tests (
repo-verified,test-coveredfor the fixed inventory/policy actions only). - Sellability impact: this can surface as "button does nothing", "modal does not open", or "action is disabled for an authorized operator" in admin-safe workflows, which weakens trust in backup, sync, evidence, review, and restore surfaces (
derived). - Product risk: the highest risk is not unauthorized execution; the current failure mode usually fails closed. The risk is silent UX failure, wrong disabled state, inconsistent render-vs-submit context, and repeated implementation drift (
repo-verified,derived). - Recommended next move: create a dedicated spec for an explicit Livewire action-context contract before adding new modal-first workspace/environment actions (
derived).
Selected case:
| Case | Result | Evidence |
|---|---|---|
| Case A - Local bug only | Rejected | Same fix pattern appears in InventoryItemResource and PolicyResource, and similar no-record actions still exist (repo-verified). |
| Case B - Repeated implementation drift | Partially true | Several pages resolve tenant/environment context independently (repo-verified). |
| Case C - Systemic Action Context Contract Gap | Selected | UiEnforcement still falls back to Filament::getTenant() for no-record actions, while workspace-scoped Livewire requests need canonical page/referer context (repo-verified, test-covered). |
| Case D - Fundamental architectural mismatch | Not selected | The repo already has canonical workspace/environment resolvers and WorkspaceUiEnforcement; the issue is incomplete enforcement of that contract, not total architectural inversion (repo-verified, derived). |
Repo State
Commands executed:
git status --short --branch
git diff --name-only
git diff --stat
git diff --cached --name-only
Current branch: 362-sync-capture-backup-operation-semantics (repo-verified).
Staged files: none (repo-verified).
Tracked dirty files before report creation (repo-verified):
apps/platform/app/Filament/Concerns/ResolvesPanelTenantContext.phpapps/platform/app/Filament/Resources/InventoryItemResource/Pages/ListInventoryItems.phpapps/platform/app/Filament/Resources/PolicyResource.phpapps/platform/app/Filament/Resources/PolicyResource/Pages/ListPolicies.phpapps/platform/app/Models/OperationRun.phpapps/platform/app/Services/AdapterRunReconciler.phpapps/platform/app/Support/Navigation/RelatedNavigationResolver.phpapps/platform/app/Support/OperationRunLinks.phpapps/platform/app/Support/Operations/LifecycleReconciliationReason.phpapps/platform/app/Support/Operations/Reconciliation/OperationRunReconciliationRegistry.phpapps/platform/app/Support/Operations/Reconciliation/RestoreExecuteReconciliationAdapter.phpapps/platform/app/Support/Rbac/UiEnforcement.phpapps/platform/tests/Feature/Console/ReconcileBackupScheduleOperationRunsCommandTest.phpapps/platform/tests/Feature/Filament/InventoryItemResourceTest.phpapps/platform/tests/Feature/PolicySyncStartSurfaceTest.phpapps/platform/tests/Unit/Support/Operations/Reconciliation/Spec359ReconciliationResultTest.phpapps/platform/tests/Unit/Support/Operations/Reconciliation/Spec360CanonicalAdapterRegistryTest.phpapps/platform/tests/Unit/Support/Operations/Reconciliation/Spec361ArtifactRegistryResolutionTest.php
Untracked files before report creation include Spec 362 code/tests and specs/362-sync-capture-backup-operation-semantics/ (repo-verified).
Diff summary before report creation: 18 tracked files changed, 241 insertions, 25 deletions (repo-verified).
Is the local fix still open? Yes. The dirty diff includes the UiEnforcement, inventory action, policy action, and Livewire referer resolver changes (repo-verified).
Is the worktree safe for a read-only audit? Yes, with caution: runtime files are dirty, so this audit did not overwrite, format, refactor, or stage them (derived).
Tests Executed
Read-only targeted test command:
cd apps/platform
./vendor/bin/sail artisan test --compact \
tests/Feature/Filament/InventoryItemResourceTest.php \
tests/Feature/PolicySyncStartSurfaceTest.php \
tests/Unit/Filament/ResolvesPanelTenantContextLivewireRefererTest.php \
tests/Feature/RunStartAuthorizationTest.php
Result: 18 passed, 121 assertions (test-covered).
Browser verification was not executed in this audit (not verified). The request described prior browser verification for Run Inventory Sync; this report treats that as prior context, not as audit-executed evidence.
Trigger Case
What broke:
- A no-record header/page action such as
run_inventory_syncorsyncrendered in a workspace-scoped admin page, but the first Livewire action click/mount evaluated action visibility/disabled state without stable environment context (repo-verified,derived). - The result was a modal that did not open, a wrong disabled/visibility state, or an action state mismatch between initial render and Livewire mounted action request (
derived).
Why the local fix worked:
UiEnforcement::forAction()now acceptsModel|Closure|null $recordand stores it as an explicit context source (apps/platform/app/Support/Rbac/UiEnforcement.php:65) (repo-verified).ListInventoryItemspassesfn (): ?ManagedEnvironment => static::resolveTenantContextForCurrentPanel()intoUiEnforcement(apps/platform/app/Filament/Resources/InventoryItemResource/Pages/ListInventoryItems.php:69,:245) (repo-verified).PolicyResource::makeSyncAction()passes the same explicit resolver (apps/platform/app/Filament/Resources/PolicyResource.php:122,:182) (repo-verified).ResolvesPanelTenantContextnow infers the admin panel from Livewire update requests using therefererpath when route/current panel context is absent (apps/platform/app/Filament/Concerns/ResolvesPanelTenantContext.php:56-117) (repo-verified,test-covered).
Which context was missing:
- Product environment context, represented by
ManagedEnvironment, was missing during the mounted action Livewire request (derived). Filament::getTenant()was not a stable truth source in that request shape because/livewire/updateis not the original/admin/...page route (repo-verified,derived).
Which previous assumption was wrong:
UiEnforcementassumed a no-record header/page action could safely fall back toFilament::getTenant()(apps/platform/app/Support/Rbac/UiEnforcement.php:641-694) (repo-verified).- In TenantPilot, workspace-first admin surfaces can represent environment context through page shell, workspace session, URL/referer, or record scope rather than Filament tenancy alone (
repo-verified,derived).
Tests that cover the local fix:
- Inventory first-click modal mount with no Filament tenant and Livewire referer:
apps/platform/tests/Feature/Filament/InventoryItemResourceTest.php:93-117(test-covered). - Policy sync first-click modal mount with no Filament tenant and Livewire referer:
apps/platform/tests/Feature/PolicySyncStartSurfaceTest.php:93-116(test-covered). - Resolver unit test for Livewire referer panel/environment reconstruction:
apps/platform/tests/Unit/Filament/ResolvesPanelTenantContextLivewireRefererTest.php:15-55(test-covered).
Tests that do not cover the broader class:
- No central test enumerates all workspace/environment-scoped no-record header actions and asserts first-click modal mount (
repo-verified). - No static guard fails new
UiEnforcement::forAction(...)no-record OperationRun actions without explicit context (not implemented). RunStartAuthorizationTestcovers readonly and cross-tenant start prevention for representative actions, but mostly withFilament::setTenant(...), not workspace-scoped Livewire referer (apps/platform/tests/Feature/RunStartAuthorizationTest.php:18-132) (test-covered,test-file-onlyfor the missing referer variant).
Context Model
| Context Axis | Primary Source | Stable Across Livewire Mounted Action? | Used By UiEnforcement? |
Risk |
|---|---|---|---|---|
| Workspace | WorkspaceContext, route/query/page context, workspace record |
Usually stable if explicitly passed | No, except via WorkspaceUiEnforcement |
Medium (repo-verified) |
| Managed Environment / Tenant | record, page resolver, OperateHubShell, ManagedEnvironment::current() |
Stable only if explicit resolver handles Livewire referer | Yes | High for no-record header actions (repo-verified, derived) |
| Filament Tenant Context | Filament::getTenant() / Filament::setTenant() |
Not stable enough for workspace-scoped Livewire update requests | Yes, final fallback | High (repo-verified, test-covered) |
| Page Context | page methods, shell context, URL/referer | Stable when resolver is designed for Livewire | Indirect only if closure passed | Medium (repo-verified) |
| Livewire Request Context | /livewire/update, x-livewire, referer |
Different from initial page route | Only through custom resolver | High (repo-verified) |
| Mounted Action Context | Filament action record/form state | Stable for record-backed actions, weak for no-record actions | Partly via $action->getRecord() |
Medium (repo-verified) |
| OperationRun Context | explicit tenant, managed_environment_id, identity inputs, context payload |
Stable if created from canonical tenant | Not directly | High if UI and execution context diverge (repo-verified, derived) |
| RBAC Capability Context | CapabilityResolver, WorkspaceCapabilityResolver |
Stable if scope is explicit | Yes | High if scope is implicit/missing (repo-verified) |
Product architecture conclusion: TenantPilot is workspace-first; environment/tenant is a product scope inside workspace. Filament tenant context can be a transport/detail, but it must not be treated as product scope truth unless explicitly justified (derived).
UiEnforcement Contract Assessment
Current design (repo-verified):
UiEnforcement::forAction(Action $action, Model|Closure|null $record = null)accepts a direct model or closure (apps/platform/app/Support/Rbac/UiEnforcement.php:65).resolveTenantWithRecord()resolves in this order: passed record, owned record, action record, stored closure/model, thenFilament::getTenant()(apps/platform/app/Support/Rbac/UiEnforcement.php:641-694).- Missing user or tenant returns
TenantAccessContext(user: null, tenant: null, isMember: false, hasCapability: false)(apps/platform/app/Support/Rbac/UiEnforcement.php:607-620). - That fail-closed behavior hides or disables actions depending on membership/capability application (
repo-verified).
Observed issue (repo-verified, derived):
- For no-record header actions, the final fallback is still
Filament::getTenant(). - Missing context is treated the same as non-membership for UI state, so the user can see a silent hidden/disabled action instead of an explanatory "context unavailable" state.
UiEnforcementis a UX gate, but several action handlers also resolve scope again, sometimes through page resolvers and sometimes from records.
Better contract (derived):
UiEnforcementshould not infer product scope for scoped no-record actions.- New workspace/environment actions should receive an explicit
UiActionContextor canonical page/record resolver. - Missing context should be distinguishable from non-membership and insufficient capability for observability and UX copy.
- OperationRun-starting handlers must reauthorize at submit/execution time using the same explicit scope.
Migration effort (derived):
- Minimal guardrail: low/medium, by requiring explicit context on no-record
UiEnforcement::forActioncalls that start runs or mutate state. - DTO/contract: medium, because many callsites are record-backed and should not be churned unnecessarily.
- Static CI guard: medium, because source scanning must avoid false positives for record-backed detail/table actions.
Action Lifecycle Assessment
| Phase | What Happens | Risk |
|---|---|---|
| Initial render | Filament evaluates visibility/disabled closures with page request context. | Usually correct when /admin/... context exists (derived). |
| First click / mount action | Livewire sends update request; route/path no longer proves original admin page. | Highest risk for no-record header actions (repo-verified, test-covered). |
| Modal mount | Form defaults and modal visibility are evaluated. | Hidden fields/defaults can be wrong if environment context is missing (repo-verified, derived). |
| Modal open | Should not create OperationRun or dispatch job. | Covered for inventory/policy; not centrally covered (test-covered, not implemented). |
| Modal submit / action execution | Action handler resolves tenant and creates run/job or mutates state. | Safer when handler re-resolves/validates context; risky when render and submit use different sources (repo-verified, derived). |
| OperationRun creation | OperationRunService receives tenant and context. |
Correct only if tenant source is canonical and reauthorized (repo-verified, derived). |
Callsite Inventory
Static search result: 83 UiEnforcement::forAction matches in apps/platform/app/Filament (repo-verified). The table below groups the relevant action-context risk surfaces rather than treating record-backed CRUD helpers as the same failure class.
| File / Class | Action | Type | Scope | Explicit Context | Record? | Modal? | OperationRun / External Effect | Reauth / Submit Guard | Risk | Evidence |
|---|---|---|---|---|---|---|---|---|---|---|
InventoryItemResource/Pages/ListInventoryItems.php |
run_inventory_sync |
Header | Environment | Yes, page resolver | No | Yes | Creates inventory.sync run/job on submit |
Context mismatch guard via hidden managed_environment_id; readonly tests |
Medium after local fix | repo-verified, test-covered |
PolicyResource.php / ListPolicies.php |
sync |
Header | Environment | Yes, page resolver | No | Confirmation modal | Creates policy.sync run/job on submit |
Missing context aborts 404 | Medium after local fix | repo-verified, test-covered |
EntraGroupResource/Pages/ListEntraGroups.php |
sync_groups |
Header | Environment | No | No | No modal | Starts directory group sync via service | Handler re-resolves tenant and returns on missing context | High latent | repo-verified, test-file-only |
EvidenceSnapshotResource/Pages/ListEvidenceSnapshots.php |
create_snapshot |
Header | Environment | No | No | Form modal | Generates evidence snapshot/run | Execution resolves tenant and returns notification on missing context | High latent | repo-verified |
EvidenceSnapshotResource.php |
create_first_snapshot |
Empty-state action | Environment | No | No | No explicit modal, action executes | Generates evidence snapshot/run | Execution resolves tenant and returns notification on missing context | Medium/high | repo-verified |
ReviewPackResource.php |
generate_pack / generate_first |
Header/empty-state action | Environment | No | No | Form modal | Generates review pack/run | Execution resolves current tenant and handles missing context | High latent | repo-verified |
EnvironmentReviewResource.php |
create_review |
Header/empty-state action | Environment | No | No | Form modal | Creates review and compose run | Execution checks access/capability after tenant resolution | High latent | repo-verified |
EnvironmentDiagnostics.php |
bootstrapOwner, mergeDuplicateMemberships |
Header | Environment | No | No | Confirmation modal | Tenant membership repair mutation | Handler resolves tenant or fails | High, sensitive admin repair | repo-verified |
EnvironmentDashboard.php |
submit_support_request, openSupportDiagnostics |
Page actions | Environment | No | No | Form/slide-over modal | Support request creation, diagnostics audit | Uses page resolver methods | Medium/high | repo-verified |
BaselineCompareLanding.php |
baseline compare action | Page/header action | Environment | No explicit UiEnforcement context |
Page state | Action/queue | Creates baseline compare run | Uses current environment/page state | Medium/high | repo-verified, derived |
CrossEnvironmentComparePage.php |
generatePromotionPreflight |
Header/page action | Workspace | Yes, workspace closure | No | No modal | Preflight generation | Workspace capability resolver | Low current, high future feature class | repo-verified |
BaselineCompareMatrix.php |
compareAssignedTenants |
Header/page action | Workspace | Yes, workspace closure | Profile/page | Confirmation modal | Starts environment-owned compare path | Workspace capability resolver | Low current | repo-verified |
CustomerReviewWorkspace.php |
create_next_review |
Page action | Environment via review record | Record closure | Yes | Confirmation modal | Review lifecycle mutation | Handler validates user/review | Medium | repo-verified |
PolicyResource/Pages/ViewPolicy.php |
capture_snapshot |
Detail header | Environment | No explicit closure, but action record/policy tenant available | Yes | Form confirmation | Creates capture snapshot run/job | Handler uses policy tenant and missing context notification | Medium | repo-verified |
PolicyResource/RelationManagers/VersionsRelationManager.php |
restore_to_intune |
Relation row action | Environment | No explicit closure | Yes | Form confirmation | Starts restore via service | Owner-scoped record resolution and tenant mismatch guard | Medium/high impact | repo-verified |
BackupScheduleResource.php |
runNow, retry |
Table row actions | Environment | No explicit closure | Yes via table record/action | No modal | Creates backup schedule run/job | Handler resolves tenant; readonly tests | Medium | repo-verified, test-covered |
RestoreRunResource.php |
create restore run | Header create action/wizard | Environment | No explicit closure | No before create | Wizard/modal-like | Restore flow and potential restore run | Wizard and write gate checks later | High impact | repo-verified |
RestoreRunResource.php |
restore/archive/forceDelete rows | Table row actions | Environment | Yes in table actions for row lifecycle actions | Yes | Confirmation modal | Mutates restore records | Scoped record resolver/audit | Medium | repo-verified |
ProviderConnectionResource.php |
check/inventory/compliance | Row/detail actions | Environment | No explicit closure | Provider connection record | No modal | Provider jobs/runs | Handler resolves tenant from record and gate service | Medium | repo-verified |
ProviderConnectionResource.php |
dedicated credential actions | Row/detail actions | Environment | No explicit closure | Provider connection record | Form/confirmation modal | Secret-bearing provider mutation | Handler resolves tenant from record | Medium/high impact | repo-verified |
ViewProviderConnection.php |
grant_admin_consent |
Detail header URL action | Environment | No explicit closure | Detail record/page tenant | URL action | External admin consent navigation | Capability UI gate only | Medium | repo-verified |
ManagedEnvironmentResource.php |
tenant sync/verify/archive/remove/restore | Record/detail/table actions | Environment record | Record/action scope | Yes | Confirmation on sensitive actions | Sync jobs and lifecycle mutation | Uses record tenant identity | Medium | repo-verified |
FindingResource.php |
triage/assign/resolve/exception actions | Row/bulk/detail actions | Environment | Mostly record/bulk selected records | Yes | Many confirmation modals | Finding lifecycle mutations | Capability checks via UiEnforcement |
Medium | repo-verified |
AlertDestinationResource.php, AlertRuleResource.php |
toggle/delete/create | Table/create actions | Workspace/environment depending model | No UiEnforcement hits in searched files |
Records | Confirmation for delete/toggle | Alert mutation, no OperationRun | Resource/policy-dependent | Medium/unknown | repo-verified, not verified |
StoredReportResource/Pages/ViewStoredReport.php |
open_current_report |
Detail header URL action | Record/report | No UiEnforcement |
Record | No | URL only | Scoped record resolver | Low | repo-verified |
Similar Patterns Found
| Pattern | Files | Evidence | Severity |
|---|---|---|---|
No-record environment header action depends on UiEnforcement fallback |
ListEntraGroups, ListEvidenceSnapshots, ReviewPackResource::generatePackAction, EnvironmentReviewResource::makeCreateReviewAction, EnvironmentDiagnostics |
Actions call UiEnforcement::forAction(...) without explicit context closure and later resolve tenant in execution |
High (repo-verified, derived) |
| Local resolver fixes repeated per surface | Inventory and policy sync now pass identical page resolver closures | Same shape in two separate implementations | High (repo-verified) |
| Modal open versus submit not centrally guarded | Inventory/policy have direct tests; evidence/review/pack/diagnostics do not show equivalent first-click matrix | Existing coverage is per-resource | Medium/high (repo-verified, not implemented) |
| Fail-closed missing context is silent | TenantAccessContext collapses missing tenant into non-member false state |
No distinct missing-context UI state in UiEnforcement |
Medium (repo-verified, derived) |
| Workspace enforcement has stricter explicit context pattern | WorkspaceUiEnforcement only resolves direct/closure workspace, no global fallback |
Better model for new context contract | Medium positive evidence (repo-verified) |
Blast Radius
| Area | Relevant Actions | Potentially Affected | Evidence | Missing Tests | Risk |
|---|---|---|---|---|---|
| Inventory | run_inventory_sync |
Fixed locally, recurrence risk remains | Explicit resolver now passed | Central all-actions guard absent | Medium |
| Policies | sync, capture_snapshot, row/bulk sync/export |
Header fixed locally; record actions lower risk | Header explicit resolver; detail uses record | Central first-click guard absent | Medium |
| Provider Connections | check, inventory sync, compliance, credential actions | Latent lower risk due records; high impact | Record-backed actions resolve provider tenant | Workspace-referer first-click modal not broad | Medium |
| Baseline Profiles | compare assigned tenants | Currently better guarded | WorkspaceUiEnforcement explicit workspace closure |
Future environment action guard absent | Low/medium |
| Baseline Compare | landing compare, cross-environment preflight | Future/present risk in page-state actions | Mixed explicit workspace and page-state environment context | No central mounted action matrix | Medium/high |
| Drift Findings | finding lifecycle actions | Mostly record/bulk action class, not trigger shape | FindingResource uses row/bulk actions |
Exhaustive first-click modal coverage not verified | Medium |
| Restore Runs | create/execute/rerun/archive/delete | High impact; not same current trigger everywhere | Create action no explicit context; row actions have scoped resolvers | Modal-first context matrix not central | High |
| Tenant Reviews | create, refresh, publish, archive, create-next | Latent risk for no-record create; record detail lower | Resource/page execution resolvers | First-click no-run tests not central | High |
| Evidence Snapshots | create, create-first, refresh, expire | Latent risk for create actions | Header/empty actions no explicit resolver | First-click no-run tests missing | High |
| Review Packs | generate, regenerate, expire | Latent risk for generate actions | generatePackAction no explicit resolver |
First-click/modal-no-run tests missing | High |
| Stored Reports | open current report | Not affected by current class | URL action, scoped record | N/A | Low |
| Support Diagnostics | open diagnostics, support request | Latent UX/audit risk | Page actions use UiEnforcement without explicit context |
First-click modal/audit timing not central | Medium/high |
| Support Requests | submit support request | Latent modal context risk | EnvironmentDashboard form action | Missing context consistency tests not verified | Medium/high |
| Alerts / Notification Routing | alert rule/destination table actions | Not clearly same UiEnforcement issue |
No UiEnforcement hits in searched files |
Authorization model not fully audited here | Medium/unknown |
| Permission Posture | evidence/review/dashboard surfaces consume posture | Future risk if actions added | No direct current action found in this audit slice | Future guard required | Medium |
| Entra Admin Roles / Groups | sync_groups, admin roles widgets |
sync_groups is latent current risk |
Header action no explicit context; readonly test uses Filament tenant | Workspace-referer first-click test missing | High |
| Customer Health | dashboards/system directory | Future risk | OperationRun-heavy metrics, not same action trigger in inspected files | Future guard required | Medium |
| Operational Controls | restore execute write gate | Future/current high-impact execution context | Restore flow has write gate; action context still mixed | Central guard absent | High |
| Cross-Tenant Compare | promotion/preflight | Future risk but current explicit workspace example is good | WorkspaceUiEnforcement closures |
OperationRun action context spec needed before promotion | Medium/high |
| Workspace Entitlements | review packs, support, billing-gated operations | Risk when disabled state differs render/submit | Review pack blocks use tenant/workspace entitlement decisions | Entitlement consistency matrix missing | High |
| Commercial/Billing State | entitlement-gated modals | Future risk | Review pack generation blocked by entitlement exception | Guard before new billing-gated actions | High |
Existing Latent Risks
| Risk | Evidence | Affected Surfaces | Severity | Recommended Fix |
|---|---|---|---|---|
sync_groups repeats the no-record header action shape without explicit context |
ListEntraGroups.php:51-80 |
Entra groups | Severity 2: High | Add to follow-up spec as representative third action for the new contract. |
| Evidence/review/pack generation actions can lose context on modal mount | ListEvidenceSnapshots.php:22-37, ReviewPackResource.php:382-400, EnvironmentReviewResource.php:405-423 |
Evidence snapshots, review packs, tenant reviews | Severity 2: High | Require explicit environment context on no-record generation actions. |
| Sensitive diagnostics repair actions rely on fallback context | EnvironmentDiagnostics.php:64-86 |
Environment diagnostics | Severity 2: High | Require explicit tenant resolver and missing-context copy. |
| Restore create action is high impact and context-sensitive | RestoreRunResource.php:282-289 |
Restore runs | Severity 2: High | Include restore create/wizard in guardrail tests before new restore UX work. |
| Missing context is not semantically distinct from non-membership | UiEnforcement.php:614-620 |
All UiEnforcement actions |
Severity 3: Medium | Model missing context as its own state in contract/DTO. |
TenantPilot audit classification:
| Finding | Classification | Severity | Delivery |
|---|---|---|---|
| No-record scoped actions depend on implicit Filament tenant fallback | Architectural Drift | Severity 2: High | Dedicated spec required |
| Missing context silently collapses into non-member UI state | Workflow Trust Gap | Severity 3: Medium | Follow-up refactor under same spec |
| No central first-click modal/no-run regression guard | Test Blind Spot | Severity 2: High | Dedicated spec required |
| OperationRun-starting action context is not statically enforceable | Workflow Trust Gap | Severity 2: High | Dedicated spec required |
Test Coverage
| Test | Scenario | First Click | Modal No-Run | Reauth / Execution Guard | Gap |
|---|---|---|---|---|---|
InventoryItemResourceTest.php:93-117 |
Inventory sync mounts from workspace-scoped Livewire referer | Yes | Yes, Queue::assertNothingPushed() |
No submit in that test | Does not cover every similar action |
PolicySyncStartSurfaceTest.php:93-116 |
Policy sync mounts from workspace-scoped Livewire referer | Yes | Yes, Queue::assertNothingPushed() |
No submit in that test | Does not cover every similar action |
ResolvesPanelTenantContextLivewireRefererTest.php:15-55 |
Resolver infers tenant from Livewire referer | N/A | N/A | N/A | Resolver only, not action matrix |
RunStartAuthorizationTest.php:18-132 |
Cross-tenant/readonly no run for inventory, Entra, backup schedule | No referer first-click | No modal matrix | Yes for representative run starts | Uses Filament::setTenant() |
| Provider operation tests found by search | Provider operation gates/runs | Not verified in this audit | Not verified | Partly covered by service/gate tests | First-click mounted action matrix absent |
| Evidence/review/pack tests | Operation artifact reconciliation exists | Not verified for first-click | Not verified | Some service/output tests exist | Needs action lifecycle tests |
Coverage conclusion: the local fix is test-covered. The class is not centrally guarded (derived).
Future Feature Risk
| Planned Feature | Why It Is Exposed | Required Guardrail Before Build |
|---|---|---|
| Customer Review Workspace v1 | Adds customer-safe workspace actions, review acknowledgement, package/evidence flows | Explicit workspace/environment action context and modal-no-run tests |
| Decision-Based Governance Inbox v1 | Likely adds modal decisions, approvals, exception lifecycle actions | No implicit Filament tenant; record/page context must be explicit |
| Localization v1 | Translated labels/tooltips can hide context/missing-state copy gaps | Missing-context state must have copy keys and tests |
| Cross-Tenant Compare and Promotion v1 | Multi-environment actions and promotion preflight can mix source/target contexts | Explicit DTO with source workspace, source environment, target environment |
| Commercial Entitlements and Billing-State Maturity | Disabled state can differ between render and submit if entitlement context is implicit | Entitlement decision must use same explicit action context |
| External Support Desk / PSA Handoff | Support handoff actions are modal-first and tenant-sensitive | First-click modal, audit-on-open, submit reauth tests |
| Private AI Execution Governance Foundation | AI execution approvals are high-trust, likely modal/OperationRun actions | Explicit operation scope, approval context, no-run-on-modal-open tests |
Most critical future features: Decision-Based Governance Inbox, Cross-Tenant Compare and Promotion, Commercial entitlement-gated actions, External Support Desk handoff, and AI execution approval actions (derived).
New Feature Action Contract
| Requirement | Required? | Current Support | Gap |
|---|---|---|---|
| Declare product scope: workspace/environment/tenant/system | Yes | Informal in many pages/specs | Not enforced for actions |
| Declare context source: record/page resolver/DTO | Yes | Some explicit closures, many implicit fallbacks | Not mandatory |
No implicit Filament::getTenant() for no-record scoped actions |
Yes | Not currently enforced | Main gap |
| Modal open must not execute or create OperationRun | Yes | Covered for inventory/policy only | No central guard |
| Submit must reauthorize | Yes | Many handlers re-resolve/guard; not uniform | Need standard test/helper |
| Execution receives explicit workspace/environment IDs | Yes | OperationRun often receives tenant; source varies | Need context contract |
| Missing context fails closed with understandable UX | Yes | Fails closed, but often silent | Need distinct state/copy |
| Tests: render, first-click, modal-no-run, submit, wrong context, readonly | Yes | Point coverage only | Need reusable test/helper or matrix |
Recommended Guardrail
Minimal (recommended immediate spec scope):
- Require explicit context closure/DTO for every no-record
UiEnforcement::forActionthat is workspace/environment-scoped. - Cover representative actions: inventory sync, policy sync, Entra group sync, evidence snapshot create, review pack generate, environment review create, restore create.
- Add reusable Pest helper/assertion for first-click modal mount and modal-no-run.
- Add a static audit command/test that lists no-record
UiEnforcement::forActionactions withOperationRun, queue dispatch, provider access, or modal forms and no explicit context.
Medium:
- Introduce
UiActionContextwith scope type, workspace, environment, source, and missing-context reason. - Make
UiEnforcementdistinguish missing context from non-member and insufficient capability. - Add
ResolvesActionContexttrait for page/header actions that wrapsResolvesPanelTenantContext, record scope, workspace scope, and Livewire referer.
Ideal:
- Make scoped action construction impossible without an explicit action context.
- Add CI guard for new OperationRun/provider/write actions without execution reauthorization and first-click lifecycle tests.
- Move action context and OperationRun start authorization into a shared contract that UI actions, jobs, and service gates can validate consistently.
Option evaluation:
| Option | Problem Solved | Problem Not Solved | Effort | Regression Risk | Recommendation |
|---|---|---|---|---|---|
| Keep local resolver | Fixes current action only | Recurrence on next action | Low | Low | Insufficient |
| Centralize resolver | Reduces duplication | Does not force use | Medium | Low/medium | Good |
Introduce UiActionContext DTO |
Models scope explicitly | Requires callsite migration | Medium | Medium | Best medium path |
Extend UiEnforcement contract |
Enforces guardrail near UX gate | Needs careful compatibility for record-backed actions | Medium | Medium | Recommended |
| Add trait for workspace/environment page actions | Improves ergonomics | Can become another optional helper | Low/medium | Low | Recommended with DTO |
| Add test helper only | Catches regressions | Does not improve design | Low | Low | Required but not enough |
| Static audit script/CI guard | Prevents new obvious violations | Needs false-positive tuning | Medium | Low | Recommended |
| Do nothing | Avoids churn | Recurrence likely | None | High product risk | Reject |
Anti-Regression Rule
Proposed hard rule (derived):
No new workspace-/environment-scoped
HeaderActionor no-record page action may callUiEnforcementor create anOperationRununless it receives an explicit action context resolved from the canonical page/record scope and has tests for render, first-click modal mount, modal-no-run, submit reauthorization, and missing-context fail-closed behavior.
Is this realistic now? Yes, if scoped to no-record header/page actions first. Applying it retroactively to every record-backed row/detail action would create too much churn and false positives (derived).
Filament v5 Blueprint Notes
- Livewire compliance: app is on Livewire 4.1.4 and Filament 5.2.1 (
repo-verifiedvia Laravel Boost). - Provider registration: this audit did not add or change Filament panels; Laravel 11+/12 provider registration location remains
apps/platform/bootstrap/providers.php(not changed). - Global search: this audit did not add resources or global search behavior. For any future globally searchable resource, the v5 rule remains: it needs Edit/View page or global search disabled (
not changed). - Destructive actions: this audit did not change actions. Existing sensitive examples inspected use
->requiresConfirmation()on destructive/confirmation actions such as policy restore, review archive, restore archive/delete, tenant lifecycle, and provider credential changes (repo-verified). - Assets: no assets added; no
filament:assetsdeployment impact (not implemented). - Testing plan: future spec should add Livewire tests for pages/actions and Filament action tests using
mountAction()for modal open andcallMountedAction()/callAction()for execution (derived, version-specific Filament guidance checked through Boost earlier in this workstream).
Final Answer
-
Where else does this currently occur?
- Confirmed latent current risk exists in no-record environment actions that still call
UiEnforcement::forActionwithout explicit context:sync_groups, evidence snapshot create, review pack generate, environment review create, environment diagnostics repair, restore create, and support dashboard actions (repo-verified,derived).
- Confirmed latent current risk exists in no-record environment actions that still call
-
Where could it occur next?
- Any new workspace/environment-scoped header/page action that opens a modal, starts an
OperationRun, dispatches a provider job, gates on readonly/entitlements, or uses page state instead of a record (derived).
- Any new workspace/environment-scoped header/page action that opens a modal, starts an
-
Is this a pattern or isolated bug?
- It is a systemic action context contract gap, with repeated implementation drift as a symptom (
derived, confidence high).
- It is a systemic action context contract gap, with repeated implementation drift as a symptom (
-
What should be mandatory for new features?
- Explicit action context, no implicit
Filament::getTenant()for no-record scoped actions, first-click modal tests, modal-no-run tests, submit reauthorization, missing-context fail-closed tests, and explicit OperationRun scope (derived).
- Explicit action context, no implicit
-
What is the smallest safe spec to prevent recurrence?
- Spec title: Livewire Action Context Contract for Workspace/Environment-Scoped Actions.
- Scope: add action context contract/trait/DTO, retrofit representative no-record actions, add reusable test helper/static guard, and document the anti-regression rule. No migrations or assets are required unless the chosen implementation adds persisted audit/telemetry for missing context (
derived).