## Summary - align tenant dashboard KPI, attention, compare, and operations truth so the page does not read calmer than the tenant's actual state - preserve tenant-safe drill-through continuity into findings, baseline compare, and canonical operations, including disabled helper states for permission-limited members - add the Spec 173 artifact set and focused regression coverage for dashboard truth alignment and drill-through behavior ## Validation - `vendor/bin/sail bin pint --dirty --format agent` - `vendor/bin/sail artisan test --compact tests/Feature/Filament/DashboardKpisWidgetTest.php tests/Feature/Filament/TenantDashboardTruthAlignmentTest.php tests/Feature/Monitoring/OperationsDashboardDrillthroughTest.php tests/Feature/Filament/NeedsAttentionWidgetTest.php tests/Feature/Filament/BaselineCompareNowWidgetTest.php tests/Feature/Filament/BaselineCompareSummaryConsistencyTest.php tests/Feature/Findings/FindingsListDefaultsTest.php tests/Feature/Findings/FindingsListFiltersTest.php tests/Feature/Findings/FindingAdminTenantParityTest.php tests/Feature/OpsUx/CanonicalViewRunLinksTest.php tests/Feature/Filament/TenantDashboardTenantScopeTest.php tests/Feature/Filament/TenantDashboardDbOnlyTest.php tests/Feature/Filament/TableStandardsBaselineTest.php tests/Feature/Filament/TableDetailVisibilityTest.php` - integrated browser smoke on the tenant dashboard, including a permission-limited member scenario Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de> Reviewed-on: #204
7.7 KiB
Phase 0 Research: Tenant Dashboard KPI & Attention Truth Alignment
Decision: Reuse the existing TenantGovernanceAggregate and compare summary path instead of creating a new dashboard aggregate
Rationale: NeedsAttention and BaselineCompareNow already depend on TenantGovernanceAggregate, which itself is derived from BaselineCompareStats and BaselineCompareSummaryAssessor. The tenant dashboard truth problem is not missing data; it is divergent interpretation and drill-through behavior across existing widgets. Extending the current aggregate-backed guard family is narrower than inventing a new dashboard-specific summary service.
Alternatives considered:
- Create a new persisted tenant-dashboard summary record: rejected because the spec explicitly forbids new persistence and the problem is request-time semantics, not a new lifecycle truth.
- Create a second query-backed dashboard aggregate: rejected because it would split ownership away from the existing compare and governance summary path.
Decision: Treat Finding::openStatusesForQuery() and existing findings tabs and quick filters as the canonical active/open universe for dashboard continuity
Rationale: The tenant findings destination already defines Needs action and Overdue tabs using Finding::openStatusesForQuery(), and the findings list already exposes high_severity, overdue, and status-based quick filtering. These are the closest existing source of truth for what the operator should recognize after clicking a dashboard signal. Dashboard metrics should align to these semantics or be visibly renamed when intentionally narrower.
Alternatives considered:
- Keep dashboard-local status semantics and accept looser drill-through continuity: rejected because it preserves the trust gap the spec is trying to remove.
- Introduce a new dashboard-only filter vocabulary: rejected because it would add a second surface contract for the same findings universe.
Decision: Treat high severity at tenant-summary level as HIGH + CRITICAL active findings unless a metric is explicitly labeled as narrower
Rationale: BaselineCompareStats already counts highSeverityActiveFindingsCount using open statuses plus HIGH + CRITICAL, and the findings list high_severity quick filter uses the same severity set. DashboardKpis is the current outlier because it only counts SEVERITY_HIGH and STATUS_NEW. The narrowest fix is to align high severity language to the existing broader tenant-summary meaning and reserve narrower wording for explicitly new or drift-only subsets.
Alternatives considered:
- Downgrade every other surface to
SEVERITY_HIGHonly: rejected because it would discard an existing criticality distinction already present in the findings destination and aggregate. - Let
high severitycontinue to mean different things by widget: rejected because the label collision is part of the operator trust problem.
Decision: Make NeedsAttention directly actionable using existing tenant-safe destinations instead of leaving it as summary-only text
Rationale: The current widget already computes the right tenant-level problem families but only exposes summary text and, for compare posture, a nextStep label without navigation. The spec requires central attention states to become genuine start points. Existing findings, baseline compare, and operations destinations already exist and are the right follow-up surfaces.
Alternatives considered:
- Keep
NeedsAttentionnon-navigational and rely on adjacent widgets for follow-up: rejected because it leaves the primary attention surface incomplete. - Introduce a new dedicated attention page: rejected because the spec explicitly avoids new overview architecture.
Decision: Keep canonical Operations routes and push tenant-prefilter continuity into existing link and filter helpers
Rationale: /admin/operations and /admin/operations/{run} are already the canonical operations destinations. The current dashboard KPI uses a raw route('admin.operations.index'), which loses tenant context. OperationRunLinks and the Operations page already provide the right seam to carry tenant-aware filter or navigation context without multiplying route families.
Alternatives considered:
- Add tenant-specific operations pages under
/admin/t/{tenant}: rejected because the repo already standardized operations on canonical admin routes. - Leave dashboard operations drill-through unfiltered: rejected because it breaks the spec's drill-through continuity requirement.
Decision: Treat attention-worthy operations follow-up as failed, warning, or unusually long-running or stalled tenant runs
Rationale: Spec 173 distinguishes governance posture from operations activity. Healthy queued or running operations should remain visible as activity, but they must not be allowed to suppress or replace governance signals. Only runs whose current status or outcome indicates failure, warning, or unusually long-running or stalled execution should escalate into NeedsAttention follow-up.
Alternatives considered:
- Treat any active operation as attention-worthy: rejected because it would collapse activity and risk into one noisy signal.
- Ignore operations follow-up completely on the dashboard: rejected because failed or stalled tenant runs do require operator follow-up and are explicitly in scope.
Decision: Permission-limited members may see truth but must not get clickable dead-end drill-throughs
Rationale: The constitution allows visible disabled actions for in-scope members who lack capability, while the spec forbids dead-end drill-throughs. The narrowest consistent behavior is to keep dashboard truth visible but render the affordance as disabled or non-clickable with helper text instead of a clickable link that would only fail after navigation.
Alternatives considered:
- Hide the state entirely: rejected because it can make the dashboard look calmer than reality for an otherwise entitled tenant member.
- Allow the click and rely on a downstream
403: rejected because it violates the no-dead-end drill-through requirement and weakens operator trust.
Decision: Preserve RecentDriftFindings and RecentOperations as diagnostic recency surfaces rather than queue surfaces
Rationale: Both table widgets already use row-click inspection, default sort, and domain-specific empty states, and their queries intentionally include recent history without filtering to only active work. The spec calls out that recent surfaces can remain diagnostic if that role is explicit. Treating them as recency/context surfaces is narrower and avoids conflating history with posture.
Alternatives considered:
- Convert recent tables into tightly filtered actionable queues: rejected because it would expand the feature into a dashboard redesign and overlap with existing findings and operations destinations.
- Remove recent surfaces from the dashboard: rejected because the spec is about truth alignment, not surface removal.
Decision: Protect the feature with cross-widget parity and drill-through tests instead of one-off manual smoke checks only
Rationale: The highest-risk regressions are semantic: mismatched count universes, false calm, and tenant context loss on destinations. Focused Pest tests around widgets and route continuity can protect those business truths directly and stay aligned with the repo's existing Livewire-heavy testing style.
Alternatives considered:
- Rely on manual dashboard review only: rejected because subtle truth drift will recur.
- Add a broad browser-only suite: rejected because the core logic is already well served by focused Livewire and feature tests, with existing browser coverage reserved for route and smoke scenarios where needed.