TenantAtlas/specs/194-governance-friction-hardening/plan.md
ahmido acc8947384 feat: harden governance action semantics (#229)
## Summary
- add the Spec 194 governance action catalog, friction classes, reason policies, and regression guards
- align exception, review, evidence, finding, tenant, provider connection, and system run actions to the shared semantics model
- add focused feature, RBAC, audit, unit, and browser coverage, including the tenant detail triage header consistency update

## Verification
- ran the focused Spec 194 verification pack from the quickstart and task plan
- ran targeted tenant triage coverage after the detail-header update
- ran `cd apps/platform && ./vendor/bin/sail bin pint --dirty --format agent`

## Filament Notes
- Filament v5 / Livewire v4 compliance preserved
- provider registration remains in `apps/platform/bootstrap/providers.php`
- globally searchable resources were not changed
- destructive actions remain confirmation-gated and server-authorized
- no new Filament assets were introduced; the existing `cd apps/platform && php artisan filament:assets` deploy step stays unchanged

Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #229
2026-04-12 21:21:44 +00:00

317 lines
22 KiB
Markdown

# Implementation Plan: Governance Friction Hardening and Operator Vocabulary
**Branch**: `194-governance-friction-hardening` | **Date**: 2026-04-12 | **Spec**: `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/specs/194-governance-friction-hardening/spec.md`
**Input**: Feature specification from `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/specs/194-governance-friction-hardening/spec.md`
**Note**: This plan keeps the work inside the existing Filament v5 / Livewire v4 surface layer, existing mutation services, existing audit loggers, and the current RBAC helpers. It explicitly avoids introducing a new workflow engine, new persistence, or a broad UI meta-framework.
## Summary
Codify one narrow governance-action semantics contract across tenant, workspace, and system surfaces. Introduce a derived governance action catalog that classifies in-scope actions into explicit friction classes, reason rules, danger semantics, and canonical vocabulary; then align the affected Filament pages and existing mutation services so exception, review, evidence, run-triage, finding-lifecycle, and tenant-lifecycle actions behave consistently. Protect the result with a spec-scoped guard, focused feature tests, RBAC regression coverage, and one browser smoke suite.
## Technical Context
**Language/Version**: PHP 8.4.15
**Primary Dependencies**: Laravel 12, Filament v5, Livewire v4, Pest v4, Tailwind CSS v4, existing `UiEnforcement`, existing audit loggers (`AuditLogger`, `WorkspaceAuditLogger`, `SystemConsoleAuditLogger`), existing mutation services (`FindingExceptionService`, `FindingWorkflowService`, `TenantReviewLifecycleService`, `EvidenceSnapshotService`, `OperationRunTriageService`)
**Storage**: PostgreSQL through existing workspace-owned and tenant-owned models; no schema change planned
**Testing**: Pest unit, feature, and browser tests run through Laravel Sail
**Target Platform**: Laravel monolith web application under `apps/platform`, with tenant routes under `/admin/t/{tenant}/...`, workspace routes under `/admin/...`, and platform routes under `/system/...`
**Project Type**: web application
**Performance Goals**: Preserve current operator interaction speed, keep render-time governance semantics DB-only with no outbound HTTP, avoid adding polling or additional round trips for confirmation flows, and keep any catalog lookups constant-time and local
**Constraints**: No new persistence, no new workflow states, no panel/provider changes, no raw capability strings, no cross-plane authorization drift, destructive-like actions keep `->requiresConfirmation()`, and no new generic execution framework
**Scale/Scope**: 8 primary operator surfaces across 3 authorization planes, 6 high-priority governance families, 4 medium or low-priority supporting families, focused changes to existing page classes, services, and tests only
## Constitution Check
*GATE: Passed before Phase 0 research. Re-check after Phase 1 design and still passing.*
| Principle | Pre-Research | Post-Design | Notes |
|-----------|--------------|-------------|-------|
| Inventory-first / snapshots-second | PASS | PASS | The feature does not alter inventory or snapshot truth. It governs action semantics only. |
| Read/write separation | PASS | PASS | Existing mutations keep confirmation, audit, and tests. No new write domain is introduced. |
| Graph contract path | N/A | N/A | No new Microsoft Graph endpoints or contract-registry changes are planned. |
| Deterministic capabilities | PASS | PASS | Existing capability registries and `UiEnforcement` remain authoritative. |
| Workspace + tenant isolation | PASS | PASS | Existing scope boundaries remain authoritative on `/admin`, `/admin/t/{tenant}`, and `/system`. |
| RBAC-UX authorization semantics | PASS | PASS | Non-member remains `404`, member-without-capability remains `403`, and server-side checks remain unchanged. |
| Run observability / Ops-UX | PASS | PASS | Existing `OperationRun` flows keep their current lifecycle and feedback contract; this feature changes only semantics. |
| Data minimization | PASS | PASS | No new persistence, caches, or semantic mirrors are introduced. |
| Proportionality / anti-bloat | PASS | PASS | The plan adds one narrow derived catalog and guard instead of a new framework or persistence layer. |
| UI semantics / few layers | PASS | PASS | The feature uses direct action semantics and targeted builders instead of a new presenter stack. |
| Filament-native UI | PASS | PASS | Native Filament actions, action groups, and current shared helpers remain the implementation path. |
| Surface taxonomy / decision-first roles | PASS | PASS | Surface roles and action semantics remain aligned with Spec 192 and Spec 193 without reclassifying the affected surfaces. |
| Filament v5 / Livewire v4 compliance | PASS | PASS | All touched surfaces remain inside the existing Filament v5 + Livewire v4 stack. |
| Provider registration location | PASS | PASS | No provider change is needed; Laravel 11+ registration remains in `bootstrap/providers.php`. |
| Global search hard rule | PASS | PASS | No new globally searchable resource is introduced and search settings are not altered. |
| Destructive action safety | PASS | PASS | Strong actions continue to execute via confirmed Filament actions plus current authorization. |
| Asset strategy | PASS | PASS | No new assets are planned. Existing deployment handling of `cd apps/platform && php artisan filament:assets` remains sufficient. |
## Filament-Specific Compliance Notes
- **Livewire v4.0+ compliance**: The plan remains on Filament v5 + Livewire v4 and introduces no legacy APIs.
- **Provider registration location**: No panel or provider changes are required; registration remains in `bootstrap/providers.php`.
- **Global search**: This feature does not add new globally searchable resources and does not change current resource search behavior.
- **Destructive actions**: `Revoke exception`, `Archive review`, `Cancel`, and `Archive` remain execution actions with confirmation and server-side authorization.
- **Asset strategy**: No new global or lazy-loaded assets are planned. Existing `filament:assets` deployment behavior remains unchanged.
- **Testing plan**: Add one spec-scoped guard layer, focused action and authorization tests, and one browser smoke suite across the highest-risk surfaces.
## Phase 0 Research
Research outcomes are captured in `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/specs/194-governance-friction-hardening/research.md`.
Key decisions:
- Reuse existing mutation services and audit loggers; do not add a new governance workflow engine.
- Introduce one narrow derived governance-action catalog instead of page-local constants or a broad action meta-framework.
- Keep action semantics family-first: exception, review, evidence, run-triage, finding-lifecycle, and tenant-lifecycle.
- Treat reason capture as a family contract and extend current services or audit metadata only where the spec requires stronger propagation.
- Use the existing three testing layers already proven in this repo: spec guard, focused feature/RBAC tests, and one browser smoke suite.
## Phase 1 Design
Design artifacts are created under `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/specs/194-governance-friction-hardening/`:
- `research.md`: implementation-shape, reason-propagation, and vocabulary decisions
- `data-model.md`: derived governance family, rule, binding, deviation, and regression models
- `contracts/governance-action-semantics.logical.openapi.yaml`: internal logical contract for governance-action family rules and surface bindings
- `quickstart.md`: implementation and verification sequence for the feature
Design highlights:
- Keep all new semantics derived, not persisted.
- Model governance rules by action family first, then bind them to concrete page actions.
- Reuse existing services as owners of state change, audit logging, and operation behavior.
- Centralize only the shared semantics that already have multiple real concrete cases.
- Keep surface placement aligned with Spec 192 and Spec 193; Spec 194 governs semantic hardness, not where actions live.
## Phase 1 - Agent Context Update
Planned command:
- `.specify/scripts/bash/update-agent-context.sh copilot`
This feature does not introduce a new technology stack, but the required context refresh still runs after the technical context and design artifacts are complete.
## Project Structure
### Documentation (this feature)
```text
specs/194-governance-friction-hardening/
├── plan.md
├── research.md
├── data-model.md
├── quickstart.md
├── spec.md
├── contracts/
│ └── governance-action-semantics.logical.openapi.yaml
└── checklists/
└── requirements.md
```
### Source Code (repository root)
```text
apps/platform/
├── app/
│ ├── Filament/
│ │ ├── Pages/
│ │ │ ├── Monitoring/
│ │ │ │ └── FindingExceptionsQueue.php # MODIFY
│ │ │ └── Operations/
│ │ │ └── TenantlessOperationRunViewer.php # REVIEW / possible minor alignment only
│ │ ├── Resources/
│ │ │ ├── FindingResource.php # MODIFY
│ │ │ ├── FindingResource/
│ │ │ │ └── Pages/
│ │ │ │ └── ViewFinding.php # MODIFY
│ │ │ ├── FindingExceptionResource/
│ │ │ │ └── Pages/
│ │ │ │ └── ViewFindingException.php # MODIFY
│ │ │ ├── EvidenceSnapshotResource.php # MODIFY
│ │ │ ├── EvidenceSnapshotResource/
│ │ │ │ └── Pages/
│ │ │ │ └── ViewEvidenceSnapshot.php # MODIFY
│ │ │ ├── TenantReviewResource/
│ │ │ │ └── Pages/
│ │ │ │ └── ViewTenantReview.php # MODIFY
│ │ │ ├── TenantResource.php # MODIFY
│ │ │ └── TenantResource/
│ │ │ └── Pages/
│ │ │ ├── ViewTenant.php # MODIFY
│ │ │ └── EditTenant.php # MODIFY
│ │ └── System/
│ │ └── Pages/
│ │ └── Ops/
│ │ └── ViewRun.php # MODIFY
│ ├── Services/
│ │ ├── Findings/
│ │ │ ├── FindingExceptionService.php # MODIFY
│ │ │ └── FindingWorkflowService.php # MODIFY
│ │ ├── Evidence/
│ │ │ └── EvidenceSnapshotService.php # MODIFY
│ │ ├── TenantReviews/
│ │ │ └── TenantReviewLifecycleService.php # MODIFY
│ │ └── SystemConsole/
│ │ └── OperationRunTriageService.php # MODIFY
│ ├── Support/
│ │ └── Ui/
│ │ └── GovernanceActions/
│ │ ├── GovernanceActionCatalog.php # NEW
│ │ ├── GovernanceActionRule.php # NEW
│ │ └── Enums/
│ │ ├── GovernanceFrictionClass.php # NEW
│ │ └── GovernanceReasonPolicy.php # NEW
└── tests/
├── Feature/
│ ├── Guards/
│ │ └── Spec194GovernanceActionSemanticsGuardTest.php # NEW
│ ├── Monitoring/
│ │ ├── FindingExceptionsQueueHierarchyTest.php # MODIFY
│ │ └── FindingExceptionsQueueTest.php # MODIFY
│ ├── Findings/
│ │ ├── FindingExceptionWorkflowTest.php # MODIFY
│ │ ├── FindingExceptionRenewalTest.php # MODIFY
│ │ ├── FindingExceptionRevocationTest.php # MODIFY
│ │ ├── FindingWorkflowViewActionsTest.php # MODIFY
│ │ └── FindingAuditLogTest.php # MODIFY
│ ├── Evidence/
│ │ └── EvidenceSnapshotResourceTest.php # MODIFY
│ ├── TenantReview/
│ │ ├── TenantReviewUiContractTest.php # MODIFY
│ │ └── TenantReviewLifecycleTest.php # MODIFY
│ ├── Operations/
│ │ ├── TenantlessOperationRunViewerTest.php # REVIEW / possible extend
│ │ └── SystemRunBlockedExecutionNotificationTest.php # REVIEW / possible extend
│ ├── Rbac/
│ │ ├── TenantLifecycleActionVisibilityTest.php # MODIFY
│ │ ├── EditTenantArchiveUiEnforcementTest.php # MODIFY
│ │ └── TenantResourceAuthorizationTest.php # MODIFY
│ └── Audit/
│ └── TenantLifecycleAuditLogTest.php # MODIFY
├── Unit/
│ └── Ui/
│ └── GovernanceActions/
│ └── GovernanceActionCatalogTest.php # NEW
└── Browser/
│ └── Spec194GovernanceFrictionSmokeTest.php # NEW
```
**Structure Decision**: Keep the work entirely inside the existing Laravel or Filament monolith under `apps/platform`. Add one narrow support namespace for shared governance semantics, then modify the affected page classes, mutation services, and focused tests. Do not introduce new persistence or a second runtime orchestration layer.
## Complexity Tracking
| Violation | Why Needed | Simpler Alternative Rejected Because |
|-----------|------------|-------------------------------------|
| New derived friction and reason taxonomy | The feature needs one shared project-wide rule for actions that already exist across multiple surfaces and panels. | Local constants and per-page copy edits would not prevent drift or make regression guardable. |
| New shared governance-action catalog | Multiple concrete families already exist and need one canonical source for friction, reason, vocabulary, and approved deviations. | Keeping all semantics inside individual page classes would duplicate logic, produce inconsistent naming, and make CI enforcement weak. |
## Proportionality Review
- **Current operator problem**: Similar governance actions currently carry different semantic weight, reason burden, and vocabulary depending on the surface.
- **Existing structure is insufficient because**: Current page-local action definitions and service calls do not provide one guardable source for friction class, reason requirement, or canonical wording across families.
- **Narrowest correct implementation**: Add a small derived catalog and enums for friction and reason policy, bind existing actions to those rules, and keep all state change in the current services.
- **Ownership cost created**: One small shared support namespace, one spec-scoped guard test, targeted page and service test updates, and one browser smoke suite.
- **Alternative intentionally rejected**: A generic governance workflow framework or persisted action matrix was rejected because the repo only needs explicit cross-surface semantics, not a new runtime engine.
- **Release truth**: current-release operator safety, auditability, and semantic consistency
## Implementation Strategy
### Phase A - Codify the shared governance semantics contract
Goal: create one derived, testable source for action families without introducing a new workflow engine.
Changes:
- Add `GovernanceFrictionClass` and `GovernanceReasonPolicy` enums.
- Add `GovernanceActionRule` plus `GovernanceActionCatalog` as the canonical mapping of action family, friction, reason policy, danger expectation, and canonical copy.
- Declare the current-release indirect risk-acceptance continuity rule so finding exception semantics remain the canonical carrier until a direct risk-acceptance surface exists.
- Add `Spec194GovernanceActionSemanticsGuardTest.php` to ensure every in-scope action family and documented deviation is declared.
- Keep the catalog derived only. Do not create DB tables or stored mirrors.
Tests:
- Add `GovernanceActionCatalogTest.php` for catalog completeness and invariants.
- Add `Spec194GovernanceActionSemanticsGuardTest.php` for project-level inventory and exception coverage.
### Phase B - Align the highest-risk governance families
Goal: normalize the surfaces where semantic inconsistency carries the highest operator risk.
Changes:
- Align exception decision and lifecycle actions on `FindingExceptionsQueue` and `ViewFindingException`.
- Align review publication and archival semantics on `ViewTenantReview`.
- Align evidence refresh versus expiry on `EvidenceSnapshotResource` and `ViewEvidenceSnapshot`, keeping `Refresh evidence` as confirmed F1 with no operator-entered reason.
- Align run triage semantics on `System ViewRun`, keeping `Retry` as confirmed F1 with no operator-entered reason while `Mark investigated` and `Cancel` keep stronger rationale rules.
- Extend or standardize reason propagation in the owning services and audit loggers where F2 or F3 requires it.
Tests:
- Extend exception workflow and queue tests.
- Extend tenant review lifecycle and UI-contract tests.
- Extend evidence snapshot resource tests.
- Add or extend run-triage tests around `ViewRun`-owned actions and audit behavior.
### Phase C - Align supporting lifecycle families and preserve calm surfaces
Goal: finish cross-surface consistency without overcorrecting lower-risk actions.
Changes:
- Align finding close and reopen semantics across header, row, and bulk actions in `FindingResource`, `ViewFinding`, and `FindingWorkflowService`.
- Align tenant archive and restore semantics across `ViewTenant`, `EditTenant`, `TenantResource`, and current audit logging, keeping `Restore` as confirmed F1 with no operator-entered reason.
- Keep indirect risk-acceptance wording aligned with the exception family and document any allowed alias only in the shared catalog.
- Review `TenantlessOperationRunViewer` to ensure it stays context-first and does not drift into a triage surface unless justified.
- Keep navigation, export, and related-context actions explicitly outside governance friction.
Tests:
- Extend finding workflow header-, row-, and bulk-action tests, finding audit tests, and finding view-action tests.
- Extend tenant lifecycle RBAC, naming, and audit tests.
- Extend any affected operation viewer tests only if the viewer surface changes semantics.
### Phase D - Browser verification and final regression protection
Goal: prove the new semantics in a real browser and prevent new local exceptions from returning.
Changes:
- Add `Spec194GovernanceFrictionSmokeTest.php` covering exception queue/detail, review detail, evidence detail, system run detail, and tenant lifecycle surfaces.
- Ensure the guard layer fails when a new governance action lacks a declared family, friction class, reason rule, or documented deviation.
- Re-run formatting and the focused Sail verification pack.
Tests:
- Browser smoke coverage for visible friction, copy, and danger separation.
- Focused guard, feature, and authorization tests for all changed families.
## Risk Assessment
| Risk | Impact | Likelihood | Mitigation |
|------|--------|------------|------------|
| The catalog grows into a general workflow framework | Medium | Low | Keep only friction, reason, vocabulary, and deviation metadata; leave execution in existing services. |
| Reason capture is added inconsistently across services | High | Medium | Make reason policy family-owned and test propagation at service and audit levels. |
| Low-risk actions accidentally inherit F3 semantics | Medium | Medium | Keep explicit F0 and F1 boundaries in the catalog and browser smoke coverage. |
| Surface-specific copy diverges from the family canon | Medium | Medium | Use one catalog source for labels and heading copy where practical and guard with tests. |
| Authorization semantics drift while actions are reworked | High | Low | Reuse existing Policies and `UiEnforcement`, and extend positive plus negative RBAC tests. |
## Test Strategy
- Add `GovernanceActionCatalogTest.php` so friction, reason, and deviation rules remain internally consistent.
- Add `Spec194GovernanceActionSemanticsGuardTest.php` to validate that every in-scope family, indirect risk-acceptance alias, and documented exception is declared.
- Extend exception queue/detail tests to assert family-consistent reason prompts and semantic separation.
- Extend review, evidence, finding, run, tenant lifecycle, and audit tests where the plan changes semantics or reason propagation, including explicit header-, row-, and bulk-finding lifecycle coverage.
- Reuse existing RBAC feature tests to prove non-member `404`, member-without-capability `403`, and correct disabled-state behavior where UI enforcement remains visible.
- Add `Spec194GovernanceFrictionSmokeTest.php` using the existing spec-based browser smoke pattern already present in the repo.
- Run the focused Sail verification commands from `quickstart.md`, then run `cd apps/platform && ./vendor/bin/sail bin pint --dirty --format agent`.
## Constitution Check (Post-Design)
Re-check result: PASS.
- Livewire v4.0+ compliance remains intact because all touched surfaces stay inside the existing Filament v5 + Livewire v4 stack.
- Provider registration remains unchanged in `bootstrap/providers.php`.
- The plan changes no global-search semantics; affected surfaces are existing resource pages or standalone pages whose search behavior is unchanged.
- Destructive and governance-changing actions keep `->requiresConfirmation()` plus existing authorization.
- No new assets are introduced; existing `filament:assets` deployment behavior remains sufficient.