## 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
22 KiB
Implementation Plan: Action Surface Contract v1.1
Branch: 169-action-surface-v11 | Date: 2026-03-30 | Spec: /Users/ahmeddarrazi/Documents/projects/TenantAtlas/specs/169-action-surface-v11/spec.md
Input: Feature specification from /Users/ahmeddarrazi/Documents/projects/TenantAtlas/specs/169-action-surface-v11/spec.md
Note: This template is filled in by the /speckit.plan command. See .specify/scripts/ for helper scripts.
Summary
Strengthen the existing action-surface contract so rendered behavior, not declaration presence alone, becomes the governing truth. The implementation adds one first-class constitution-aligned surface_type enum to ActionSurfaceDeclaration while keeping ActionSurfaceProfile as the slot-requirement model, extends primary discovery to the enrolled system-panel table pages under app/Filament/System/Pages, codifies inspect-model and More-menu ordering rules in the validator and reference docs, and protects the contract with both fast validator tests and representative Livewire guard tests anchored on clickable-row, explicit-inspect, and system-panel reference surfaces.
Technical Context
Language/Version: PHP 8.4.15
Primary Dependencies: Laravel 12, Filament v5, Livewire v4, Pest v4, existing ActionSurfaceDeclaration, ActionSurfaceValidator, ActionSurfaceDiscovery, ActionSurfaceExemptions, and Filament Tables / Actions APIs
Storage: PostgreSQL unchanged; no new persistence, cache store, queue payload, or durable artifact
Testing: Pest 4 feature tests and Livewire component tests, including validator stubs and rendered table guard coverage, executed through Laravel Sail
Target Platform: Laravel monolith web application in Sail locally and containerized Linux deployment in staging/production
Project Type: web application
Performance Goals: Keep repository-wide action-surface validation deterministic and CI-friendly, add no new runtime queries or cross-request state to operator surfaces, and keep behavior-aware render checks limited to representative guard surfaces rather than every declaration-backed class
Constraints: Derived-only governance slice, no new business routes or assets, no new capability or policy family, no new baseline exemptions for already enrolled surfaces, chooser/dashboard/widget/onboarding exemptions remain explicit, and all work must stay within Filament v5 / Livewire v4 conventions
Scale/Scope: The enrolled reference surfaces named by Spec 169 across monitoring pages, reporting/evidence registers, representative CRUD and read-only registry resources, and the six enrolled system-panel list pages; representative render coverage for clickable-row, explicit-inspect, ordered overflow behavior, and out-of-scope preservation
Constitution Check
GATE: Passed before Phase 0 research. Re-checked after Phase 1 design and still passing.
| Principle | Pre-Research | Post-Design | Notes |
|---|---|---|---|
| Read/write separation | PASS | PASS | This is a UI governance and guard-coverage slice only. No domain write path, operation start surface, or Graph call changes are introduced. |
| Workspace + tenant isolation / RBAC-UX | PASS | PASS | Discovery coverage broadens validation only; it does not alter route visibility, panel access, 404 vs 403 semantics, or capability enforcement on tenant or system surfaces. |
| Proportionality / no premature abstraction | PASS WITH JUSTIFIED ENUM | PASS WITH JUSTIFIED ENUM | One new first-class enum and one declaration field are justified because ActionSurfaceProfile cannot distinguish constitution-governed inspect models across CRUD, queue, audit, and registry surfaces. No second registry or UI meta-framework is introduced. |
| Persisted truth / behavioral state | PASS | PASS | No new table, artifact, cache, or persisted status family is introduced. The new surface_type enum is declaration-time behavior metadata only. |
| UI constitution / one inspect model / placeholder ban | PASS | PASS | This feature directly enforces UI-SURF-001, UI-HARD-001, UI-EX-001, and UI-REVIEW-001 by failing redundant inspect patterns, empty groups, and mismatched surface behavior. |
| Filament-native UI | PASS | PASS | The implementation continues to govern native Filament recordUrl(), row actions, ActionGroup, and BulkActionGroup rather than inventing replacement UI. |
| Filament v5 / Livewire v4 compliance | PASS | PASS | The plan keeps all work inside the current Filament v5 + Livewire v4 stack and adds only Livewire-compatible component tests. |
| Provider registration location | PASS | PASS | No panel provider change is required; Laravel 11+ provider registration remains in bootstrap/providers.php. |
| Global search hard rule | PASS | PASS | No globally searchable resource behavior changes are introduced in this slice. |
| Destructive action safety | PASS | PASS | Existing destructive actions remain executed through Filament actions with confirmation and authorization; this feature only validates placement and ordering. |
| Asset strategy | PASS | PASS | No new panel or shared assets are added, so there is no filament:assets deployment impact. |
| Testing truth (TEST-TRUTH-001) | PASS | PASS | The plan uses fast validator stubs for semantic rules and representative rendered tests for business-visible behavior instead of expanding broad presentation-only test matrices. |
Project Structure
Phase 0 Research
Research outcomes are captured in /Users/ahmeddarrazi/Documents/projects/TenantAtlas/specs/169-action-surface-v11/research.md.
Key decisions:
- Add a first-class
ActionSurfaceTypeenum toActionSurfaceDeclarationwhile keepingActionSurfaceProfileas the slot-requirement model. - Keep inspect-model compatibility rules close to the existing contract stack by implementing them in the validator and enum helpers rather than a second registry or framework layer.
- Extend discovery narrowly to declared system-panel table pages under
app/Filament/System/Pages, using table + declaration opt-in so auth, dashboards, widgets, runbooks, and other deferred surfaces remain out of scope. - Keep current panel-scope metadata unchanged for this slice because no active consumer relies on a system-panel scope enum, and the feature’s requirement is validation coverage rather than a new panel taxonomy.
- Split guard coverage between fast validator tests and representative Livewire render tests so declaration-level drift and rendered-behavior drift are both blocked.
Phase 1 Design
Design artifacts are created under /Users/ahmeddarrazi/Documents/projects/TenantAtlas/specs/169-action-surface-v11/:
research.md: phase-0 decisions and rejected alternativesdata-model.md: declaration v1.1 contract, surface-type enum, decision rules, and discovery-scope modelcontracts/action-surface-governance.logical.openapi.yaml: internal logical contract for declaration, discovery, and validation behaviorquickstart.md: focused implementation and verification workflow
Design decisions:
ActionSurfaceTypeis explicit declaration data, not derived metadata.ActionSurfaceProfileremains in place to drive required-slot validation.PrimaryLinkColumnremains an exception path and requires an explicit reason instead of a new exception object model.- System-panel discovery uses filesystem scope plus declaration opt-in instead of a hardcoded class allowlist.
- Behavior-aware ordering checks stay in representative tests rather than trying to introspect every Filament action tree generically.
Project Structure
Documentation (this feature)
specs/169-action-surface-v11/
├── spec.md
├── plan.md
├── research.md
├── data-model.md
├── quickstart.md
├── contracts/
│ └── action-surface-governance.logical.openapi.yaml
├── checklists/
│ └── requirements.md
└── tasks.md
Source Code (repository root)
app/
├── Filament/
│ ├── Pages/
│ │ └── Monitoring/
│ │ ├── AuditLog.php
│ │ ├── FindingExceptionsQueue.php
│ │ └── Operations.php
│ ├── Resources/
│ │ ├── BackupScheduleResource.php
│ │ ├── BaselineProfileResource.php
│ │ ├── OperationRunResource.php
│ │ ├── PolicyResource.php
│ │ ├── TenantResource.php
│ │ └── Workspaces/
│ │ └── WorkspaceResource.php
│ └── System/
│ └── Pages/
│ ├── Directory/
│ │ ├── Tenants.php
│ │ └── Workspaces.php
│ ├── Ops/
│ │ ├── Failures.php
│ │ ├── Runs.php
│ │ ├── Runbooks.php
│ │ └── Stuck.php
│ ├── RepairWorkspaceOwners.php
│ └── Security/
│ └── AccessLogs.php
├── Support/
│ └── Ui/
│ └── ActionSurface/
│ ├── ActionSurfaceDeclaration.php
│ ├── ActionSurfaceDiscovery.php
│ ├── ActionSurfaceExemptions.php
│ ├── ActionSurfaceProfileDefinition.php
│ ├── ActionSurfaceValidator.php
│ └── Enums/
│ ├── ActionSurfaceInspectAffordance.php
│ ├── ActionSurfacePanelScope.php
│ ├── ActionSurfaceProfile.php
│ └── ActionSurfaceType.php
docs/
├── product/
│ └── standards/
│ └── filament-actions-ux.md
└── ui/
└── action-surface-contract.md
tests/
├── Feature/
│ ├── Guards/
│ │ ├── ActionSurfaceContractTest.php
│ │ └── ActionSurfaceValidatorTest.php
│ └── Rbac/
│ └── TenantActionSurfaceConsistencyTest.php
Structure Decision: Keep the existing Laravel monolith structure. Extend the current action-surface support stack under app/Support/Ui/ActionSurface, update the enrolled reference surfaces named by the spec, sync the two developer-facing standards docs, and protect the feature through the current Pest guard suite instead of creating a new module or documentation tree.
Implementation Strategy
Phase A — Introduce the explicit surface-type contract
Goal: Add one constitution-aligned enum and declaration field without creating a second action-governance framework.
| Step | File | Change |
|---|---|---|
| A.1 | app/Support/Ui/ActionSurface/Enums/ActionSurfaceType.php |
Add the new first-class enum with the five enforced surface families: CRUD / List-first Resource, Read-only Registry / Report, Queue / Review, History / Audit, and Config-lite. |
| A.2 | app/Support/Ui/ActionSurface/ActionSurfaceDeclaration.php |
Extend the declaration with a first-class surfaceType field and fluent/factory support, keeping profile as the slot-requirement model and preserving existing defaults, slots, exemptions, and metadata. |
| A.3 | app/Support/Ui/ActionSurface/Enums/ActionSurfaceInspectAffordance.php and app/Support/Ui/ActionSurface/ActionSurfaceProfileDefinition.php |
Keep current affordance/profile models intact while adding the minimum helper semantics needed to evaluate allowed affordance combinations. |
Phase B — Roll out surface types across the enrolled reference surfaces
Goal: Move the reference surfaces enrolled by the spec to explicit declaration-level surface typing before validator enforcement turns strict.
| Step | File | Change |
|---|---|---|
| B.1 | app/Filament/Pages/Monitoring/Operations.php, AuditLog.php, FindingExceptionsQueue.php, EvidenceOverview.php, and app/Filament/Pages/Reviews/ReviewRegister.php |
Establish the primary clickable-row, explicit-inspect, and reporting-registry reference pages with explicit surface types that match the constitution. |
| B.2 | Representative CRUD and read-only registry resources such as TenantResource, PolicyResource, BackupScheduleResource, BaselineProfileResource, WorkspaceResource, AlertDeliveryResource, BaselineSnapshotResource, EvidenceSnapshotResource, ReviewPackResource, and TenantReviewResource |
Align the enrolled resource reference families with the new explicit surface-type field so inspect-model and ordering checks have stable anchors. |
| B.3 | app/Filament/Resources/OperationRunResource.php and the enrolled system list pages under app/Filament/System/Pages/** |
Keep the run-log and cross-panel registry references in the same rollout slice so the validator can fail on missing types without leaving the reference pack in a mixed state. |
Phase C — Bring system-panel table pages into the primary discovery pass
Goal: Eliminate the split between the main validator and the targeted system-page assertions.
| Step | File | Change |
|---|---|---|
| C.1 | app/Support/Ui/ActionSurface/ActionSurfaceDiscovery.php |
Add narrow discovery for app/Filament/System/Pages/** that includes only table-backed pages with declarations, so the six enrolled system list pages enter the primary validator. |
| C.2 | app/Support/Ui/ActionSurface/ActionSurfaceExemptions.php |
Preserve explicit exemptions for deferred families and verify no stale baseline exemptions remain for the six enrolled system pages. |
| C.3 | app/Filament/System/Pages/Ops/Runs.php, Failures.php, Stuck.php, Directory/Tenants.php, Directory/Workspaces.php, and Security/AccessLogs.php |
Confirm these pages remain declaration-backed reference surfaces under the main validator without sweeping in auth, dashboard, runbook, or break-glass pages. |
Phase D — Enforce inspect-model and ordering behavior in the validator and docs
Goal: Make the contract fail for behaviorally wrong declarations, not just missing slots.
| Step | File | Change |
|---|---|---|
| D.1 | app/Support/Ui/ActionSurface/ActionSurfaceValidator.php |
Add behavior-aware rules: require explicit surfaceType, validate allowed inspect affordances by surface type, reject redundant lone View patterns on clickable-row surfaces, and require an explicit reason when PrimaryLinkColumn is used as an exception path. |
| D.2 | docs/ui/action-surface-contract.md and docs/product/standards/filament-actions-ux.md |
Update the developer-facing reference docs together so the constitution-aligned inspect and ordering rules match the validator and the test suite. |
| D.3 | tests/Feature/Guards/ActionSurfaceValidatorTest.php |
Extend the stub-based validator suite to cover missing surfaceType, invalid surface-type/affordance pairings, and required exception reasons. |
Phase E — Add representative rendered guard coverage
Goal: Prove the declaration cannot claim conformance while the actual Filament table behavior drifts.
| Step | File | Change |
|---|---|---|
| E.1 | tests/Feature/Guards/ActionSurfaceContractTest.php |
Add or extend rendered table tests for clickable-row references, explicit-inspect history/audit references, explicit-inspect queue/review references, reporting-registry coverage, system-panel discovery coverage, and helper-first / workflow-next / destructive-last More menu ordering. |
| E.2 | tests/Feature/Rbac/TenantActionSurfaceConsistencyTest.php |
Keep one RBAC-aware tenant resource reference proving row-click and More-menu semantics remain aligned with capability gating and overflow placement. |
| E.3 | vendor/bin/sail bin pint --dirty --format agent plus focused Pest runs |
Format touched files and run the narrow verification pack for validator, contract guard, and tenant action-surface consistency coverage. |
Key Design Decisions
D-001 — surfaceType is explicit and separate from profile
ActionSurfaceProfile remains the technical slot-requirement model. The new surfaceType is the constitution-governed behavioral classification. Keeping them separate avoids forcing slot rules and operator interaction semantics into the same enum.
D-002 — Inspect rules stay close to the existing contract stack
The feature should not introduce a second registry or action-governance subsystem. The narrowest implementation is one new enum plus validator logic and small helper methods where needed.
D-003 — System discovery is opt-in, not broad
System-panel coverage is achieved by discovering declared, table-backed pages under app/Filament/System/Pages, not by sweeping every system page into the validator. This preserves explicit exemptions for auth, dashboards, choosers, and other deferred surfaces.
D-004 — Representative render tests enforce business-visible truth
The validator can prove declaration semantics, but it cannot prove Filament table behavior alone. Representative Livewire tests must anchor the real clickable-row, explicit-inspect, and helper-first / workflow-next / destructive-last rules.
D-005 — PrimaryLinkColumn remains a justified exception path
The spec needs stronger control over linked-column inspect affordances, but not a new exception object model. A required explicit reason in the declaration is sufficient for this slice.
Risk Assessment
| Risk | Impact | Likelihood | Mitigation |
|---|---|---|---|
Declaration rollout is incomplete when surfaceType becomes required |
High | Medium | Introduce the contract and update the entire enrolled reference pack in the same implementation slice before making missing surfaceType a validator failure. |
| Discovery becomes too broad and sweeps in auth, dashboard, or deferred system surfaces | High | Medium | Limit the new discovery path to declared, table-backed system pages and preserve explicit baseline exemptions for deferred families. |
surfaceType and profile drift semantically |
Medium | Medium | Document their separate responsibilities in code, docs, and tests, and anchor each critical surface family with representative declarations. |
| More-menu ordering tests become brittle because of exact action sequences | Medium | Medium | Assert ordering invariants such as helper-first, workflow-next, destructive-last, and non-empty groups instead of pinning every menu to a full exact sequence. |
PrimaryLinkColumn remains under-specified |
Medium | Low | Require explicit reason text and representative validation coverage before allowing the exception path to pass. |
Test Strategy
- Extend
tests/Feature/Guards/ActionSurfaceValidatorTest.phpwith stub declarations that cover requiredsurfaceType, invalid affordance combinations, and explicit exception-reason requirements. - Extend
tests/Feature/Guards/ActionSurfaceContractTest.phpwith representative Livewire coverage for Monitoring Operations orOperationRunResource, Audit Log, Finding Exceptions Queue, a reporting or evidence register, one CRUDMoremenu that proves helper-first, workflow-next, destructive-last ordering, and the six primary system-panel list pages discovered by the validator. - Keep one tenant-plane RBAC-aware reference test in
tests/Feature/Rbac/TenantActionSurfaceConsistencyTest.phpso action-surface behavior remains compatible with disabled-vs-forbidden gating rules. - Run the narrow Sail verification pack from
quickstart.mdbefore considering the slice complete. - Ask whether the user wants the full suite after focused tests pass.
Complexity Tracking
| Violation | Why Needed | Simpler Alternative Rejected Because |
|---|---|---|
New first-class surfaceType enum and declaration field |
The existing ActionSurfaceProfile cannot distinguish constitution-level inspect behavior between queue, audit, registry, CRUD, and config-lite surfaces, so behavior-aware validation needs explicit type data. |
Reusing profile or hiding the distinction in metadata would keep the rule implicit, make validator failures less actionable, and preserve the declaration-only gap this spec is fixing. |
Proportionality Review
- Current operator problem: The repository can already prove that declarations exist, but it cannot yet prove that the declared inspect model and overflow behavior actually match the constitution. That leaves real clickable-row and explicit-inspect surfaces vulnerable to silent behavioral drift.
- Existing structure is insufficient because:
ActionSurfaceProfileonly describes slot requirements. It is too coarse to distinguish queue and audit surfaces that require explicit inspect from CRUD and registry surfaces that require one-click open. Primary discovery also still excludes the enrolled system-panel list pages. - Narrowest correct implementation: Add one first-class
surfaceTypeenum toActionSurfaceDeclaration, extend discovery narrowly to the already-enrolled system table pages, and strengthen the validator plus representative rendered tests. Do not add a second registry, persistence layer, or UI framework. - Ownership cost created: This adds one enum, one declaration field, more explicit declaration work on the enrolled reference surfaces, stronger validator logic, and a small set of focused guard tests that reviewers must maintain as the contract evolves.
- Alternative intentionally rejected: Documentation-only guidance and declaration-only slot validation were rejected because they cannot fail when rendered behavior drifts. Replacing
ActionSurfaceProfileentirely was rejected because slot requirements and constitution surface semantics are separate concerns. - Release truth: Current-release truth. The repository already contains both correct clickable-row and correct explicit-inspect patterns, and the missing work is durable enforcement.