## 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
468 lines
12 KiB
YAML
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' |