TenantAtlas/specs/190-baseline-compare-matrix/contracts/baseline-compare-matrix.logical.openapi.yaml
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

468 lines
12 KiB
YAML

openapi: 3.1.0
info:
title: Workspace Baseline Compare Matrix Internal Surface Contract
version: 0.1.0
summary: Internal logical contract for workspace baseline compare matrix reads, drilldown context, and compare-all launch behavior
description: |
This contract is an internal planning artifact for Spec 190. The affected routes still
render HTML through Filament and Livewire. The schemas below define the bounded read
models and action payloads that must be derivable from existing baseline, compare,
finding, and run truth before the matrix surface can render or trigger compare-all.
servers:
- url: /internal
x-baseline-compare-matrix-consumers:
- surface: baseline.profile.detail
sourceFiles:
- apps/platform/app/Filament/Resources/BaselineProfileResource/Pages/ViewBaselineProfile.php
mustRender:
- baseline_profile_id
- reference_snapshot_id
- visible_tenant_count
- open_compare_matrix_action
- compare_assigned_tenants_action
- surface: baseline.compare.matrix
sourceFiles:
- apps/platform/app/Filament/Pages/BaselineCompareMatrix.php
- apps/platform/resources/views/filament/pages/baseline-compare-matrix.blade.php
mustRender:
- reference
- filters
- tenant_summaries
- subject_summaries
- matrix_rows
- freshness_legend
- trust_legend
mustAccept:
- policy_type
- state
- severity
- tenant_sort
- subject_sort
- subject_key
- surface: tenant.compare.drilldown
sourceFiles:
- apps/platform/app/Filament/Pages/BaselineCompareLanding.php
- apps/platform/app/Support/Navigation/CanonicalNavigationContext.php
mustRender:
- matrix_source_context
- baseline_profile_id
- subject_key
- back_link
paths:
/admin/baseline-profiles/{profile}/compare-matrix:
get:
summary: Render the workspace baseline compare matrix for one selected baseline profile
operationId: viewBaselineCompareMatrix
parameters:
- name: profile
in: path
required: true
schema:
type: integer
- name: policy_type
in: query
required: false
schema:
type: array
items:
type: string
- name: state
in: query
required: false
schema:
type: array
items:
$ref: '#/components/schemas/MatrixCellState'
- name: severity
in: query
required: false
schema:
type: array
items:
$ref: '#/components/schemas/FindingSeverity'
- name: tenant_sort
in: query
required: false
schema:
$ref: '#/components/schemas/TenantSort'
- name: subject_sort
in: query
required: false
schema:
$ref: '#/components/schemas/SubjectSort'
- name: subject_key
in: query
required: false
schema:
type: string
description: Optional focused subject row selector for subject-first drilldown within the same matrix route.
responses:
'200':
description: Rendered workspace matrix plus a fully derived matrix bundle
content:
text/html:
schema:
type: string
application/vnd.tenantpilot.baseline-compare-matrix+json:
schema:
$ref: '#/components/schemas/BaselineCompareMatrixBundle'
'403':
description: Actor is in scope but lacks workspace baseline view capability
'404':
description: Workspace or baseline profile is outside actor scope
/internal/workspaces/{workspace}/baseline-profiles/{profile}/compare-assigned-tenants:
post:
summary: Launch compare for all visible assigned tenants under the selected baseline profile
operationId: compareAssignedTenants
parameters:
- name: workspace
in: path
required: true
schema:
type: integer
- name: profile
in: path
required: true
schema:
type: integer
responses:
'200':
description: Compare-all launch summary derived from underlying tenant compare starts
content:
application/vnd.tenantpilot.compare-assigned-tenants+json:
schema:
$ref: '#/components/schemas/CompareAssignedTenantsLaunchResult'
'403':
description: Actor is in scope but lacks workspace baseline manage capability
'404':
description: Workspace or baseline profile is outside actor scope
components:
schemas:
MatrixCellState:
type: string
enum:
- match
- differ
- missing
- ambiguous
- not_compared
- stale_result
FindingSeverity:
type: string
enum:
- low
- medium
- high
- critical
FreshnessState:
type: string
enum:
- fresh
- stale
- never_compared
- unknown
TrustLevel:
type: string
enum:
- trustworthy
- limited_confidence
- diagnostic_only
- unusable
TenantSort:
type: string
enum:
- tenant_name
- deviation_count
- freshness_urgency
SubjectSort:
type: string
enum:
- deviation_breadth
- policy_type
- display_name
MatrixReference:
type: object
additionalProperties: false
required:
- workspaceId
- baselineProfileId
- baselineProfileName
- baselineStatus
- referenceState
- assignedTenantCount
- visibleTenantCount
properties:
workspaceId:
type: integer
baselineProfileId:
type: integer
baselineProfileName:
type: string
baselineStatus:
type: string
referenceSnapshotId:
type:
- integer
- 'null'
referenceSnapshotCapturedAt:
type:
- string
- 'null'
format: date-time
referenceState:
type: string
referenceReasonCode:
type:
- string
- 'null'
assignedTenantCount:
type: integer
visibleTenantCount:
type: integer
MatrixTenantSummary:
type: object
additionalProperties: false
required:
- tenantId
- tenantName
- freshnessState
- matchedCount
- differingCount
- missingCount
- ambiguousCount
- notComparedCount
- trustLevel
properties:
tenantId:
type: integer
tenantName:
type: string
compareRunId:
type:
- integer
- 'null'
compareRunStatus:
type:
- string
- 'null'
compareRunOutcome:
type:
- string
- 'null'
freshnessState:
$ref: '#/components/schemas/FreshnessState'
lastComparedAt:
type:
- string
- 'null'
format: date-time
matchedCount:
type: integer
differingCount:
type: integer
missingCount:
type: integer
ambiguousCount:
type: integer
notComparedCount:
type: integer
maxSeverity:
anyOf:
- $ref: '#/components/schemas/FindingSeverity'
- type: 'null'
trustLevel:
$ref: '#/components/schemas/TrustLevel'
MatrixSubjectSummary:
type: object
additionalProperties: false
required:
- subjectKey
- policyType
- deviationBreadth
- missingBreadth
- ambiguousBreadth
- notComparedBreadth
- trustLevel
properties:
subjectKey:
type: string
policyType:
type: string
displayName:
type:
- string
- 'null'
baselineExternalId:
type:
- string
- 'null'
deviationBreadth:
type: integer
missingBreadth:
type: integer
ambiguousBreadth:
type: integer
notComparedBreadth:
type: integer
maxSeverity:
anyOf:
- $ref: '#/components/schemas/FindingSeverity'
- type: 'null'
trustLevel:
$ref: '#/components/schemas/TrustLevel'
MatrixCell:
type: object
additionalProperties: false
required:
- tenantId
- subjectKey
- state
- trustLevel
- policyTypeCovered
properties:
tenantId:
type: integer
subjectKey:
type: string
state:
$ref: '#/components/schemas/MatrixCellState'
severity:
anyOf:
- $ref: '#/components/schemas/FindingSeverity'
- type: 'null'
trustLevel:
$ref: '#/components/schemas/TrustLevel'
reasonCode:
type:
- string
- 'null'
compareRunId:
type:
- integer
- 'null'
findingId:
type:
- integer
- 'null'
findingWorkflowState:
type:
- string
- 'null'
lastComparedAt:
type:
- string
- 'null'
format: date-time
policyTypeCovered:
type: boolean
MatrixRow:
type: object
additionalProperties: false
required:
- subject
- cells
properties:
subject:
$ref: '#/components/schemas/MatrixSubjectSummary'
cells:
type: array
items:
$ref: '#/components/schemas/MatrixCell'
MatrixFilterState:
type: object
additionalProperties: false
properties:
policyTypes:
type: array
items:
type: string
states:
type: array
items:
$ref: '#/components/schemas/MatrixCellState'
severities:
type: array
items:
$ref: '#/components/schemas/FindingSeverity'
tenantSort:
$ref: '#/components/schemas/TenantSort'
subjectSort:
$ref: '#/components/schemas/SubjectSort'
focusedSubjectKey:
type:
- string
- 'null'
BaselineCompareMatrixBundle:
type: object
additionalProperties: false
required:
- reference
- filters
- tenantSummaries
- subjectSummaries
- rows
properties:
reference:
$ref: '#/components/schemas/MatrixReference'
filters:
$ref: '#/components/schemas/MatrixFilterState'
tenantSummaries:
type: array
items:
$ref: '#/components/schemas/MatrixTenantSummary'
subjectSummaries:
type: array
items:
$ref: '#/components/schemas/MatrixSubjectSummary'
rows:
type: array
items:
$ref: '#/components/schemas/MatrixRow'
CompareAssignedTenantTarget:
type: object
additionalProperties: false
required:
- tenantId
- launchState
properties:
tenantId:
type: integer
runId:
type:
- integer
- 'null'
launchState:
type: string
enum:
- queued
- already_queued
- blocked
reasonCode:
type:
- string
- 'null'
CompareAssignedTenantsLaunchResult:
type: object
additionalProperties: false
required:
- baselineProfileId
- visibleAssignedTenantCount
- queuedCount
- alreadyQueuedCount
- blockedCount
- targets
properties:
baselineProfileId:
type: integer
visibleAssignedTenantCount:
type: integer
queuedCount:
type: integer
alreadyQueuedCount:
type: integer
blockedCount:
type: integer
targets:
type: array
items:
$ref: '#/components/schemas/CompareAssignedTenantTarget'