TenantAtlas/specs/198-monitoring-page-state/plan.md

26 KiB

Implementation Plan: Monitoring Page-State Contract

Branch: 198-monitoring-page-state | Date: 2026-04-15 | Spec: /Users/ahmeddarrazi/Documents/projects/TenantAtlas/specs/198-monitoring-page-state/spec.md Input: Feature specification from /Users/ahmeddarrazi/Documents/projects/TenantAtlas/specs/198-monitoring-page-state/spec.md

Note: This plan keeps the work inside the existing Filament v5 / Livewire v4 page layer, current query and session helpers, and the current monitoring pages. It explicitly avoids a new global page-state framework, new persistence, or a shell-context refactor.

Summary

Codify one bounded monitoring page-state contract for Operations, Audit Log, Finding Exceptions Queue, Evidence Overview, Baseline Compare Landing, and Baseline Compare Matrix, with Baseline Compare Landing remaining the compare launch-context support surface. Reuse existing Livewire page properties, CanonicalAdminTenantFilterState, CanonicalNavigationContext, existing session-backed table persistence, and the current compare draft/apply pattern to make contextual prefilter state, active state, inspect state, draft state, and shareable or restorable state explicit and consistent without adding a new runtime state engine.

Technical Context

Language/Version: PHP 8.4.15
Primary Dependencies: Laravel 12, Filament v5, Livewire v4, Pest v4, Tailwind CSS v4, existing CanonicalAdminTenantFilterState, CanonicalNavigationContext, OperateHubShell, Filament InteractsWithTable, and page-local Livewire state on the affected Filament pages
Storage: PostgreSQL plus existing Laravel session-backed table filter, search, and sort persistence; no schema change planned
Testing: Pest feature tests, Filament or Livewire page tests, existing table-state persistence tests, and focused Pest browser smoke tests run through Laravel Sail
Target Platform: Laravel monolith web application under apps/platform, with canonical monitoring routes under /admin and tenant-bound compare routes under /admin/t/{tenant} or active tenant context
Project Type: web application
Performance Goals: Keep monitoring pages DB-only at render time, preserve current session-backed table persistence, avoid new outbound HTTP or queued work during state hydration, avoid N+1 query regressions, and keep first-mount state hydration deterministic and cheap
Constraints: No new global page-state framework, no new persistence, no panel or route-family changes, no shell or context refactor that belongs in Spec 199, no authorization-plane changes, no new Graph calls, no new badge taxonomy, and no forced flattening of Baseline Compare Matrix into a generic table contract
Scale/Scope: 5 primary in-scope page-state surfaces, 1 compare launch-context support surface, 6 page classes, 7 existing focused test files, likely 1 new cross-surface contract test, and 1 focused browser smoke suite or extension of existing smoke coverage

Constitution Check

