## 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
11 KiB
Data Model: Workspace Baseline Compare Matrix V1
Overview
This feature introduces no new persisted entity. The matrix is a derived workspace-scoped read model over existing baseline reference truth, tenant compare execution truth, and compare-created finding truth.
Existing Source Truths
Baseline reference truth
Types: Existing workspace-owned source of truth
Sources: BaselineProfile, BaselineSnapshot, BaselineSnapshotTruthResolver, BaselineSnapshotItem
| Source | Purpose | Key Fields |
|---|---|---|
BaselineProfile |
Selects the reference standard and active snapshot pointer | id, workspace_id, name, status, active_snapshot_id, scope_jsonb, capture_mode |
BaselineSnapshot |
Represents the effective baseline reference state | id, baseline_profile_id, workspace_id, state, completed_at, operation_run_id |
BaselineSnapshotItem |
Defines the subject row axis for the matrix | baseline_snapshot_id, policy_type, subject_key, external_id, display_name |
Assignment truth
Type: Existing workspace-to-tenant mapping truth
Source: BaselineTenantAssignment
| Source | Purpose | Key Fields |
|---|---|---|
BaselineTenantAssignment |
Defines which tenants belong in the matrix target set | workspace_id, tenant_id, baseline_profile_id, override_scope_jsonb |
Tenant compare truth
Type: Existing tenant-owned execution and diagnostic truth
Sources: OperationRun with type = baseline_compare, BaselineCompareStats, baseline_compare run context
| Source | Purpose | Key Fields |
|---|---|---|
OperationRun |
Stores compare lifecycle, timestamps, summary counts, and context | tenant_id, workspace_id, type, status, outcome, completed_at, summary_counts, context |
OperationRun.context.baseline_compare |
Stores compare diagnostics needed for trust and no-result interpretation | coverage, reason_code, reason_translation, evidence_gaps, subjects_total, fidelity, inventory_sync_run_id |
BaselineCompareStats |
Existing single-tenant projection for freshness, coverage, findings count, and operator explanation | state, reasonCode, reasonMessage, lastComparedIso, coverageStatus, evidenceGapDetails, operatorExplanation |
Drift finding truth
Type: Existing tenant-owned technical drift and workflow truth
Source: Finding with finding_type = drift and source = baseline.compare
| Source | Purpose | Key Fields |
|---|---|---|
Finding |
Stores subject-level technical differences plus workflow metadata | tenant_id, workspace_id, scope_key, subject_external_id, subject_type, severity, status, baseline_operation_run_id, current_operation_run_id, evidence_jsonb |
New Derived Read Models
BaselineCompareMatrixReference
Type: Request-scoped reference bundle
Source: BaselineProfile + resolved effective BaselineSnapshot
| Field | Type | Notes |
|---|---|---|
workspaceId |
integer | Active workspace scope |
baselineProfileId |
integer | Selected baseline profile |
baselineProfileName |
string | Operator-facing reference label |
baselineStatus |
string | Existing profile lifecycle/status |
referenceSnapshotId |
integer or null | Null when compare is blocked |
referenceSnapshotCapturedAt |
datetime or null | For freshness context |
referenceState |
string | ready or a blocked state such as no_snapshot |
referenceReasonCode |
string or null | Existing compare-snapshot truth reason |
assignedTenantCount |
integer | Total assigned tenant count in workspace |
visibleTenantCount |
integer | Count after visibility filtering |
MatrixTenantSummary
Type: Request-scoped visible tenant summary
Source: visible tenant set + latest relevant compare run + derived cell states
| Field | Type | Notes |
|---|---|---|
tenantId |
integer | Visible tenant identifier |
tenantName |
string | Operator-facing column label |
compareRunId |
integer or null | Latest relevant compare run for this baseline |
compareRunStatus |
string or null | queued, running, completed, or null |
compareRunOutcome |
string or null | Existing OperationRun outcome |
freshnessState |
string | fresh, stale, never_compared, or unknown |
lastComparedAt |
datetime or null | Latest completed compare time |
matchedCount |
integer | Derived from cell states |
differingCount |
integer | Derived from cell states |
missingCount |
integer | Derived from cell states |
ambiguousCount |
integer | Derived from cell states |
notComparedCount |
integer | Derived from cell states |
maxSeverity |
string or null | Highest visible severity among differing cells |
trustLevel |
string | Existing trustworthiness semantics reused at tenant level |
MatrixSubjectSummary
Type: Request-scoped baseline subject summary
Source: BaselineSnapshotItem + visible tenant cell states
| Field | Type | Notes |
|---|---|---|
subjectKey |
string | Reused existing subject identity |
policyType |
string | For filter and grouping |
displayName |
string or null | Operator-facing row label |
baselineExternalId |
string or null | Secondary drilldown metadata |
deviationBreadth |
integer | Count of visible tenants in differ or missing |
missingBreadth |
integer | Count of visible tenants in missing |
ambiguousBreadth |
integer | Count of visible tenants in ambiguous |
notComparedBreadth |
integer | Count of visible tenants in not_compared |
maxSeverity |
string or null | Highest visible severity across differing cells |
trustLevel |
string | Highest-risk trust signal across visible cells |
MatrixCell
Type: Request-scoped cell read model
Source: BaselineSnapshotItem + latest relevant compare run context + compare-created findings
| Field | Type | Notes |
|---|---|---|
tenantId |
integer | Visible tenant column key |
subjectKey |
string | Subject row key |
state |
string | match, differ, missing, ambiguous, not_compared, or stale_result |
severity |
string or null | From current technical drift finding when present |
trustLevel |
string | Reused trustworthiness semantics or cell-level downgraded trust |
reasonCode |
string or null | Existing reason/evidence-gap code when the state is not a plain match |
compareRunId |
integer or null | Latest relevant compare run |
findingId |
integer or null | Existing finding drilldown target when a related finding exists |
findingWorkflowState |
string or null | Secondary workflow context; never overrides technical state |
lastComparedAt |
datetime or null | Latest compare timestamp for this tenant |
policyTypeCovered |
boolean | False when the run never covered this subject's policy type |
CompareAssignedTenantsLaunchResult
Type: Request-scoped action result bundle
Source: repeated calls to existing BaselineCompareService::startCompare()
| Field | Type | Notes |
|---|---|---|
baselineProfileId |
integer | Selected baseline profile |
visibleAssignedTenantCount |
integer | Size of eligible visible set considered by the action |
queuedCount |
integer | New compare runs started |
alreadyQueuedCount |
integer | Existing active runs reused |
blockedCount |
integer | Compare starts refused by normal service preconditions |
targets |
array | Per-tenant action outcome bundle with tenant id, run id or reason code |
Validation Rules
Visibility rules
- Only tenants visible to the current actor may produce columns, counts, or drilldown targets.
- Summary counts are always computed from the visible tenant set only.
- Hidden tenants are never represented as anonymous remainder counts.
Reference rules
- The matrix may render technical compare truth only when the selected baseline profile resolves to a usable effective snapshot.
- If no usable snapshot exists, the page remains in a blocked state and no cell state may imply compare health.
Cell derivation precedence
- If no usable reference snapshot exists, the page is blocked and no cells are materialized.
- If the tenant has no completed compare against this baseline reference or the subject's policy type was not covered, the cell state is
not_compared. - If the latest completed compare result predates the current effective snapshot or is stale under existing stale-result policy, the cell state is
stale_resultunless a stronger blocked or absent condition applies. - If the latest relevant compare run records an evidence-gap or reason code indicating ambiguous or low-confidence identity matching for the subject, the cell state is
ambiguous. - If the latest relevant compare run records the subject as missing from tenant truth, the cell state is
missing. - If the latest relevant compare output created or updated a drift finding for the subject under the current compare run, the cell state is
differregardless of finding workflow status. - Otherwise the cell state is
match.
Freshness rules
freshmeans the tenant has a completed compare result against the current effective snapshot and it does not exceed the existing stale-result threshold.stalemeans the compare result predates the current effective snapshot or breaches the existing stale-result threshold.never_comparedmeans no relevant compare run exists for the tenant and selected baseline.unknownis reserved for unexpected cases where timestamps or run truth are missing but a cell still exists.
Trust rules
- Trust levels reuse existing compare explanation semantics rather than a new matrix-only taxonomy.
matchmay only be shown when the subject was covered and no ambiguity or missing-basis signal exists.not_comparedand uncovered policy types are treated as low-trust or unusable for operator interpretation.
Relationships
- One
BaselineProfileresolves to zero or one effectiveBaselineSnapshotfor compare. - One
BaselineSnapshothas manyBaselineSnapshotItemrows. - One
BaselineProfilehas manyBaselineTenantAssignmentrows. - One visible tenant may have many
baseline_compareOperationRunrows over time, but the matrix resolves one latest relevant run per tenant for the selected baseline. - One latest relevant compare run may have many related drift
Findingrows. - One matrix cell may link to zero or one preferred finding drilldown and always belongs to exactly one visible tenant and one subject row.
Rendering Rules
- Technical deviation state is primary; finding workflow state is secondary.
- Tenant running or queued compare state is shown at tenant-summary level and does not replace the last known completed technical truth unless the latest completed truth is absent.
- Subject-focused views reuse the same row and cell models and simply reduce the visible subject set to one selected subject.
- Empty and degraded states remain page-level states rather than synthetic match rows.