TenantAtlas/specs/082-action-surface-contract/spec.md
ahmido a770b32e87 feat: action-surface contract inspect affordance + clickable rows (#100)
Implements Spec 082 updates to the Filament Action Surface Contract:

- New required list/table slot: InspectAffordance (clickable row via recordUrl preferred; also supports View action or primary link column)
- Retrofit view-only tables to remove lone View row action buttons and use clickable rows
- Update validator + guard tests, add golden regression assertions
- Add docs: docs/ui/action-surface-contract.md

Tests (local via Sail):
- vendor/bin/sail artisan test --compact tests/Feature/Guards/ActionSurfaceContractTest.php
- vendor/bin/sail artisan test --compact tests/Feature/Guards/ActionSurfaceValidatorTest.php
- vendor/bin/sail artisan test --compact tests/Feature/Rbac/ActionSurfaceRbacSemanticsTest.php
- vendor/bin/sail artisan test --compact tests/Feature/Filament/EntraGroupSyncRunResourceTest.php

Notes:
- Filament v5 / Livewire v4 compatible.
- No destructive-action behavior changed in this PR.

Co-authored-by: Ahmed Darrazi <ahmeddarrazi@MacBookPro.fritz.box>
Reviewed-on: #100
2026-02-08 20:31:36 +00:00

192 lines
16 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Feature Specification: Action Surface Contract + CI Enforcement
**Feature Branch**: `082-action-surface-contract`
**Created**: 2026-02-08
**Status**: Draft
**Input**: User description: "Spec 082 — Action Surface Contract + CI Enforcement"
## Clarifications
### Session 2026-02-08
- Q: Group label standard: “More” vs “Actions” → A: Use “More” as the standard label for secondary row actions and bulk action groups.
- Q: Standard safe bulk action for ReadOnly/RunLog: allow Export everywhere? → A: Yes — Export is the default safe bulk action for ReadOnly and RunLog surfaces when data exists; otherwise an explicit exemption with reason is required.
- Q: Exemption policy: time-boxed vs reason-only? → A: Exemptions must include a non-empty reason; an optional tracking link/reference may be included; no deadline is required.
- Q: Audit policy: destructive-only vs all mutations? → A: Audit events are required for all mutations and for operation-start triggers (run/sync/verify/dispatch).
- Q: Validator scope/discovery: which components are in-scope for CI enforcement? → A: Tenant + Admin panels; validate Resources, Pages, and embedded relationship sub-lists; exclude Widgets.
## User Scenarios & Testing *(mandatory)*
### User Story 1 - Prevent incomplete UI action surfaces (Priority: P1)
As a developer, when I add or modify an admin-console UI component (list, detail, embedded relationship sub-list), I must explicitly declare its expected action surface (or explicitly exempt parts with a reason), so that automated checks prevent regressions before merge.
**Why this priority**: This is the regression gate. Without it, incomplete action surfaces ship repeatedly and create inconsistent UX and RBAC drift.
**Independent Test**: Add a minimal new UI component without an action surface declaration and confirm the automated gate fails with a precise message; then add a declaration (or exemption) and confirm the gate passes.
**Acceptance Scenarios**:
1. **Given** a new or modified in-scope UI component without an action surface declaration, **When** validation runs, **Then** it fails with a message naming the component and the missing required slots.
2. **Given** a component with a complete declaration (or exemptions with reasons), **When** validation runs, **Then** it passes.
3. **Given** a component uses an exemption, **When** validation runs, **Then** it fails if the exemption reason is missing or empty.
---
### User Story 2 - Consistent, enterprise-grade actions and empty-state guidance (Priority: P2)
As an admin-console user, I see predictable and consistent actions across lists and details: primary actions are obvious, secondary actions are grouped, bulk actions exist where appropriate, and empty states provide an actionable next step.
**Why this priority**: Reduces training cost and operational mistakes; improves speed and confidence in admin workflows.
**Independent Test**: Pick one CRUD-style list and one run-log style list; verify each has the expected action areas (header/row/bulk/empty-state; detail header actions) and that ordering/grouping conventions are consistent.
**Acceptance Scenarios**:
1. **Given** a list view with records, **When** a user inspects available actions, **Then** they see at most two visible row actions and all secondary actions are grouped under a consistent label.
2. **Given** a list view with zero records, **When** the page is shown, **Then** it includes at least one CTA that helps resolve the empty state.
3. **Given** a list view that supports selection, **When** the user selects one or more records, **Then** at least one bulk action is available or the UI explicitly documents why bulk is intentionally not offered.
---
### User Story 3 - Predictable RBAC behavior for members vs non-members (Priority: P3)
As a security reviewer, I need consistent authorization semantics across all in-scope actions:
- A non-member must not discover tenant/workspace surfaces (deny-as-not-found).
- A member without the required capability must be prevented from completing the action (deny-as-forbidden), and the UI must communicate why.
**Why this priority**: Prevents information disclosure and capability drift; keeps the product aligned with least privilege.
**Independent Test**: Use a representative surface and verify (a) non-member receives not-found, and (b) member without capability receives forbidden and sees disabled UI with a helpful explanation.
**Acceptance Scenarios**:
1. **Given** a user who is not a member of the relevant tenant/workspace scope, **When** they attempt to access a related page or record, **Then** the response is not-found.
2. **Given** a user who is a member but lacks capability for a specific action, **When** they attempt to execute the action, **Then** the response is forbidden.
3. **Given** a member without capability viewing the UI, **When** they inspect the action surface, **Then** disallowed actions are visible-but-disabled with a clear explanation (not silently missing).
### Edge Cases
- A surface is intentionally read-only and has no meaningful bulk actions (must be explicitly exempted with a reason).
- A user loses membership/capability between page load and action execution (server-side enforcement must still apply).
- Bulk mutations above a risk threshold must require stronger confirmation (typed confirmation) to reduce accidental damage.
- Empty states that cannot be resolved from within the current surface (CTA should route users to the correct “next step” surface).
- Global search and cross-scope links must not reveal existence of tenant/workspace data to non-members.
## Requirements *(mandatory)*
**Constitution alignment (required):** This feature is primarily a UI governance and authorization-consistency feature. It MUST not introduce new external calls or long-running operations as part of the enforcement gate.
**Constitution alignment (RBAC-UX):** This feature formalizes 404 vs 403 semantics and requires both UI signaling and server-side enforcement for in-scope actions.
**Constitution alignment (Action Surfaces):** This feature defines and enforces an Action Surface Contract, and introduces a mandatory UI Action Matrix for future admin UI-related specs.
### Functional Requirements
- **FR-001 (Contract profiles)**: The system MUST define a small set of action-surface profiles (e.g., CRUD, ReadOnly, RunLog, embedded relationship sub-lists) that describe minimum expected action areas for list and detail surfaces.
- **FR-002 (Required action slots)**: For each profile, the system MUST define required action slots for:
- list/table header actions,
- row actions,
- bulk actions,
- empty-state CTA(s),
- detail header actions,
- create/edit save/cancel conventions (where applicable).
- **FR-003 (Exemptions)**: The system MUST allow explicit exemptions for specific slots, and each exemption MUST include:
- a non-empty reason, and
- an optional tracking reference (issue/PR link or identifier).
- **FR-004 (Mandatory declaration)**: For every in-scope admin-console UI component that is new or modified, a declaration MUST exist that:
- identifies the profile,
- declares which slots are satisfied,
- lists any exemptions with reasons.
- **FR-005 (Automated gate)**: The system MUST automatically validate contract compliance for all in-scope UI components in the code review pipeline, and MUST fail with actionable messages when requirements are not met.
- **FR-006 (Consistency rules)**: The system MUST enforce the following UX conventions through (a) declaration validation for all in-scope surfaces and (b) runtime tests on representative surfaces:
- no more than two visible row actions (typically View/Edit),
- secondary actions grouped under the standard label “More”,
- bulk actions grouped under the standard label “More”,
- destructive actions are never primary.
- **FR-006a (Default safe bulk action)**: For ReadOnly and RunLog profiles, the default bulk action MUST be “Export” when the surface contains data; if “Export” is not offered, an explicit exemption with a reason is required.
- **FR-007 (Safety + confirmation)**: Any mutating or destructive action MUST require confirmation. For this feature, compliance is enforced via declaration checks plus representative runtime tests for touched surfaces.
- Mutation classes for FR-007:
- data mutation: create/update/delete/archive/restore/attach/detach,
- operation-start trigger: enqueue/dispatch/run/sync/verify/retry actions,
- access/safety mutation: role or membership changes, credential or permission updates, safety gate toggles.
- High-risk bulk actions MUST require typed confirmation when any of the following is true:
- the action is destructive/irreversible, or
- the action affects more than 25 records, or
- the action changes tenant/workspace access, credentials, or operational safety gates.
- **FR-008 (Auditability)**: All mutations and operation-start triggers MUST use existing audit sinks and MUST write an audit event that records at minimum: actor, scope (tenant/workspace), action type, target(s), timestamp, and outcome.
- Audit sinks for FR-008:
- tenant-scoped actions: `App\Services\Intune\AuditLogger` (writes `audit_logs`),
- workspace/platform-scoped actions: `App\Services\Audit\WorkspaceAuditLogger` (writes `audit_logs`).
- Operation-start triggers MAY satisfy FR-008 via canonical `OperationRun` creation when the action is run-oriented and existing architecture uses `OperationRun` as the observability record.
- For this feature, FR-008 is enforced as: no regressions on touched representative surfaces and no new ad-hoc audit sink introduced.
- **FR-009 (RBAC UI gating standard)**: UI gating MUST follow the projects standard semantics:
- non-member → deny-as-not-found behavior,
- member without capability → deny-as-forbidden behavior,
- UI communicates “forbidden” as visible-but-disabled actions with a tooltip/explanation.
- **FR-010 (Server-side authorization)**: Server-side authorization MUST be enforced for every action execution (not just UI visibility) and MUST align with FR-009 semantics (see constitution RBAC-UX-001..004).
- **FR-011 (Canonical run links)**: Any “View run” deep link MUST use the canonical tenantless operations path as the primary link; tenant-scoped convenience links may exist but must not be primary.
- **FR-012 (Scope)**: The contract MUST cover both the tenant-scoped admin console and the platform/admin console surfaces for resources, pages, and embedded relationship sub-lists.
- **FR-012a (Explicit CI scope)**: The automated gate MUST validate Resources, Pages, and embedded relationship sub-lists in both tenant-scoped and platform/admin consoles, and MUST exclude Widgets from enforcement.
### Non-Goals
- This feature does not redesign visual layout or page styling.
- This feature does not change domain workflows (sync/restore/verify), except to ensure action surfaces are complete or explicitly exempted.
- This feature does not change panel architecture or audience boundaries.
### Assumptions
- A safe, broadly applicable bulk action exists for read-only/run-log surfaces (“Export”) where data volume and permissions allow.
- Some existing surfaces will require temporary exemptions to keep the repository in a releasable state while retrofit work is completed.
### Enforcement Boundaries
- The CI validator for this feature is declaration-driven and deterministic (filesystem + reflection only).
- Runtime behavior is verified via representative tests for each profile and each critical rule family (grouping, confirmation, RBAC semantics, canonical run links).
- Deep runtime introspection of every Filament action in CI is explicitly out of scope for this iteration.
## UI Action Matrix *(mandatory when admin UI components are changed)*
_Note: This matrix applies whenever admin UI components (resources, pages, or embedded relationship sub-lists) are added or changed._
This feature introduces a contract that applies broadly. The matrix below documents the minimum action surface expectations by profile.
| Surface | Location | Header Actions | Row Actions (max 2 visible) | Bulk Actions (grouped) | Empty-State CTA(s) | View Header Actions | Create/Edit Save+Cancel | Audit log? | Notes / Exemptions |
|---|---|---|---|---|---|---|---|---|---|
| CRUD list + detail | Tenant panel + Admin panel | Primary: Create (if allowed). Optional: domain CTA (e.g., Sync/Run) | Primary: View, Edit (if allowed). Secondary: grouped under “More” | At least 1 bulk action (e.g., Archive/Restore/Export) | Primary CTA: Create or domain CTA | Primary: Edit (if allowed). Secondary grouped under “More” | Consistent Save + Cancel; no destructive primary | Yes (for mutations + operation-start) | Exempt bulk only with explicit reason (e.g., no safe bulk operation exists) |
| ReadOnly list + detail | Tenant panel + Admin panel | At least 1 CTA that provides value (e.g., Export/Refresh) | Primary: View. Secondary grouped under “More” | Bulk: Export (default) | CTA that resolves empty state (e.g., Refresh now) | “More” group for secondary actions | N/A | Maybe (typically for operation-start actions) | Exempt Export only with explicit reason (e.g., legal/security constraints) |
| RunLog list + detail | Tenant panel + Admin panel | Optional CTA routing to Operations hub | Primary: View | Bulk: Export (default) or Prune if retention exists; otherwise exempt with reason | Optional CTA routing to Operations hub | “View run” canonical link; optional Export | N/A | Maybe (for prune/retention changes) | “View run” must be canonical tenantless as primary deep link |
| Embedded relationship sub-list | Under parent detail | Header: Add/Attach/Create/Refresh depending on context | Primary: View and one context action (e.g., Detach/Remove). Secondary grouped under “More” | At least 1 bulk action or exemption with reason | CTA: next step (Add/Attach/Refresh) | N/A | N/A | Yes (for mutations) | Bulk may be exempted if relationship action is inherently single-record |
### Key Entities *(include if feature involves data)*
- **Action Surface Declaration**: A structured, human-reviewed statement of what actions a surface offers (and why some may be exempt).
- **Action Surface Profile**: A category of surfaces with shared minimum action expectations (CRUD, ReadOnly, RunLog, embedded relationship sub-list).
- **Action Slot**: A required location/type of action (header, row, bulk, empty-state, detail header, save/cancel).
- **Exemption**: A documented, explicit deviation from the contract for a specific slot.
- **Capability**: The permission concept used to decide whether an action is allowed.
- **Audit Event**: A record that an action was attempted/executed and its outcome.
### Dependencies
- A single, canonical capability registry exists and is used consistently for authorization decisions.
- Standard UI gating helpers exist for tenant/workspace scopes and are the only allowed approach for UI enable/disable behavior.
- A centralized audit-event sink exists to record user-triggered mutations and operation starts.
### Acceptance Notes (verification approach)
- Contract compliance is verified via an automated validation gate that enumerates in-scope UI components and checks required slots/exemptions.
- Authorization correctness is verified via representative automated tests that prove not-found vs forbidden behavior across at least one surface per profile.
- Consistency/confirmation/canonical-link behavior is verified on representative runtime surfaces; declaration checks remain the repo-wide completeness gate.
## Success Criteria *(mandatory)*
### Measurable Outcomes
- **SC-001 (Coverage)**: 100% of in-scope UI components are contract-compliant or have explicit exemptions with reasons.
- **SC-002 (Regression prevention)**: Any new/modified in-scope UI component without a declaration fails validation in the review pipeline.
- **SC-003 (Action completeness)**: For all CRUD and ReadOnly list surfaces, the selection state always presents at least one bulk action or an explicit exemption.
- **SC-004 (Authorization correctness)**: At least one representative surface per profile has automated tests proving not-found vs forbidden behavior (non-member vs member without capability).
- **SC-005 (Developer experience)**: Validation failures provide actionable messages that identify the component and missing slot(s), enabling a fix without additional investigation.