GATE: Passed before Phase 0 research. Re-checked 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, snapshot, or backup truth. It standardizes page-state semantics only.
Read/write separation PASS PASS Existing writes such as exception approval or rejection keep their current confirmation, audit, and authorization behavior. No new write workflow is introduced.
Graph contract path N/A N/A No Microsoft Graph calls or contract-registry changes are planned.
Deterministic capabilities PASS PASS Capability checks stay in the existing registries and page actions. The work does not add new raw capability strings or new auth planes.
Workspace + tenant isolation PASS PASS Requested tenant filters, selected-record deeplinks, and compare focus state remain subject to existing workspace and tenant entitlement checks.
RBAC-UX authorization semantics PASS PASS Non-members remain 404, members without capability remain 403, and server-side authorization remains authoritative for mutations and restricted drilldowns.
Run observability / Ops-UX PASS PASS No new OperationRun is introduced. Monitoring pages remain DB-only at render, and no start surface behavior is changed.
Data minimization PASS PASS No new persistence, caches, or derived artifacts are introduced. The contract remains derived from existing page and query state.
Proportionality / anti-bloat PASS WITH JUSTIFIED TAXONOMY PASS WITH JUSTIFIED TAXONOMY The feature introduces a bounded page-state taxonomy because five primary monitoring surfaces plus the compare launch surface already need the same explicit contract. It avoids a runtime framework or registry unless implementation proves one tiny helper is truly unavoidable.
UI semantics / few layers PASS PASS The design uses direct page-state declarations and existing helpers, not a presenter or explanation framework.
Filament-native UI PASS PASS Existing Filament pages, tables, actions, and Blade views remain the implementation path. No custom status system or new UI framework is needed.
Shell boundary / Spec 199 separation PASS PASS Global workspace or tenant shell concerns remain out of scope. Any discovered shell drift is documented for Spec 199 instead of solved here.
Filament v5 / Livewire v4 compliance PASS PASS All touched surfaces remain on Filament v5 and Livewire v4 page patterns.
Provider registration location PASS PASS No panel or provider change is required; Laravel 11+ provider registration remains in bootstrap/providers.php.
Global search hard rule PASS PASS No globally searchable resource is added or modified. Existing search behavior remains unchanged.
Destructive action safety PASS PASS Existing destructive-like governance actions in Finding Exceptions Queue keep ->requiresConfirmation() and current authorization and audit semantics.
Asset strategy PASS PASS No new global or lazy-loaded 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 implementation stays on Filament v5 + Livewire v4 page, table, and action APIs. No legacy Livewire or Filament APIs are introduced.
  • Provider registration location: No provider or panel registration changes are planned. Laravel 11+ provider registration remains in bootstrap/providers.php.
  • Global search: No in-scope page is a globally searchable resource surface, and this plan does not change global search visibility for any existing resource.
  • Destructive actions: Approve exception and Reject exception remain the only destructive-like actions directly affected by state-contract cleanup. They continue to execute via Action::make(...)->action(...) with ->requiresConfirmation(), existing server-side authorization, and existing audit behavior. Operations, Audit Log, Evidence Overview, and Baseline Compare Matrix do not gain new destructive actions.
  • Asset strategy: No new assets or build steps are planned. Existing deployment handling of cd apps/platform && php artisan filament:assets remains unchanged.
  • Testing plan: Extend the current Operations, Audit Log, Finding Exceptions Queue, Evidence Overview, Baseline Compare Landing, Baseline Compare Matrix, ActionSurfaceRbacSemanticsTest, BaselineCompareMatrixAuthorizationTest, and table-persistence suites, and add one narrow cross-surface contract test plus focused browser smoke coverage for refresh, back, deeplink, share behavior, and unauthorized requested-state fallback.

Phase 0 Research

Research outcomes are captured in /Users/ahmeddarrazi/Documents/projects/TenantAtlas/specs/198-monitoring-page-state/research.md.

Key decisions:

  • Reuse CanonicalAdminTenantFilterState, CanonicalNavigationContext, existing session-backed table persistence, and page-local Livewire state instead of introducing a global page-state framework.
  • Make state precedence explicit: supported query or deeplink input hydrates first on mount, session provides baseline only for explicitly persisted filter/search/sort state, and active local state becomes authoritative after hydration.
  • Unify selected-record inspect on Audit Log and Finding Exceptions Queue so action-based inspect and deeplinked selected IDs express the same inspect state.
  • Keep Baseline Compare Matrix as the only explicit draft/apply special case, with draft state remaining local-only and unapplied draft state never being shareable or restorable.
  • Treat Baseline Compare Landing as a launch-context broker for matrix state, not as a competing owner of compare state.
  • Extend existing Pest and Livewire test seams rather than leaning on browser-only validation or adding a new guard framework.

Phase 1 Design

Design artifacts are created under /Users/ahmeddarrazi/Documents/projects/TenantAtlas/specs/198-monitoring-page-state/:

  • research.md: page-state decisions, rationale, and rejected alternatives
  • data-model.md: derived page-state contract model, state-field descriptors, hydration rules, and inspect-state descriptors
  • contracts/monitoring-page-state.logical.openapi.yaml: internal logical contract for the bounded page-state model and per-surface expectations
  • quickstart.md: implementation and verification workflow for Spec 198

Design highlights:

  • Keep the contract derived and page-local. No new persisted truth or generalized runtime registry is planned.
  • Reuse existing helpers for tenant-sensitive session state and navigation context rather than adding a second helper layer.
  • Make the role of each state field explicit per surface: contextual prefilter, active, draft, inspect, and shareable/restorable.
  • Keep Operations and Evidence Overview in the simple query or session plus active-state pattern, keep Audit Log and Finding Exceptions Queue in the unified selected-record inspect pattern, and keep Baseline Compare Matrix in the explicit draft/applied/focus special-case pattern.
  • Add cross-surface regression protection through focused feature tests instead of a new runtime state validator.

Phase 1 — Agent Context Update

