# 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`.
