# Implementation Plan: Action Surface Contract Compliance & RBAC Hardening (Spec 090) **Branch**: `090-action-surface-contract-compliance` | **Date**: 2026-02-12 | **Spec**: `specs/090-action-surface-contract-compliance/spec.md` **Input**: Feature specification from `specs/090-action-surface-contract-compliance/spec.md` ## Summary Bring a focused set of Filament v5 action surfaces into compliance with the repo’s **Filament UI Action Surface Contract**, and harden RBAC semantics using the existing enforcement helpers. This work primarily: - Shrinks `ActionSurfaceExemptions::baseline()` for the in-scope components. - Adds `actionSurfaceDeclaration()` to those components (or explicit exemptions with reasons). - Standardizes action ordering/grouping (“View → Edit → More → Destructive last”; max 2 visible row actions). - Ensures **successful** side-effect actions create `AuditLog` entries. ## Technical Context **Language/Version**: PHP 8.4.15 **Framework**: Laravel 12 **Admin UI**: Filament v5 + Livewire v4.0+ **Storage**: PostgreSQL (Sail) **Testing**: Pest v4 (`vendor/bin/sail artisan test`) **Target Platform**: Docker/Sail local; Dokploy staging/prod **Project Type**: Laravel monolith **Performance Goals**: Keep “start” surfaces enqueue-only; preserve guard test runtime **Constraints**: - No new dependencies - BackupSchedule retention is explicitly deferred - Use existing RBAC + audit primitives; no new tables **Scale/Scope**: Multi-tenant admin UI; targeted subset of Resources/Pages ## Constitution Check *GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.* - ✅ Filament v5 targets Livewire v4.0+ (no Livewire v3) - ✅ Authorization is enforced server-side (UI visibility is not security) - ✅ RBAC-UX semantics: non-member → 404; member missing capability → 403 (and disabled + tooltip in UI) - ✅ Destructive actions execute via `->action(...)` and include `->requiresConfirmation()` - ✅ Action Surface Contract enforced via guard tests; exemptions must be explicit and minimized - ✅ Audit logs for successful side-effect actions (per spec clarification) ## Project Structure ### Documentation (this feature) ```text specs/090-action-surface-contract-compliance/ ├── spec.md ├── plan.md ├── research.md ├── data-model.md ├── quickstart.md └── contracts/ └── action-surface-contract-v1.openapi.yaml ``` ### Source Code (relevant to this spec) ```text app/ ├── Filament/Resources/ │ ├── ProviderConnectionResource.php │ ├── BackupScheduleResource.php │ ├── FindingResource.php │ ├── TenantResource.php │ ├── PolicyResource/Pages/ListPolicies.php │ └── PolicyResource/Pages/ViewPolicy.php ├── Filament/Resources/Workspaces/WorkspaceResource.php ├── Support/Rbac/UiEnforcement.php ├── Support/Rbac/WorkspaceUiEnforcement.php └── Support/Ui/ActionSurface/ ├── ActionSurfaceDeclaration.php └── ActionSurfaceExemptions.php tests/Feature/Guards/ ├── ActionSurfaceContractTest.php └── ActionSurfaceValidatorTest.php ``` **Structure Decision**: Laravel monolith; modifications are limited to existing Filament resources/pages + guard tests. ## Phase 0 — Outline & Research (DOCS COMPLETE) Completed in `specs/090-action-surface-contract-compliance/research.md`. Key findings: - Action Surface Contract enforcement already exists via guard tests and `ActionSurfaceDeclaration`. - Many in-scope resources are currently excluded by `ActionSurfaceExemptions::baseline()`. - RBAC enforcement helpers exist and already implement 404/403 semantics: `UiEnforcement`, `WorkspaceUiEnforcement`. - Audit logging services exist and sanitize metadata. ## Phase 1 — Design & Contracts (DOCS COMPLETE) Outputs: - `data-model.md`: No schema changes required. - `contracts/action-surface-contract-v1.openapi.yaml`: Internal contract artifact (no new HTTP endpoints). - `quickstart.md`: Local verification commands and checklist. Design choices: - “View” requirement is interpreted as an **inspection affordance** (clickable row, primary linked column, or explicit View action) consistent with the constitution. - Audit logs are required for successful dispatch/execution surfaces only. - Directory groups (Entra groups) and inventory items already follow the inspection-affordance pattern (clickable rows) and have explicit declarations; they are verification-only in this spec to avoid expanding scope. Note: Phases 0–1 are complete for documentation and design artifacts. Implementation and verification work begins in Phase 2 and is tracked in `specs/090-action-surface-contract-compliance/tasks.md`. ## Phase 2 — Implementation Plan ### Step 1 — Make contract guard the primary gate Target guard tests: - `tests/Feature/Guards/ActionSurfaceContractTest.php` - `tests/Feature/Guards/ActionSurfaceValidatorTest.php` Workflow: 1) Add `actionSurfaceDeclaration()` to one component. 2) Remove its baseline exemption. 3) Run guard tests. 4) Repeat. ### Step 2 — Shrink baseline exemptions File: `app/Support/Ui/ActionSurface/ActionSurfaceExemptions.php` Remove baseline exemptions for components once they have declarations: - `ProviderConnectionResource` - `BackupScheduleResource` - `FindingResource` - `TenantResource` - `Workspaces/WorkspaceResource` ### Step 3 — Add action surface declarations (per component) Add `actionSurfaceDeclaration()` and ensure required slots are satisfied. Expected profile direction (final selection should match existing profiles in the repo): - CRUD list + inspect + edit: use a CRUD-capable profile; keep row actions to 2 max. - List-only read-only resources should use a read-only profile (already in repo patterns). ### Step 4 — Fix known hotspots #### (P1) Policy: Capture snapshot dispatch File: `app/Filament/Resources/PolicyResource/Pages/ViewPolicy.php` - Wrap action with `UiEnforcement::forAction(...)` using the canonical capability. - Ensure the server-side `before(...)` aborts with 404/403 appropriately. - Add audit logging on successful dispatch via `App\Services\Intune\AuditLogger`. #### (P1) Policy: Sync action confirmation semantics File: `app/Filament/Resources/PolicyResource/Pages/ListPolicies.php` - Remove misuse of `destructive()` for non-destructive sync/run actions. - Keep confirmation if required, but with non-destructive semantics. #### (P2) Findings: action ordering + grouping File: `app/Filament/Resources/FindingResource.php` - Ensure `View` is first. - Move secondary actions (like acknowledge) into `More`. - Ensure bulk actions are grouped and capability gated via `UiEnforcement`. #### (P2/P3) Backup schedules: empty-state CTA + grouping File: `app/Filament/Resources/BackupScheduleResource.php` - Ensure empty-state action exists (create) and is capability gated. - Ensure “run now / retry” actions are gated and audited (audit already exists for manual dispatch; verify consistency). #### (P1/P2) Tenant + Workspaces: RBAC semantics + bulk grouping Files: - `app/Filament/Resources/TenantResource.php` - `app/Filament/Resources/Workspaces/WorkspaceResource.php` Ensure: - Destructive actions are last and confirmed. - Bulk actions are grouped under a consistent `More` group. - Workspaces use `WorkspaceUiEnforcement` + `WorkspaceAuditLogger` for workspace-scoped side effects. ### Step 5 — Testing plan Minimum programmatic verification: - Guard tests stay green as exemptions shrink. - Add or extend focused Pest feature tests for: - 404 vs 403 RBAC semantics for at least one representative mutation action. - Successful audit log creation for at least one enqueue/dispatch surface. ### Step 6 — Formatting Run formatter before final handoff: - `vendor/bin/sail bin pint --dirty` ## Deploy / Ops Notes - No migrations expected. - No new Filament assets expected; if any assets are registered, ensure deploy runs `php artisan filament:assets`.