TenantAtlas/specs/169-action-surface-v11/plan.md

22 KiB
Raw Permalink Blame History

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 ActionSurfaceType enum to ActionSurfaceDeclaration while keeping ActionSurfaceProfile as 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 features 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 alternatives
  • data-model.md: declaration v1.1 contract, surface-type enum, decision rules, and discovery-scope model
  • contracts/action-surface-governance.logical.openapi.yaml: internal logical contract for declaration, discovery, and validation behavior
  • quickstart.md: focused implementation and verification workflow

Design decisions:

  • ActionSurfaceType is explicit declaration data, not derived metadata.
  • ActionSurfaceProfile remains in place to drive required-slot validation.
  • PrimaryLinkColumn remains 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.php with stub declarations that cover required surfaceType, invalid affordance combinations, and explicit exception-reason requirements.
  • Extend tests/Feature/Guards/ActionSurfaceContractTest.php with representative Livewire coverage for Monitoring Operations or OperationRunResource, Audit Log, Finding Exceptions Queue, a reporting or evidence register, one CRUD More menu 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.php so action-surface behavior remains compatible with disabled-vs-forbidden gating rules.
  • Run the narrow Sail verification pack from quickstart.md before 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: ActionSurfaceProfile only 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 surfaceType enum to ActionSurfaceDeclaration, 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 ActionSurfaceProfile entirely 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.