# Feature Specification: Workspace Baseline Compare Matrix V1

**Feature Branch**: `190-baseline-compare-matrix`  
**Created**: 2026-04-11  
**Status**: Draft  
**Input**: User description: "Spec 190 - Workspace Baseline Compare Matrix V1"

## Spec Candidate Check *(mandatory — SPEC-GATE-001)*

- **Problem**: Workspace operators can define baselines, assign tenants, and review tenant-level compare truth, but they still cannot see one workspace-scoped picture of how all assigned visible tenants deviate from the same baseline standard.
- **Today's failure**: Portfolio governance remains tenant-by-tenant. Operators must open tenants individually, cannot rank the worst deviators quickly, cannot see which subjects drift across many tenants, and can misread stale, absent, or ambiguous compare truth as calm.
- **User-visible improvement**: One workspace matrix shows the reference baseline, the visible assigned tenants, the breadth of deviation by tenant and subject, freshness, and trust, with direct drilldown into existing compare and finding surfaces.
- **Smallest enterprise-capable version**: A read-only workspace-scoped matrix for one `BaselineProfile` at a time, powered by existing snapshot, compare, finding, and `OperationRun` truth, plus one `Compare assigned tenants` action.
- **Explicit non-goals**: Promotion, rollout orchestration, approval, cross-workspace compare, persisted portfolio compare reports, manual match overrides, remediation actions, compliance mapping, exports, and a generalized standardization framework.
- **Permanent complexity imported**: One new workspace page, one derived aggregation shape for rows, columns, cells, and summaries, additional filter and sort semantics, compare-all orchestration over existing runs, drilldown continuity, and focused feature/browser regression coverage. No new persisted domain artifact is imported.
- **Why now**: The baseline, finding, `OperationRun`, and workspace portfolio foundations already exist. Without a workspace-level compare projection, a high-value MSP/operator workflow stays fragmented even though the underlying truth is already in the product.
- **Why not local**: Tenant-local compare and finding pages cannot answer cross-tenant questions such as "which visible tenant deviates most from this baseline right now?" or "which subject is recurring across the portfolio?" without repeated context switching and manual aggregation by the operator.
- **Approval class**: Core Enterprise
- **Red flags triggered**: `New Achsen` because a matrix can accidentally become a second truth layer, and `New Meta-Infrastructure` because cross-tenant aggregation can tempt a generalized compare platform. Defense: V1 is baseline-referenced, read-only, live-aggregated from existing truth, and explicitly forbids new persisted cross-tenant compare artifacts.
- **Score**: Nutzen: 2 | Dringlichkeit: 2 | Scope: 2 | Komplexitaet: 1 | Produktnaehe: 2 | Wiederverwendung: 2 | **Gesamt: 11/12**
- **Decision**: approve

## Spec Scope Fields *(mandatory)*

- **Scope**: workspace
- **Primary Routes**:
  - `/admin/baseline-profiles` as the existing workspace list and selection entry for baseline standards
  - `/admin/baseline-profiles/{record}` as the existing baseline profile detail that gains the canonical matrix entry action
  - `/admin/baseline-profiles/{record}/compare-matrix` as the new canonical workspace route for one baseline compare matrix
  - `/admin/t/{tenant}/baseline-compare` as the existing tenant drilldown landing
  - `/admin/findings` plus finding detail as existing finding drilldown surfaces
  - Monitoring -> Operation Run Detail for compare-all follow-up and tenant compare run truth
- **Data Ownership**:
  - Workspace-owned reference truth remains `BaselineProfile`, `BaselineSnapshot`, `BaselineSnapshotItem`, and baseline-to-tenant assignment records.
  - Tenant-owned compare truth remains the existing tenant compare outputs, drift findings, and `OperationRun` rows created for compare execution.
  - The matrix itself is a read-only workspace projection over visible assigned tenants and does not persist a new cross-tenant compare result, report, or standardization artifact.
