TenantAtlas/specs/169-action-surface-v11/research.md
ahmido 37c6d0622c feat: implement spec 169 action surface contract v1.1 (#200)
## 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
2026-03-30 09:21:39 +00:00

5.3 KiB

Phase 0 Research: Action Surface Contract v1.1

Decision: Add a first-class ActionSurfaceType enum to ActionSurfaceDeclaration while keeping ActionSurfaceProfile

Rationale: ActionSurfaceProfile currently governs which slots are required, but it does not distinguish constitution-level interaction semantics. The repo needs to tell the difference between clickable-row CRUD or registry surfaces and legitimate explicit-inspect queue or audit surfaces. A first-class surfaceType field makes that distinction explicit without forcing slot requirements and behavioral rules into the same enum.

Alternatives considered:

  • Replace ActionSurfaceProfile entirely: rejected because slot requirements and constitution surface semantics are different concerns and existing declarations already rely on the profile model.
  • Derive surface type from metadata or inferred heuristics: rejected because the clarified spec requires a first-class declaration-level field and because inference would keep validator failures ambiguous.

Decision: Keep inspect-model compatibility rules close to the existing contract stack

Rationale: The narrowest implementation is one new enum plus validator logic and small helper methods where needed. That keeps the feature inside the current ActionSurfaceDeclaration / ActionSurfaceValidator architecture and avoids introducing a second action-governance registry or policy layer.

Alternatives considered:

  • Create a new ActionSurfaceTypeDefinition registry or service: rejected because the slice does not justify another framework layer.
  • Enforce the new rules in tests only: rejected because the main validator must become the primary contract gate, not just the rendered tests.

Decision: Extend primary discovery to declared system-panel table pages only

Rationale: The six enrolled system list pages already have declarations and targeted tests, but the main discovery pass still excludes them. The safest extension is to scan app/Filament/System/Pages/** narrowly for declared, table-backed pages so the main validator covers them without accidentally enrolling auth, dashboard, widget, or other deferred system surfaces.

Alternatives considered:

  • Hardcode an allowlist of six system classes: rejected because it would preserve a parallel discovery model and create more manual maintenance.
  • Broadly discover every page under app/Filament/System/Pages: rejected because it would sweep in deferred surfaces such as auth, runbooks, and break-glass tooling.

Decision: Keep current panel-scope metadata unchanged for this slice

Rationale: The feature needs system-panel discovery coverage, not a new panel taxonomy. The current codebase does not have an active consumer that requires a System panel scope enum, so adding one now would widen the slice without solving the immediate operator problem.

Alternatives considered:

  • Add ActionSurfacePanelScope::System: rejected because no current validation or runtime rule depends on it, and the spec does not require it.
  • Infer system scope through panel providers and extend all scope tests: rejected as unnecessary expansion beyond the current feature goal.

Decision: Split enforcement between fast validator tests and representative Livewire render tests

Rationale: Validator tests are the right seam for declaration-time rules such as required surfaceType, allowed affordance combinations, and required exception reasons. Livewire render tests are the right seam for business-visible behavior such as row click actually existing, explicit inspect actually preserving context, and More groups actually rendering helpers first, workflow actions next, and destructive actions last.

Alternatives considered:

  • Declaration-only guard coverage: rejected because the constitution explicitly rejects declaration-only conformance when rendered behavior drifts.
  • Browser-only coverage: rejected because it would be slower, broader, and less precise than the current Livewire table guard pattern already used in the repo.

Decision: Treat PrimaryLinkColumn as an exception path that requires an explicit reason

Rationale: The spec needs stronger control over linked-column inspect affordances, but the repo does not need a dedicated exception object or secondary taxonomy for that. Requiring an explicit reason on the declaration is enough to keep the exception visible and reviewable.

Alternatives considered:

  • Allow PrimaryLinkColumn anywhere the enum is present: rejected because the constitution requires a concrete reason when row click is not the correct primary inspect model.
  • Add a new exception class hierarchy: rejected because it would add structure without a separate current-release problem to solve.

Decision: Keep the two reference docs in lockstep with validator behavior

Rationale: The repo already has two developer-facing references for this domain: docs/ui/action-surface-contract.md and docs/product/standards/filament-actions-ux.md. The feature should update both together so implementation guidance, spec language, and guard behavior stay aligned.

Alternatives considered:

  • Update only the constitution: rejected because day-to-day implementation guidance lives in the shorter reference docs.
  • Update only one doc and let the other lag: rejected because that would recreate the ambiguity the feature is meant to remove.