## Summary - amend the operator UI constitution and related SpecKit templates for the new UI/UX governance rules - add Spec 168 artifacts plus the tenant governance aggregate implementation used by the tenant dashboard, banner, and baseline compare landing surfaces - normalize Filament action surfaces around clickable-row inspection, grouped secondary actions, and explicit action-surface declarations across enrolled resources and pages - fix post-suite regressions in membership cache priming, finding workflow state refresh, tenant review derived-state invalidation, and tenant-bound backup-set related navigation ## Commit Series - `docs: amend operator UI constitution` - `spec: add tenant governance aggregate contract` - `feat: add tenant governance aggregate contract` - `refactor: normalize filament action surfaces` - `fix: resolve post-suite state regressions` ## Testing - `vendor/bin/sail artisan test --compact` - Result: `3176 passed, 8 skipped (17384 assertions)` ## Notes - Livewire v4 / Filament v5 stack remains unchanged - no provider registration changes; `bootstrap/providers.php` remains the relevant location - no new global-search resources or asset-registration changes in this branch Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de> Reviewed-on: #199
5.3 KiB
Phase 0 Research: Tenant Governance Aggregate Contract
Decision: Build the shared contract from BaselineCompareStats and its summary assessment instead of creating a second query-backed summary path
Rationale: BaselineCompareStats::forTenant() already resolves baseline assignment, consumable snapshot availability, latest compare-run posture, findings visibility counts, and the governance-attention count family that this spec needs. NeedsAttention is the main surface still re-querying overdue, expiring, lapsed, and high-severity finding counts locally even though those values are already derivable from compare stats. Building the aggregate from BaselineCompareStats keeps one query-backed truth and avoids a second summary service that would drift.
Alternatives considered:
- Create a new persisted tenant-governance summary record: rejected because the current-release problem is request-time consistency, not independent lifecycle or historical reporting truth.
- Create a new query service that bypasses
BaselineCompareStats: rejected because it would duplicate compare availability and attention logic that already exists.
Decision: Introduce one narrow TenantGovernanceAggregate runtime contract instead of continuing to treat BaselineCompareStats as an implicit shared summary
Rationale: BaselineCompareStats is a broad compare-detail object that also carries diagnostics and landing-specific support data. The operator problem is narrower: multiple surfaces need one shared summary posture, count family, and next-action intent. A dedicated aggregate keeps that summary ownership explicit while allowing the landing page to keep deeper diagnostics in BaselineCompareStats.
Alternatives considered:
- Use
BaselineCompareStatsdirectly everywhere with no new contract: rejected because it would leave summary ownership implicit and make it easier for widgets to keep local business rules. - Move every compare diagnostic into the new aggregate: rejected because it would bloat a summary contract into a second detail model.
Decision: Reuse the existing request-scoped derived-state infrastructure from Spec 167
Rationale: The repo already binds RequestScopedDerivedStateStore in the Laravel container and uses it for request-local reuse of ArtifactTruthPresenter, OperationUxPresenter, and RelatedNavigationResolver. This feature needs the same boundary: one request-local derived result per deterministic question, explicit invalidation rules, and a guard path that prevents local ad hoc caches from coming back.
Alternatives considered:
- Add a static cache inside
NeedsAttention,BaselineCompareNow, orBaselineCompareCoverageBanner: rejected because Spec 167 explicitly moved the codebase away from that pattern. - Use
Cache::remember()or another cross-request store: rejected because the spec requires request-local reuse only and stale cross-request posture would be harder to reason about.
Decision: Keep next-action intent inside the aggregate, but keep final URLs and panel-specific drill-down mapping local to surfaces
Rationale: The semantic drift problem is not only conflicting counts; it is also conflicting operator follow-up. The aggregate must therefore answer the stable business question “what is the next action target category for this tenant state?” while leaving local surfaces free to map that answer to tenant-panel URLs, run links, or findings links appropriate to their context.
Alternatives considered:
- Store absolute URLs inside the aggregate: rejected because final URLs remain panel-aware and surface-local, and capability-gated destinations should stay local to the consuming surface.
- Leave next-action computation local to every widget or page: rejected because next-action drift is part of the problem this spec is supposed to solve.
Decision: Keep BaselineCompareLanding as the home for diagnostics, but move its default-visible posture semantics onto the aggregate
Rationale: The spec explicitly distinguishes default-visible operator posture from diagnostics-only information. BaselineCompareLanding should still own evidence gaps, duplicate-name diagnostics, detailed compare context, and the existing Compare now action. What changes is that its primary posture zone should no longer be another local semantic owner.
Alternatives considered:
- Leave the landing page fully outside the aggregate: rejected because the landing page is one of the authoritative summary surfaces listed in the spec.
- Collapse the landing page into a pure aggregate view: rejected because operators still need compare diagnostics there.
Decision: Protect the feature with parity, memoization, and guard tests rather than ad hoc performance scripts
Rationale: The highest-risk regressions are semantic drift, hidden local re-queries, and request-scope leakage. Focused Pest tests can assert those business truths directly, and the derived-state adoption guard can stop future surfaces from quietly reintroducing local caches or unsupported family access patterns.
Alternatives considered:
- Rely only on query counting or manual profiling: rejected because it would miss operator-visible drift and consumer-adoption regressions.
- Treat this as a pure unit-test problem: rejected because the spec is about cross-surface agreement and request behavior, which requires feature-level coverage too.