- **RBAC**:
  - Workspace membership plus `WORKSPACE_BASELINES_VIEW` is required to open the matrix and inspect workspace baseline reference truth.
  - `WORKSPACE_BASELINES_MANAGE` is required to start `Compare assigned tenants`.
  - In-scope members who can view the baseline but lack `WORKSPACE_BASELINES_MANAGE` still see `Compare assigned tenants` on the baseline detail and matrix surfaces in a disabled state with helper text; forced execution remains `403`.
  - Matrix columns are limited to tenants the actor may already see under existing workspace and tenant visibility rules; drilldowns continue to enforce their destination capabilities such as `TENANT_VIEW` and `TENANT_FINDINGS_VIEW`.
  - Non-members of the workspace or a tenant scope remain `404`; in-scope members missing the required capability remain `403`.

## UI/UX Surface Classification *(mandatory when operator-facing surfaces are changed)*

| Surface | Surface Type | Primary Inspect/Open Model | Row Click | Secondary Actions Placement | Destructive Actions Placement | Canonical Collection Route | Canonical Detail Route | Scope Signals | Canonical Noun | Critical Truth Visible by Default | Exception Type |
|---|---|---|---|---|---|---|---|---|---|---|---|
| Baseline profile detail | Detail / action-launch surface | Explicit `Open compare matrix` header action | forbidden | Detail header actions | Existing archive remains in detail header | `/admin/baseline-profiles` | `/admin/baseline-profiles/{record}` | Active workspace, baseline status, assignment count, reference snapshot truth | Baseline profile | Whether this baseline is compare-ready and how many tenants are assigned | none |
| Workspace baseline compare matrix | Workspace matrix / report surface | Explicit tenant, subject, and cell drilldowns | forbidden | Header toolbar, summary strips, and focused in-matrix controls | none | `/admin/baseline-profiles/{record}/compare-matrix` | Same route with focused tenant or subject state, plus existing drilldowns to tenant compare or finding surfaces | Active workspace, selected baseline profile, reference snapshot, visible tenant count, filter scope, freshness legend | Baseline compare matrix | Tenant deviation breadth, subject breadth, freshness, ambiguity, and not-compared truth | matrix-grid surface |
| Tenant compare and finding drilldowns from the matrix | Existing detail/list drilldown surfaces | Explicit matrix drilldown links only | forbidden | Existing local actions remain in their current placements | Existing destructive or lifecycle actions remain where already defined | `/admin/t/{tenant}/baseline-compare` and `/admin/findings` | Existing tenant compare and finding detail routes | Tenant context, source baseline profile, source subject focus, arrival source | Baseline compare / Finding | Why the operator drilled from the matrix and what subject or deviation is being followed up | canonical-navigation extension |

## Operator Surface Contract *(mandatory when operator-facing surfaces are changed)*

| Surface | Primary Persona | Surface Type | Primary Operator Question | Default-visible Information | Diagnostics-only Information | Status Dimensions Used | Mutation Scope | Primary Actions | Dangerous Actions |
|---|---|---|---|---|---|---|---|---|---|
| Baseline profile detail | Workspace manager | Detail / launch surface | Is this the right reference baseline, and should I open or refresh its matrix now? | Profile name and status, effective snapshot, assignment count, matrix entry, last compare coverage summary | Snapshot capture history, run diagnostics, low-level compare details | baseline lifecycle, snapshot completeness, visible assignment coverage | `TenantPilot only` for profile edits, `simulation only` for compare starts | Open compare matrix, Compare assigned tenants, Capture baseline | Archive baseline profile |
| Workspace baseline compare matrix | Workspace operator | Workspace matrix / drilldown hub | Which visible assigned tenants diverge from this baseline, how fresh and trustworthy is that truth, and where should I drill next? | Reference baseline, reference snapshot, visible-vs-assigned counts, per-tenant summaries, per-subject summaries, cell states, freshness legend, trust legend | Matching method detail, evidence-gap reasons, run identifiers, raw subject keys | compare result state, freshness, trust or ambiguity, visible severity breadth | `simulation only` for compare starts; otherwise read-only | Compare assigned tenants, filter matrix, focus subject, open tenant compare, open finding follow-up | none |
| Tenant compare and finding drilldowns from the matrix | Workspace operator continuing investigation | Existing follow-up surface | What exactly is wrong for this tenant and subject, and what existing workflow should I use next? | Tenant context, baseline context, subject context, current compare or finding truth, return path | Raw evidence payloads, underlying run payload, low-level diagnostic detail | tenant compare readiness, finding workflow state, drift severity, evidence completeness | Existing destination-specific mutation scope only | View compare context, view finding context, return to matrix | Existing destination-specific dangerous actions only |

