# 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 `BaselineCompareLanding` page 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 `CanonicalNavigationContext` already 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.