Planned command:

  • .specify/scripts/bash/update-agent-context.sh copilot

This feature does not introduce a new language or framework, but the required agent-context refresh still runs after the design artifacts are complete.

Project Structure

Documentation (this feature)

specs/198-monitoring-page-state/
├── spec.md
├── plan.md
├── research.md
├── data-model.md
├── quickstart.md
├── contracts/
│   └── monitoring-page-state.logical.openapi.yaml
├── checklists/
│   └── requirements.md
└── tasks.md

Source Code (repository root)

apps/platform/
├── app/
│   ├── Filament/
│   │   └── Pages/
│   │       ├── Monitoring/
│   │       │   ├── Operations.php                                  # MODIFY
│   │       │   ├── AuditLog.php                                    # MODIFY
│   │       │   ├── FindingExceptionsQueue.php                      # MODIFY
│   │       │   └── EvidenceOverview.php                            # MODIFY
│   │       ├── BaselineCompareLanding.php                          # MODIFY
│   │       └── BaselineCompareMatrix.php                           # MODIFY
│   └── Support/
│       ├── Filament/
│       │   └── CanonicalAdminTenantFilterState.php                 # REUSE / possible small extend
│       ├── Navigation/
│       │   └── CanonicalNavigationContext.php                      # REUSE / possible small extend
│       └── OperateHub/
│           └── OperateHubShell.php                                 # REUSE
├── resources/
│   └── views/
│       └── filament/
│           └── pages/
│               ├── monitoring/
│               │   ├── operations.blade.php                        # MODIFY
│               │   ├── audit-log.blade.php                         # MODIFY
│               │   ├── partials/
│               │   │   └── audit-log-inspect-event.blade.php       # MODIFY
│               │   ├── finding-exceptions-queue.blade.php          # MODIFY
│               │   └── evidence-overview.blade.php                 # MODIFY
│               ├── baseline-compare-landing.blade.php              # MODIFY
│               └── baseline-compare-matrix.blade.php               # MODIFY
└── tests/
    ├── Feature/
    │   ├── Monitoring/
    │   │   ├── OperationsDashboardDrillthroughTest.php             # MODIFY
    │   │   ├── AuditLogInspectFlowTest.php                         # MODIFY
    │   │   ├── FindingExceptionsQueueHierarchyTest.php             # MODIFY
    │   │   └── MonitoringPageStateContractTest.php                 # NEW
    │   ├── Evidence/
    │   │   └── EvidenceOverviewPageTest.php                        # MODIFY
    │   ├── Filament/
    │   │   ├── BaselineCompareMatrixPageTest.php                   # MODIFY
    │   │   ├── BaselineCompareLandingStartSurfaceTest.php          # MODIFY
    │   │   └── TableStatePersistenceTest.php                       # MODIFY
    │   └── Rbac/
    │       ├── BaselineCompareMatrixAuthorizationTest.php          # REUSE / possible extend
    │       └── ActionSurfaceRbacSemanticsTest.php                  # REUSE / possible extend
    └── Browser/
        ├── Spec190BaselineCompareMatrixSmokeTest.php               # REUSE / possible extend
        ├── Spec194GovernanceFrictionSmokeTest.php                  # REUSE / possible extend
        └── Spec198MonitoringPageStateSmokeTest.php                 # NEW

Structure Decision: Keep the work entirely inside the existing Laravel/Filament monolith under apps/platform. Modify the affected page classes, their existing Blade views, and focused test suites. Reuse current support helpers and extract a new support helper only if repeated implementation proves that at least three pages share the same normalization code in a way that cannot stay page-local.

Complexity Tracking

Violation Why Needed Simpler Alternative Rejected Because
Cross-surface monitoring page-state taxonomy and per-surface contract documentation (BLOAT-001 trigger) Five primary monitoring surfaces plus the compare launch surface already have overlapping but divergent page-state behavior, and the product needs explicit hydration, inspect, and restore rules now. Pure page-by-page local cleanup would reduce individual bugs but would not produce a shared operator contract or durable regression coverage across the monitoring family.