## Proportionality Review *(mandatory when structural complexity is introduced)*

- **New source of truth?**: no
- **New persisted entity/table/artifact?**: no
- **New abstraction?**: yes, one narrow request-scoped matrix builder plus one centralized badge adapter for matrix state semantics
- **New enum/state/reason family?**: no
- **New cross-domain UI framework/taxonomy?**: no
- **Current operator problem**: The product has strong tenant-level baseline compare truth but still lacks a portfolio answer to "who deviates most from this baseline right now?"
- **Existing structure is insufficient because**: Existing tenant compare and finding surfaces are tenant-first or item-first. They cannot show visible-set deviation breadth, freshness, and trust across all assigned tenants without forcing the operator to aggregate manually.
- **Narrowest correct implementation**: One baseline-scoped matrix page, one narrow request-scoped matrix builder, one centralized badge adapter that reuses canonical compare truth, plus one batch compare trigger that reuses current snapshots, compare outputs, findings, run truth, and drilldown surfaces.
- **Ownership cost**: One new page, one narrow derived builder, one centralized badge adapter, derived aggregation queries, one narrow matrix-grid presentation exception, additional filter/sort coverage, and focused feature/browser regression tests.
- **Alternative intentionally rejected**: A new `CrossTenantCompare`, stored report model, generalized tenant-vs-tenant compare engine, promotion workflow, or broader standardization platform.
- **Release truth**: current-release portfolio governance visibility

## User Scenarios & Testing *(mandatory)*

### User Story 1 - Scan visible drift across assigned tenants (Priority: P1)

As a workspace operator, I want one matrix for one baseline profile so I can see which visible assigned tenants and subjects deviate most without opening tenants one by one.

**Why this priority**: This is the core product outcome. If the matrix cannot answer the portfolio question directly, the feature misses its reason to exist.

**Independent Test**: Open the matrix for a baseline profile with mixed compare outcomes and verify that the reference baseline, visible tenant set, per-tenant summaries, per-subject summaries, and cell states are all understandable from one page.

**Acceptance Scenarios**:

1. **Given** a usable baseline reference and multiple visible assigned tenants with existing compare outcomes, **When** the operator opens the compare matrix, **Then** the page shows one column per visible assigned tenant, one row per baseline subject, and truthful cell states for match, differ, missing, ambiguous, stale, or not compared.
2. **Given** the actor can see only a subset of assigned tenants, **When** the matrix opens, **Then** only visible tenants appear and every count and summary stays scoped to that visible set.

---

### User Story 2 - Refresh compare truth for the visible assigned set (Priority: P1)

As a workspace operator, I want to compare all visible assigned tenants in one action so I can refresh portfolio truth without visiting each tenant.

**Why this priority**: The matrix becomes operationally useful only if an authorized operator can refresh stale portfolio truth from the same workspace surface.

**Independent Test**: Start `Compare assigned tenants` from the baseline detail or matrix page and verify that the product reuses normal compare execution semantics, shows honest run progress, and reflects partial completion accurately.

**Acceptance Scenarios**:

1. **Given** an authorized operator and visible assigned tenants eligible for compare, **When** the operator confirms `Compare assigned tenants`, **Then** the system starts or reuses normal tenant compare execution for each eligible visible tenant and returns the operator to honest queued or running matrix truth.
2. **Given** some visible assigned tenants succeed and others fail or remain running, **When** the matrix refreshes, **Then** the page distinguishes completed, running, failed, stale, and never-compared tenants without collapsing them into a single calm state.

---

### User Story 3 - Drill from the matrix into existing follow-up surfaces (Priority: P2)

As a workspace operator, I want cell, tenant, and subject drilldowns so I can move from the portfolio view into existing compare or finding workflows without reconstructing context.

**Why this priority**: The matrix must be a decision surface, not a dead-end report.

**Independent Test**: Open a differing, missing, or ambiguous cell and confirm the drilldown lands in an existing tenant compare or finding surface with the tenant, subject, and baseline context still understandable.

**Acceptance Scenarios**:

