# Research — Spec 090 (Action Surface Contract Compliance & RBAC Hardening) ## Decisions ### Decision 1 — Use the repo’s existing Action Surface Contract guard - **Chosen**: Retrofit in-scope Filament Resources/Pages/RelationManagers by adding `actionSurfaceDeclaration()` and removing baseline exemptions in `App\Support\Ui\ActionSurface\ActionSurfaceExemptions::baseline()`. - **Rationale**: The repository already enforces the constitution’s Filament Action Surface Contract through `tests/Feature/Guards/ActionSurfaceContractTest.php`. Aligning to that system avoids bespoke checks and keeps CI gates meaningful. - **Alternatives considered**: - Ad-hoc “View button exists” UI assertions per resource → rejected (doesn’t cover grouping/bulk/empty-state semantics; duplicates existing guard). ### Decision 2 — Interpret “View affordance” as an *inspection affordance* (not always a literal View button) - **Chosen**: Treat the spec’s “visible View” requirement as “a visible inspection affordance”, matching the constitution’s accepted forms: - clickable rows via `recordUrl()` (preferred when View would be the only row action) - a primary linked column - a dedicated `View` row action (when there are other row actions like Edit) - **Rationale**: The constitution explicitly forbids a lone View row button and prefers clickable rows in that case. The repo already uses this approach for read-only, list-only surfaces (e.g., inventory items). - **Alternatives considered**: - Add `ViewAction` everywhere → rejected (would create “lone View” buttons on read-only lists, conflicting with constitution and existing tests). ### Decision 3 — Reuse central RBAC UI enforcement helpers for all Filament actions - **Chosen**: For tenant-scoped actions, use `App\Support\Rbac\UiEnforcement`. For workspace-scoped actions, use `App\Support\Rbac\WorkspaceUiEnforcement`. - **Rationale**: These helpers implement the constitution’s RBAC-UX semantics (non-member 404, member missing capability 403, disabled + tooltip in UI) and validate capabilities against the canonical registry. - **Alternatives considered**: - Inline `->authorize(...)`, ad-hoc `->visible(...)`, or `->disabled(...)` checks → rejected (too easy to drift; violates “central helpers” expectation). ### Decision 4 — Audit successful side-effect actions via existing audit log services - **Chosen**: - Tenant-scoped audit entries: `App\Services\Intune\AuditLogger` (writes to `audit_logs` with `tenant_id`). - Workspace-scoped audit entries: `App\Services\Audit\WorkspaceAuditLogger` (writes to `audit_logs` with `workspace_id`, `tenant_id = null`). - **Rationale**: The repo already uses these loggers and sanitizes metadata via `AuditContextSanitizer`. - **Alternatives considered**: - Logging denied/cancelled attempts → rejected (explicitly out of scope for Spec 090). ## Current Repo State (Key Findings) ### Action Surface Contract infrastructure exists - Guard tests: - `tests/Feature/Guards/ActionSurfaceContractTest.php` - `tests/Feature/Guards/ActionSurfaceValidatorTest.php` - Runtime support: - `app/Support/Ui/ActionSurface/*` - Many “legacy” resources are exempt via: - `app/Support/Ui/ActionSurface/ActionSurfaceExemptions.php` ### In-scope components are currently exempt from the contract guard The constitution guard currently exempts (among others): - `App\Filament\Resources\ProviderConnectionResource` - `App\Filament\Resources\BackupScheduleResource` - `App\Filament\Resources\FindingResource` - `App\Filament\Resources\TenantResource` - `App\Filament\Resources\Workspaces\WorkspaceResource` Exact baseline exemptions to remove in Spec 090 implementation: - `App\Filament\Resources\ProviderConnectionResource` - `App\Filament\Resources\BackupScheduleResource` - `App\Filament\Resources\FindingResource` - `App\Filament\Resources\TenantResource` - `App\Filament\Resources\Workspaces\WorkspaceResource` Spec 090’s intent is to shrink that exemption list by bringing these in-scope surfaces into compliance. ### RBAC helpers are present and already used on some surfaces - `UiEnforcement` provides: - non-member hidden + server abort(404) - member missing capability disabled + tooltip + server abort(403) - destructive confirmation via `->requiresConfirmation()` (generic destructive copy) ### Notable hotspot: Policy “Capture snapshot” - Implemented in `app/Filament/Resources/PolicyResource/Pages/ViewPolicy.php`. - Currently: - has confirmation + correct domain copy - queues an `OperationRun` - **needs** capability-first gating and audit logging for successful dispatch. ### Notable semantics mismatch: Policy list “Sync from Intune” - Implemented in `app/Filament/Resources/PolicyResource/Pages/ListPolicies.php`. - The action uses `UiEnforcement::destructive()` to obtain confirmation, which applies generic destructive modal copy. - Per Spec 090 + constitution: sync/run actions should not be presented as destructive. ## Open Questions Resolved By Repo Evidence - **How is action-surface compliance enforced?** → Via `ActionSurfaceValidator` + `ActionSurfaceContractTest`. - **How to log audits?** → Use `AuditLogger` / `WorkspaceAuditLogger` with sanitized metadata. - **How to apply RBAC consistently?** → Wrap Filament actions with `UiEnforcement` / `WorkspaceUiEnforcement`.