Proportionality Review

  • Current operator problem: Similar monitoring pages currently give different answers about what a deeplink sets, what refresh restores, when a selected record is authoritative, and whether draft state exists.
  • Existing structure is insufficient because: The current page-local implementations contain the behavior, but not a consistent or documented contract. Without an explicit shared model, the same drift will continue page by page.
  • Narrowest correct implementation: Reuse current helpers and page-local Livewire state, add explicit per-surface state declarations and targeted tests, and keep Compare Matrix as one documented special case. Do not add persistence or a global runtime framework.
  • Ownership cost created: Reviewers must maintain one bounded page-state taxonomy, a small set of per-surface declarations, and focused regression coverage for deeplink, inspect, and restoration behavior.
  • Alternative intentionally rejected: A global page-state engine or shell-level state resolver was rejected because the affected surfaces can be standardized with existing helpers and local state.
  • Release truth: current-release operator predictability and monitoring-family consistency

Implementation Strategy

Phase A — Declare the bounded page-state contract per surface

Goal: Make the state classes, query roles, and shareable or restorable subset explicit without adding a global framework.

Step File Change
A.1 apps/platform/app/Filament/Pages/Monitoring/Operations.php, AuditLog.php, FindingExceptionsQueue.php, EvidenceOverview.php, BaselineCompareMatrix.php, BaselineCompareLanding.php Add or align explicit page-local contract declarations for contextual prefilter, active, draft, inspect, and shareable or restorable state, plus invalid-state fallback behavior
A.2 Existing page-local mount and hydrate methods Make first-mount precedence explicit between query input, session state, and page-local defaults
A.3 apps/platform/tests/Feature/Monitoring/MonitoringPageStateContractTest.php Add one cross-surface contract test that asserts every in-scope page exposes the expected state classes, query roles, shareable or restorable semantics, and Audit Log/Finding Exceptions inspect-vocabulary compatibility

Phase B — Standardize the simple monitoring pattern on Operations and Evidence Overview

Goal: Align the surfaces that should behave like query or session plus active-state pages, not special-case workbenches.

Step File Change
B.1 apps/platform/app/Filament/Pages/Monitoring/Operations.php Make requested dashboard prefilter, requested tenant scope, active tab, and persisted table state follow one deterministic precedence rule
B.2 apps/platform/app/Filament/Pages/Monitoring/EvidenceOverview.php Align query hydration, session persistence, clear-filter behavior, and shareable filter semantics with the shared simple monitoring contract
B.3 apps/platform/tests/Feature/Monitoring/OperationsDashboardDrillthroughTest.php, apps/platform/tests/Feature/Filament/TableStatePersistenceTest.php, and apps/platform/tests/Feature/Rbac/ActionSurfaceRbacSemanticsTest.php Cover query-first hydration, tenant-sensitive reset behavior, active tab restoration, persistence boundaries, and unauthorized requested-tenant fallback
B.4 apps/platform/tests/Feature/Evidence/EvidenceOverviewPageTest.php Extend multi-filter hydration, clear-filter, and refresh or reopen scenarios

Phase C — Unify inspect-state semantics on Audit Log and Finding Exceptions Queue

Goal: Ensure action-based inspect and deeplinked selected-record entry use one primary inspect model on each surface.

Step File Change
C.1 apps/platform/app/Filament/Pages/Monitoring/AuditLog.php and apps/platform/resources/views/filament/pages/monitoring/audit-log.blade.php Make selectedAuditLogId the only inspect contract, with consistent open, close, refresh, and invalid-selection fallback behavior
C.2 apps/platform/app/Filament/Pages/Monitoring/FindingExceptionsQueue.php and apps/platform/resources/views/filament/pages/monitoring/finding-exceptions-queue.blade.php Make selectedFindingExceptionId the only inspect and decision state, with summary and action lanes deriving from that single selection
C.3 apps/platform/resources/views/filament/pages/monitoring/partials/audit-log-inspect-event.blade.php Align inline inspect rendering with the single selected-event contract
C.4 apps/platform/tests/Feature/Monitoring/AuditLogInspectFlowTest.php, apps/platform/tests/Feature/Monitoring/FindingExceptionsQueueHierarchyTest.php, and apps/platform/tests/Feature/Rbac/ActionSurfaceRbacSemanticsTest.php Add positive and negative cases for query-selected inspect, invalid or unauthorized IDs, close-detail behavior, refresh behavior, selection clearing when filters no longer match, and compatible selected-record vocabulary across both surfaces

Phase D — Preserve the Compare Matrix special case while making it explicit

Goal: Keep Baseline Compare Matrix powerful while making draft, applied, focus, and launch-context semantics predictable.