1. **Given** a differing or missing matrix cell, **When** the operator opens its drilldown, **Then** the product lands in an existing tenant compare or finding context for that tenant and subject with the baseline reference still clear.
2. **Given** a subject row that deviates across several visible tenants, **When** the operator focuses that subject, **Then** the product shows the subject-first picture across the visible tenant set without inventing a new persisted subject report.

---

### User Story 4 - Stay honest in degraded or low-trust conditions (Priority: P2)

As a workspace operator, I want empty, partial, stale, and ambiguous states to stay explicit so the matrix never reads as healthier than the underlying truth.

**Why this priority**: False calmness would be worse than having no matrix at all.

**Independent Test**: Open the matrix for a baseline profile with no usable snapshot, no prior compare runs, stale results, and ambiguous matches, and verify that each degraded condition is visibly distinct from a normal match state.

**Acceptance Scenarios**:

1. **Given** the baseline profile has no usable reference snapshot, **When** the operator opens the matrix, **Then** the page does not imply compare truth exists and instead shows one clear next-step call to action.
2. **Given** compare truth is ambiguous, stale, or absent, **When** the matrix renders, **Then** those cells and summaries remain visibly distinct from matches and point the operator toward the right next compare or drilldown action.

### Edge Cases

- A baseline profile has assigned tenants but no usable reference snapshot; the matrix must block compare interpretation instead of rendering empty matches.
- A baseline profile has assigned tenants but the current actor can see none of them; the page must explain visible-set scoping without leaking hidden tenant counts or names.
- A visible assigned tenant has never been compared against this baseline; the cell and tenant summary must read as `Not compared` rather than healthy.
- A visible tenant has compare results against an older baseline reference than the current one; the result must read as stale rather than current.
- Subject identity is ambiguous for one or more tenants; the matrix must show ambiguity instead of forcing a match or differ conclusion.
- `Compare assigned tenants` starts successfully for some visible tenants but not others; the surface must stay honest about partial start and partial completion.
- One policy type has high trust while another has low trust; filtering by policy type must preserve the correct trust explanation for the visible slice.

## Requirements *(mandatory)*

**Constitution alignment (required):** This feature adds one workspace-scoped operator surface and reuses existing compare execution. It introduces no new Microsoft Graph contract path, no new write workflow beyond compare start, and no new source of baseline or finding truth. `Compare assigned tenants` remains a simulation-only governance action that must respect confirmation, tenant isolation, auditability through existing run truth, and focused regression coverage.

**Constitution alignment (PROP-001 / ABSTR-001 / PERSIST-001 / STATE-001 / BLOAT-001):** The feature stays within the default bias of deriving before persisting. The matrix is a thin derived projection over existing baseline snapshots, compare outputs, findings, and run context. V1 must not create new persistence, a generalized compare framework, or a new persisted state family just to support the matrix.

**Constitution alignment (OPS-UX):** `Compare assigned tenants` reuses existing `baseline_compare` run semantics only. Toast feedback remains intent-only, progress remains on active-ops and run-detail surfaces, and terminal notification behavior remains initiator-aware. `OperationRun.status` and `OperationRun.outcome` remain service-owned via `OperationRunService`. Any summary counts written to runs remain numeric-only and must use canonical summary keys. Scheduled or system-run semantics stay unchanged: no initiator means no terminal DB notification, and Monitoring remains the audit surface. Regression coverage must prove that compare-all does not invent shadow statuses or a second batch truth.

**Constitution alignment (RBAC-UX):** This feature affects the workspace-admin plane on `/admin/baseline-profiles` and the tenant follow-up plane on `/admin/t/{tenant}/baseline-compare`, plus existing finding drilldowns. Cross-plane access remains deny-as-not-found. Non-members of the workspace or tenant scope receive `404`. In-scope members missing `WORKSPACE_BASELINES_VIEW`, `WORKSPACE_BASELINES_MANAGE`, `TENANT_VIEW`, or `TENANT_FINDINGS_VIEW` as required by the destination surface receive `403`. Server-side enforcement remains mandatory for matrix view, compare start, and every drilldown. Global search behavior stays unchanged; the matrix itself is not introduced as a separate global-search result.

