## Summary - add a workspace-scoped baseline compare matrix page under baseline profiles - derive matrix tenant summaries, subject rows, cell states, freshness, and trust from existing snapshots, compare runs, and findings - add confirmation-gated `Compare assigned tenants` actions on the baseline detail and matrix surfaces without introducing a workspace umbrella run - preserve matrix navigation context into tenant compare and finding drilldowns and add centralized matrix badge semantics - include spec, plan, data model, contracts, quickstart, tasks, and focused feature/browser coverage for Spec 190 ## Verification - `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Unit/Badges/BaselineCompareMatrixBadgesTest.php tests/Feature/Baselines/BaselineCompareMatrixBuilderTest.php tests/Feature/Baselines/BaselineCompareMatrixCompareAllActionTest.php tests/Feature/Baselines/BaselineComparePerformanceGuardTest.php tests/Feature/Filament/BaselineCompareMatrixPageTest.php tests/Feature/Filament/BaselineProfileCompareStartSurfaceTest.php tests/Feature/Rbac/BaselineCompareMatrixAuthorizationTest.php tests/Feature/Guards/ActionSurfaceContractTest.php tests/Feature/Guards/NoAdHocStatusBadgesTest.php tests/Feature/Guards/NoDiagnosticWarningBadgesTest.php` - `cd apps/platform && ./vendor/bin/sail bin pint --dirty --format agent` - completed an integrated-browser smoke flow locally for matrix render, differ filter, finding drilldown round-trip, and `Compare assigned tenants` confirmation/action Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de> Reviewed-on: #221
7.3 KiB
Research: Workspace Baseline Compare Matrix V1
Decision: Build the matrix as a baseline-scoped workspace page under the existing baseline profile resource
Rationale
The existing baseline workflow is already anchored on BaselineProfileResource and ViewBaselineProfile. A matrix is meaningful only in the context of one explicit baseline reference, so the narrowest route is a child workspace page such as /admin/baseline-profiles/{record}/compare-matrix. This keeps the baseline profile as the visible standard, preserves existing navigation expectations, and avoids introducing a new top-level workspace workbench.
Alternatives considered
- Add a separate top-level workspace navigation item: rejected because it weakens the explicit baseline reference and creates a second entry pattern for the same workflow.
- Extend the existing tenant
BaselineCompareLandingpage to become cross-tenant: rejected because that page is tenant-scoped by design and would blur tenant and workspace concerns.
Decision: Derive matrix truth from BaselineSnapshotItem plus the latest relevant tenant compare run plus compare-created findings
Rationale
No single existing artifact is sufficient by itself. BaselineSnapshotItem defines the immutable subject row axis and reference truth. The latest baseline_compare OperationRun provides freshness, coverage, evidence-gap, and trust metadata. Findings created with source = baseline.compare provide durable per-subject technical drift detail and severity. Combining those existing sources yields matrix rows, columns, summaries, and drilldowns without adding a second persisted compare model.
Alternatives considered
- Use findings alone: rejected because findings do not represent matches, do not fully encode no-result or stale truth, and can be filtered by workflow status in ways that would understate current technical deviation.
- Use run context alone: rejected because findings remain the durable subject-level drilldown truth and severity carrier across compare executions.
- Persist a new cross-tenant compare report: rejected because V1 can be derived live from existing truth.
Decision: Treat technical deviation state as primary and finding workflow state as secondary metadata
Rationale
The matrix must answer whether a tenant currently differs from the baseline, not whether someone already triaged the resulting finding. A current compare may still indicate drift even if a related finding is acknowledged, risk accepted, or closed. Therefore cell and summary counts must derive from the latest compare truth for the selected baseline and visible tenant, while any finding workflow state remains optional secondary metadata for drilldown or badges.
Alternatives considered
- Count only open findings: rejected because terminal workflow states can hide still-present technical drift.
- Ignore findings entirely: rejected because the operator still needs severity and existing finding drilldown continuity.
Decision: Reuse existing stale-result semantics and reference-snapshot freshness rules
Rationale
The repo already defines baseline compare staleness in BaselineCompareSummaryAssessor, including a seven-day age threshold and stale_result evidence semantics. The matrix should reuse that rule and additionally mark results stale when the latest completed compare predates the current effective baseline snapshot. This keeps tenant and workspace compare freshness language aligned.
Alternatives considered
- Invent a matrix-specific freshness threshold: rejected because it would create a parallel freshness language.
- Use only elapsed time: rejected because a result can become stale immediately when the baseline reference snapshot changes.
Decision: Reuse evidence-gap and operator-explanation metadata for trust and ambiguity signals
Rationale
baseline_compare run context already records evidence gaps, uncovered policy types, and related diagnostics. BaselineCompareExplanationRegistry and the existing trustworthiness semantics already distinguish trustworthy, limited-confidence, diagnostic-only, and unusable result conditions. The matrix should reuse those signals at tenant and subject level, while mapping per-cell ambiguity from evidence-gap reason codes such as ambiguous or low-confidence match situations.
Alternatives considered
- Add a new trust or certainty taxonomy for the matrix: rejected because the compare domain already has operator-trust semantics.
- Hide uncertainty behind summary prose only: rejected because the spec explicitly forbids hidden ambiguity.
Decision: Use existing CanonicalNavigationContext patterns for matrix drilldown continuity
Rationale
The codebase already uses CanonicalNavigationContext, RelatedNavigationResolver, and OperationRunLinks to preserve source context and return paths across related surfaces. The matrix only needs source baseline, source subject, and return path continuity. Reusing canonical navigation context is narrower than extending PortfolioArrivalContext, which is currently specialized for backup-health and recovery-evidence triage families.
Alternatives considered
- Reuse
PortfolioArrivalContext: rejected because the matrix is not a backup or recovery concern-family workflow and would pollute that token's allowlist and semantics. - Introduce a brand-new context token type: rejected because
CanonicalNavigationContextalready solves the generic return-path problem.
Decision: Implement Compare assigned tenants as visible-tenant fan-out over existing tenant compare starts, without a workspace umbrella run
Rationale
BaselineCompareService::startCompare() and tenant-scoped OperationRun creation already implement canonical baseline compare start semantics, deduplication, queued feedback, and run observability. V1 compare-all should iterate the visible assigned tenant set and reuse those existing starts. Aggregate progress on the matrix page can then be derived from the underlying tenant runs. This avoids inventing a second run identity or a workspace-level shadow batch truth.
Alternatives considered
- Create one workspace umbrella
OperationRun: rejected because the spec explicitly avoids a new parallel run truth and the repo already treats tenant compare as tenant-owned execution. - Start compare directly in the page without reusing the service: rejected because it would duplicate preconditions, snapshot truth resolution, and dedupe behavior.
Decision: Cover the feature primarily with focused feature tests plus one browser smoke test
Rationale
The matrix combines query-heavy aggregation, RBAC filtering, and one new interactive page surface. Focused feature tests should verify aggregation correctness, compare-all launch behavior, and authorization semantics. One browser smoke test is sufficient to prove the two-dimensional page and its core drilldown or action affordances render correctly under Livewire and Filament, without turning the spec into a browser-heavy suite.
Alternatives considered
- Browser-test the full feature exhaustively: rejected because most business truth can be validated faster and more deterministically through feature tests.
- Limit tests to one page smoke test: rejected because the feature's core risks are incorrect aggregation, stale or ambiguous truth, and hidden-tenant leakage.