Step File Change
D.1 apps/platform/app/Filament/Pages/BaselineCompareLanding.php and apps/platform/resources/views/filament/pages/baseline-compare-landing.blade.php Treat landing state as launch context only, and make matrix launch parameters explicit and non-competing
D.2 apps/platform/app/Filament/Pages/BaselineCompareMatrix.php and apps/platform/resources/views/filament/pages/baseline-compare-matrix.blade.php Make applied filter state, draft filter state, presentation mode, focus state, and shareable slices explicit; keep draft state local-only until apply
D.3 apps/platform/tests/Feature/Filament/BaselineCompareMatrixPageTest.php Extend draft-versus-applied, focus restoration, refresh behavior, and non-shareable draft discard coverage
D.4 apps/platform/tests/Feature/Filament/BaselineCompareLandingStartSurfaceTest.php and apps/platform/tests/Feature/Rbac/BaselineCompareMatrixAuthorizationTest.php Cover launch-context hydration, focus handoff, and invalid or unauthorized requested compare context

Phase E — Browser verification and closure documentation

Goal: Prove the contract through UI-level flows and leave a clear handoff boundary for shell-context concerns.

Step File Change
E.1 apps/platform/tests/Browser/Spec198MonitoringPageStateSmokeTest.php Add a focused browser smoke suite for deeplink, refresh, back, close-detail, and share behavior across the in-scope surfaces
E.2 Existing browser suites Reuse or extend Spec190BaselineCompareMatrixSmokeTest.php and Spec194GovernanceFrictionSmokeTest.php where they already cover relevant state flows
E.3 specs/198-monitoring-page-state/quickstart.md and final implementation notes Record the final state-class mapping, shareable/restorable subset, and any shell or context handoff that remains for Spec 199

Key Design Decisions

D-001 — Reuse current helpers and keep the contract page-local

CanonicalAdminTenantFilterState, CanonicalNavigationContext, and current page-local Livewire properties already cover most of the needed mechanics. The narrowest implementation is to make the contract explicit on the pages that already own the state.

The consistent precedence rule is: supported query or deeplink input hydrates first, session supplies only explicitly persisted table filter, search, or sort state, and page-local active state becomes authoritative after mount.

D-003 — Selected-record inspect is the authoritative inspect model on Audit Log and Finding Exceptions Queue

The inspect action, the selected summary, and the deeplinked selected ID must all express the same state. There must be no second inspect world beside the selected-record contract.

D-004 — Baseline Compare Matrix keeps explicit draft, applied, and focus slices

Compare Matrix already has a genuine draft/apply interaction model. The right move is to document and tighten it, not to flatten it into a direct-active table pattern.

D-005 — Regression protection belongs in focused tests, not a new runtime state engine

The contract is enforceable through page-local declarations and focused Pest coverage. That keeps the implementation surface small and avoids importing a new state registry or framework.

Risk Assessment

Risk Impact Likelihood Mitigation
Query and session precedence regressions change what first mount restores High Medium Make precedence explicit per page, extend query-param and table-persistence tests, and cover tenant-sensitive filter resets
Invalid deeplinks leave phantom selected state or stale action lanes High Medium Add invalid-ID fallback rules and test them on Audit Log, Finding Exceptions Queue, and Baseline Compare launch context
Compare Matrix accidentally persists or shares draft state High Medium Keep draft and applied state separate in code and tests, and assert that shared links restore only applied and focused state
Shell-context concerns leak into Spec 198 and widen scope Medium Medium Keep workspace or tenant shell concerns documented only as handoff items for Spec 199
Implementation drifts into a generic page-state framework Medium Medium Keep helpers page-local by default and extract only a tiny shared helper if duplication across multiple pages proves it necessary during implementation

Test Strategy

  • Extend Operations, Audit Log, Finding Exceptions Queue, Evidence Overview, Baseline Compare Landing, Baseline Compare Matrix, and TableStatePersistenceTest with deterministic query, session, refresh, back, and restore coverage.
  • Add one focused cross-surface contract test for the explicit page-state declarations on the in-scope pages.
  • Reuse current RBAC suites where requested state can become unauthorized or cross-tenant.
  • Add one browser smoke suite for the top user journeys: Operations deeplink and refresh, Audit Log selected-event open and close, Finding Exceptions selected-exception review state, Evidence Overview filter clear behavior, and Baseline Compare draft/apply plus focus restoration.
  • Run focused verification through Sail and format only touched files with Pint.