**Constitution alignment (OPS-EX-AUTH-001):** Not applicable beyond reaffirming that this feature does not introduce auth-handshake HTTP behavior.

**Constitution alignment (BADGE-001):** Any new matrix legends, badges, or label mappings for match, differ, missing, ambiguous, not compared, stale result, freshness, or trust must stay centralized and derived from canonical compare truth. No page-local color language may redefine those meanings. Tests must cover any new or changed centralized mappings.

**Constitution alignment (UI-FIL-001):** The feature should use a Filament page, Filament header actions, sections, stats, filters, and shared badge or alert primitives wherever possible. The tenant-by-subject grid itself may require custom Blade markup because standard one-axis tables do not represent two-dimensional matrix inspection well enough; this is the only approved exception. Even with that exception, buttons, alerts, legends, empty states, and state badges must still use shared primitives and central semantics rather than page-local styling rules.

**Constitution alignment (UI-NAMING-001):** Operator-facing vocabulary must stay consistent across the matrix, baseline detail, run titles, and notifications: `Baseline compare matrix`, `Compare assigned tenants`, `Reference snapshot`, `Visible tenants`, `Match`, `Differs`, `Missing`, `Ambiguous match`, `Not compared`, and `Stale result`. Internal implementation terms such as `cross-tenant compare engine`, `matrix resolver`, or `portfolio deviation artifact` must stay out of primary labels.

**Constitution alignment (UI-CONST-001 / UI-SURF-001 / UI-HARD-001 / UI-EX-001 / UI-REVIEW-001):** The new surface is classified as a workspace matrix/report because the operator task is cross-tenant inspection, not CRUD. It has one primary inspect model: explicit tenant, subject, and cell drilldowns. Row click is forbidden because matrix rows and columns each represent different drilldown intents. Secondary actions live in the page header and matrix summaries. The matrix has no destructive actions. Its canonical collection route and canonical detail route are the same matrix route with focused state, while deeper investigation moves into existing tenant compare or finding routes. Scope signals must show active workspace, selected baseline profile, reference snapshot, visible-vs-assigned counts, and filter state. The critical truth visible by default is deviation breadth plus freshness and trust for the visible tenant set.

**Constitution alignment (OPSURF-001):** Default-visible matrix content must remain operator-first: baseline reference, visible tenant set, per-tenant deviation summary, per-subject breadth, freshness, and low-trust signals. Diagnostics such as raw subject keys, matching mechanics, and run identifiers must stay secondary. Status dimensions must remain separate: compare result state, freshness, trust or ambiguity, and visible severity breadth. `Compare assigned tenants` must communicate `simulation only` before execution. Workspace and tenant context must remain explicit in the matrix, the drilldowns, and the return path.

**Constitution alignment (UI-SEM-001 / LAYER-001 / TEST-TRUTH-001):** Direct exposure of tenant compare truth alone is insufficient because the operator needs a visible-set portfolio decision surface. The solution must stay a thin derived aggregation layer rather than a second truth system. Tests must focus on business consequences such as wrong visible-set counts, stale or absent truth being mistaken for calm, ambiguous matches being hidden, and drilldowns losing context.

**Constitution alignment (Filament Action Surfaces):** The Action Surface Contract remains satisfied. `BaselineProfileResource` keeps its existing primary inspect model and adds `Open compare matrix` plus `Compare assigned tenants` in the detail header. The new matrix page has no row-click primary open, no redundant `View` action, no empty action groups, and no destructive actions. UI-FIL-001 remains satisfied with the narrow matrix-grid exception described above.

**Constitution alignment (UX-001 — Layout & Information Architecture):** The new matrix page must use sections or cards for reference truth, summaries, filters, and the matrix body. Because the core surface is two-dimensional, the matrix body is a narrow UX-001 exemption from standard Filament table layout. That exemption does not remove the need for search/filter/sort over core dimensions, a specific empty-state title plus explanation plus exactly one call to action, or BADGE-001-compliant state markers. No new create or edit form is introduced.

### Functional Requirements

