# 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 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 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)

```text
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)

```text
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.
