TenantAtlas/specs/190-baseline-compare-matrix/data-model.md
ahmido eca19819d1 feat: add workspace baseline compare matrix (#221)
## 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
2026-04-11 10:20:25 +00:00

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

  1. If no usable reference snapshot exists, the page is blocked and no cells are materialized.
  2. 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.
  3. If the latest completed compare result predates the current effective snapshot or is stale under existing stale-result policy, the cell state is stale_result unless a stronger blocked or absent condition applies.
  4. 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.
  5. If the latest relevant compare run records the subject as missing from tenant truth, the cell state is missing.
  6. If the latest relevant compare output created or updated a drift finding for the subject under the current compare run, the cell state is differ regardless of finding workflow status.
  7. Otherwise the cell state is match.

Freshness rules

  • fresh means the tenant has a completed compare result against the current effective snapshot and it does not exceed the existing stale-result threshold.
  • stale means the compare result predates the current effective snapshot or breaches the existing stale-result threshold.
  • never_compared means no relevant compare run exists for the tenant and selected baseline.
  • unknown is 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.
  • match may only be shown when the subject was covered and no ambiguity or missing-basis signal exists.
  • not_compared and uncovered policy types are treated as low-trust or unusable for operator interpretation.

Relationships

  • One BaselineProfile resolves to zero or one effective BaselineSnapshot for compare.
  • One BaselineSnapshot has many BaselineSnapshotItem rows.
  • One BaselineProfile has many BaselineTenantAssignment rows.
  • One visible tenant may have many baseline_compare OperationRun rows 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 Finding rows.
  • 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.