- **FR-190-001 Workspace entry surface**: The system MUST provide a new workspace-scoped compare matrix page for one baseline profile at a time, reachable from existing workspace baseline context and directly from the selected baseline profile detail.
- **FR-190-002 Reference truth visibility**: The matrix MUST identify the selected baseline profile, the profile status, the reference snapshot or effective baseline state in use, the total assigned tenants, and the currently visible tenant count.
- **FR-190-003 Explicit compare source truth**: The matrix MUST derive from one explicit baseline reference state. If no usable reference state exists, the page MUST not imply compare truth and MUST explain the blocked state.
- **FR-190-004 Visible target set**: Matrix columns MUST be limited to tenants assigned to the selected baseline profile in the same workspace that the current actor is allowed to see. Hidden tenants MUST NOT leak by name, count, or indirect summary.
- **FR-190-005 Subject identity reuse**: Matrix rows MUST reuse the existing baseline subject identity and matching strategy. V1 MUST NOT introduce a competing subject identity system.
- **FR-190-006 Cell truth states**: Each subject-by-tenant cell MUST resolve to a truthful visible outcome representing at least `Match`, `Differs`, `Missing`, `Ambiguous match`, `Not compared`, or `Stale result`, even if the final UI wording is slightly adjusted.
- **FR-190-007 No false calmness**: `Ambiguous match`, `Not compared`, `Stale result`, and insufficient-basis conditions MUST remain visibly distinct from `Match` and MUST NOT contribute to healthy counts as if they were matches.
- **FR-190-008 Per-tenant summary**: The page MUST show a summary for each visible tenant including compared subject count, differing count, missing count, ambiguous count, highest visible severity or attention level, and compare freshness.
- **FR-190-009 Per-subject summary**: The page MUST show a summary for each subject including deviation breadth across visible tenants, missing breadth, ambiguous breadth, and highest visible severity or attention level.
- **FR-190-010 Filtering**: The matrix MUST support filtering by policy type, state group at minimum (`All`, `Deviations only`, `Missing only`, `Ambiguous only`, and a stale/no-result view), and visible severity band.
- **FR-190-011 Sorting**: The matrix MUST support sorting by tenant name, tenant deviation count, tenant freshness urgency, and subject deviation breadth.
- **FR-190-012 Compare-all availability**: Authorized users MUST be able to start `Compare assigned tenants` from the matrix page and from the selected baseline profile detail when the baseline has a usable reference state.
- **FR-190-013 Compare-all execution model**: `Compare assigned tenants` MUST start or reuse normal tenant compare execution only for eligible visible assigned tenants and MUST NOT invent a second persisted batch truth or shadow status model.
- **FR-190-014 Compare-all honesty**: The matrix MUST surface queued, running, completed, failed, partial, stale, and never-compared truth from the underlying compare runs and findings without collapsing mixed outcomes into one success state.
- **FR-190-015 Freshness visibility**: The matrix MUST show when each visible tenant was last compared against the selected baseline reference, whether that result predates the current reference state, and whether the tenant has never been compared.
- **FR-190-016 Trust visibility**: The matrix MUST show the applicable matching or identity trust signal for low-confidence cells or subjects, including ambiguous matches and missing compare basis, without forcing raw technical diagnostics into the primary scan path.
- **FR-190-017 Cell drilldown**: A matrix cell with meaningful follow-up MUST open an existing tenant compare or finding surface that keeps the tenant, subject, and baseline context understandable on arrival.
- **FR-190-018 Tenant drilldown**: A tenant summary or header interaction MUST open the existing tenant baseline compare landing or equivalent tenant follow-up surface for that tenant under the selected baseline context.
- **FR-190-019 Subject focus**: A subject interaction MUST open or switch to a subject-focused view across the current visible tenant set without creating a new persisted subject report artifact.
- **FR-190-020 Existing-truth-first aggregation**: V1 MUST derive matrix cells, summaries, freshness, and trust from existing baseline snapshots, compare outputs, findings, inventory subject identity, and `OperationRun` context before considering any new stored projection.
- **FR-190-021 No new portfolio persistence**: V1 MUST NOT introduce a first-class `CrossTenantCompare`, `CrossTenantCompareResult`, `PortfolioDeviationReport`, or equivalent persisted artifact unless a later spec proves live aggregation insufficient.
- **FR-190-022 RBAC-safe degradation**: Whenever the actor can see only part of the assigned tenant set, every column, summary, filter total, and subject breadth count MUST be computed from the visible set only or explicitly labeled as visible-set-only.
- **FR-190-023 Empty and degraded states**: The page MUST provide explicit operator-readable states for no usable snapshot, no assigned tenants, no visible tenants, no compare results, partially complete compare coverage, and all-low-trust results.
- **FR-190-024 Read-only boundary**: V1 MUST remain read-only apart from starting compare execution. It MUST NOT add remediation, restore, promotion, exception, approval, or manual match-override actions to matrix cells or summaries.
- **FR-190-025 Centralized semantics**: Any new matrix-specific legends, labels, or badges MUST stay centrally mapped from existing canonical truth and MUST NOT create a new persisted status family.
- **FR-190-026 Automated coverage**: Automated coverage MUST verify the core matrix flow, compare-all, visible-set RBAC filtering, ambiguous matching, stale/no-result honesty, policy-type filtering, subject focus, drilldown continuity, degraded-state behavior, and query-bounded read shapes for wide visible tenant and subject sets.

