## Summary - implement the Action Surface Contract v1.1 runtime changes for Spec 169 - add the new explicit ActionSurfaceType contract, validator/discovery updates, and enrolled surface declarations - update Filament action-surface documentation, focused guard tests, and spec artifacts for the completed feature ## Included - clickable-row vs explicit-inspect enforcement across monitoring, reporting, CRUD, and system reference surfaces - helper-first, workflow-next, destructive-last overflow ordering checks - system panel list discovery in the primary action-surface validator - Spec 169 artifacts: spec, plan, tasks, research, data model, quickstart, and logical contract ## Verification - focused Pest verification pack completed for: - tests/Feature/Guards/ActionSurfaceValidatorTest.php - tests/Feature/Guards/ActionSurfaceContractTest.php - tests/Feature/Rbac/TenantActionSurfaceConsistencyTest.php - integrated browser smoke test completed for admin-side reference surfaces: - /admin/operations - /admin/audit-log - /admin/finding-exceptions/queue - /admin/reviews - /admin/tenants ## Notes - system panel browser smoke coverage could not be exercised in the same session because /system routes require platform authentication in the integrated browser - Livewire target remains v4-compliant and no provider registration or asset strategy changes are introduced by this PR Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de> Reviewed-on: #200
3.8 KiB
Action surface contract
This project enforces a small “action surface contract” for Filament Resources / Pages / RelationManagers to keep table UIs consistent, quiet, and safe.
Inspect affordance (required)
Any list-style surface that exposes records must provide an inspect affordance so an admin can open a record.
Surface-type decision tree
The inspect model is driven by the declaration surfaceType:
- CRUD / List-first Resource
Use one-click open by default, normally
recordUrl().PrimaryLinkColumnis allowed only as an explicit exception with a concrete reason. - Read-only Registry / Report
Use one-click open by default, normally
recordUrl(). This includes scan-first reporting surfaces such as Monitoring Operations, Review Register, Evidence Overview, and read-only registry resources. - Queue / Review
Use explicit inspect (
Inspectrow action or equivalent same-page selected detail). Do not make the full row clickable. - History / Audit
Use explicit inspect (
Inspectrow action or equivalent same-page selected detail). Do not make the full row clickable. - Config-lite
Edit-as-inspect is allowed, but it still uses one obvious open path and must not add a competing
Viewaction.
Accepted implementations
- Clickable rows (preferred): set
recordUrl()for the table. - Inspect action: a row action used only on queue / review or history / audit surfaces where context must stay on the same page.
- Primary link column: a column that is clearly the primary affordance to open the record, with an explicit
PrimaryLinkColumnreason in the declaration.
Rule: no lone “View” button
Avoid rendering a table that only has a single inspect-style row action on a clickable-row surface. This creates visual noise and adds an unnecessary Actions column.
Preferred approach:
- Make the row clickable via
recordUrl()and setactions([])so no Actions column is rendered.
PrimaryLinkColumn exception rule
Use PrimaryLinkColumn only when full-row click is the wrong interaction model for that specific surface.
- The declaration must use a clickable surface type (
CrudListFirstResource,ReadOnlyRegistryReport, orConfigLite). - The declaration must include a non-empty
primaryLinkColumnReason. - Queue / review and history / audit surfaces may not use
PrimaryLinkColumnas a shortcut around explicit inspect.
Reporting / evidence register rule
Review and evidence registers are governed as ReadOnlyRegistryReport surfaces.
ReviewRegisterandEvidenceOverviewkeep clickable-row inspection as the primary open path.- Do not add a duplicate
View reviewor equivalent open action beside the row click. - Safe non-inspect shortcuts may remain when they are clearly secondary.
More-menu ordering
Governed ActionGroup and BulkActionGroup menus use one stable order:
- Navigation or inspect helpers first
- Non-destructive workflow or lifecycle actions next
- Destructive actions last
Examples:
Policies: export before sync, sync before ignore/deleteBackup schedules: run/retry before archive or force deleteTenants: related onboarding and safe navigation shortcuts before sync or verification, with archive/force delete trailing
Placeholder groups are forbidden
ActionGroup and BulkActionGroup exist to hold real secondary actions, not to reserve layout space.
- Do not render an empty
Moremenu after visibility, record-state, or RBAC filtering removes every effective action. - On clickable-row surfaces with only one safe shortcut, that shortcut may still live under
Morewhen it preserves a cleaner scan-first list.
RBAC / safety
- If the current user cannot inspect a record,
recordUrl()must returnnullfor that record. - UI visibility is not authorization; always enforce permissions at the policy / resource level.