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

22 KiB

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)

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)

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.