## Non-Goals

- No policy promotion or push between tenants
- No rollout waves, rings, or fleet orchestration
- No approval workflow or accepted-deviation flow
- No cross-workspace compare
- No ad hoc tenant-A-vs-tenant-B compare
- No manual match override workflow
- No deep field-by-field compare editor
- No compare export or stored report artifact in V1
- No portfolio-level finding persistence beyond existing truth
- No automatic remediation

## Assumptions

- Existing `BaselineProfile`, `BaselineSnapshot`, tenant compare outputs, findings, and `OperationRun` truth remain the canonical building blocks for V1.
- One baseline profile is the reference frame for one matrix at a time.
- Existing baseline subject identity and matching strategy are already authoritative enough to support portfolio aggregation, as long as ambiguity stays explicit.
- `Compare assigned tenants` can fan out to existing tenant compare execution without requiring a second persisted batch artifact.
- Existing tenant compare and finding surfaces can accept bounded canonical navigation context so the operator can understand why they drilled from the matrix.

## Dependencies

- Existing workspace baseline resources and snapshot truth
- Existing tenant baseline compare landing and compare execution
- Existing finding generation, finding detail, and finding triage surfaces
- Existing `baseline_compare` `OperationRun` lifecycle and Monitoring surfaces
- Existing `CanonicalNavigationContext`, `RelatedNavigationResolver`, and return-path helpers for bounded matrix drilldown continuity

## Risks

- The matrix could expand into a generalized standardization platform unless V1 stays baseline-referenced and read-only.
- Low-trust subject matching could be over-read as certainty unless ambiguity stays highly visible.
- Workspace aggregation could leak hidden tenants unless every summary is computed from the visible set only.
- Very wide tenant or subject sets could reduce scanability unless filtering and subject focus stay first-class.
- Old compare results could be misread as current truth unless freshness stays explicit and prominent.

## Review Questions

- Does the matrix clearly show which baseline reference state it is using?
- Does V1 aggregate existing compare and finding truth instead of inventing a second compare system?
- Are stale, absent, and ambiguous states visibly distinct from matches?
- Are visible-set RBAC boundaries fully preserved in columns, summaries, and drilldowns?
- Does `Compare assigned tenants` stay consistent with existing `OperationRun` semantics?
- Does the matrix stay operator-first rather than becoming a technical dashboard?
- Does drilldown land in existing follow-up workflows with enough context preserved?
- Has the spec stayed read-only apart from compare start and avoided new persistence?

## Definition of Done

This feature is complete when:

- a workspace-scoped compare matrix exists for one selected baseline profile,
- the matrix uses existing baseline, compare, finding, and run truth rather than a new persisted portfolio artifact,
- visible tenants are strictly filtered by RBAC and hidden tenants do not leak through summaries,
- `Compare assigned tenants` is available to authorized operators and reuses normal compare execution,
- per-tenant deviation and freshness summary is visible,
- per-subject deviation breadth is visible,
- ambiguous, stale, and not-compared states are rendered honestly,
- drilldown to existing tenant compare or finding surfaces works with understandable context,
- no promotion, approval, remediation, or manual match-override logic has been introduced,
- feature and, where appropriate, browser tests cover the core operator flows.

## UI Action Matrix *(mandatory when Filament is changed)*

| Surface | Location | Header Actions | Inspect Affordance (List/Table) | Row Actions (max 2 visible) | Bulk Actions (grouped) | Empty-State CTA(s) | View Header Actions | Create/Edit Save+Cancel | Audit log? | Notes / Exemptions |
|---|---|---|---|---|---|---|---|---|---|---|
| Baseline profiles resource | `app/Filament/Resources/BaselineProfileResource.php` and `app/Filament/Resources/BaselineProfileResource/Pages/ViewBaselineProfile.php` | Existing list-header actions remain | Existing explicit `View` inspect affordance remains | Existing `View`; `Edit` and `Archive baseline profile` remain under `More` where already used | None | Existing create CTA remains | `Open compare matrix`, `Compare assigned tenants`, existing `Capture baseline`, `Edit`, and `Archive baseline profile` | Existing save/cancel unchanged | Yes | `Open compare matrix` requires `WORKSPACE_BASELINES_VIEW`. `Compare assigned tenants` requires `WORKSPACE_BASELINES_MANAGE`, is confirmation-gated, is `simulation only`, and remains visible-disabled with helper text for in-scope members who can view but cannot manage. `Archive baseline profile` remains the only destructive action and stays confirmation-gated. |
| Workspace baseline compare matrix page | New workspace page at `app/Filament/Pages/BaselineCompareMatrix.php` or equivalent | `Compare assigned tenants`, `Back to baseline profile` | Explicit tenant, subject, and cell drilldowns only; no row click | Inline `Focus subject` at most; all other follow-up opens use explicit drilldowns | None | One context-specific CTA such as `Capture baseline` or `Compare assigned tenants` depending on the blocked or empty state | Same as header actions | Not applicable | Yes through underlying compare runs | Action Surface Contract remains satisfied. The matrix is a narrow grid-surface exemption because a two-dimensional tenant-by-subject view is not a normal one-axis table. `Compare assigned tenants` remains visible-disabled with helper text for in-scope users missing manage capability, and forced execution still fails with `403`. No destructive actions are added. |
| Tenant compare and finding drilldowns from matrix context | `app/Filament/Pages/BaselineCompareLanding.php` and existing finding resource/detail surfaces | Existing destination actions remain in place | Existing destination inspect models remain in place | Existing destination row actions remain unchanged | Existing destination bulk actions remain unchanged | Existing destination empty-state behavior remains, but matrix arrival context must preserve return meaning | Existing destination view-header actions remain | Not applicable | Existing destination audit behavior remains | This spec reuses destination surfaces and adds bounded matrix-arrival context only. It does not add new destructive actions or competing inspect models there. |

### Key Entities *(include if feature involves data)*

- **Baseline compare matrix view**: The derived workspace projection for one selected baseline profile across visible assigned tenants and baseline subjects.
- **Compare target tenant**: A visible tenant assigned to the selected baseline profile whose existing compare truth can be refreshed or inspected.
- **Compare subject row**: The reusable baseline subject identity shown across the visible tenant set.
- **Matrix cell state**: The derived outcome for one subject and one visible tenant, including compare outcome, freshness, and trust.
- **Tenant freshness summary**: The per-tenant view of last compare time, deviation counts, and urgency against the selected baseline reference.

## Success Criteria *(mandatory)*

### Measurable Outcomes

- **SC-190-001**: In validation scenarios with a selected baseline profile and multiple visible assigned tenants, an operator can identify the most divergent visible tenant and the broadest recurring subject from the matrix without opening tenant detail pages first.
- **SC-190-002**: In acceptance coverage, 100% of visible tenants or cells with stale, absent, or ambiguous compare truth remain visually distinct from matched truth.
- **SC-190-003**: An authorized operator can start compare execution for the full visible assigned set in one action and the product shows honest queued, running, completed, or partial progress without inventing a separate batch status language.
- **SC-190-004**: In negative visibility coverage, matrix summaries and counts disclose no hidden tenant identity or hidden-tenant aggregate when the actor can see only part of the assigned set.
- **SC-190-005**: From a differing, missing, or ambiguous cell, the operator can reach an existing tenant-level follow-up surface in one drilldown step with enough preserved context to understand why they arrived there.
