TenantAtlas/specs/203-baseline-compare-strategy/contracts/baseline-compare-strategy.logical.openapi.yaml
ahmido d644265d30 Spec 203: extract baseline compare strategy (#233)
## Summary
- extract baseline compare orchestration behind an explicit strategy contract and registry
- preserve the current Intune compare path through a dedicated `IntuneCompareStrategy`
- harden compare launch and review surfaces for mixed, unsupported, incomplete, and strategy-failure truth
- add Spec 203 artifacts, focused regression coverage, and future-domain strategy proof tests

## Testing
- `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Unit/Baselines/CompareStrategyRegistryTest.php tests/Unit/Baselines/CompareSubjectResultContractTest.php tests/Feature/Baselines/BaselineCompareStrategySelectionTest.php tests/Feature/Baselines/BaselineComparePreconditionsTest.php tests/Feature/Baselines/BaselineCompareExecutionGuardTest.php tests/Feature/Baselines/BaselineCompareMatrixCompareAllActionTest.php tests/Feature/Filament/BaselineProfileCompareStartSurfaceTest.php tests/Feature/Filament/BaselineCompareLandingStartSurfaceTest.php tests/Feature/Filament/BaselineCompareLandingWhyNoFindingsTest.php tests/Feature/Filament/BaselineCompareMatrixPageTest.php tests/Feature/Filament/OperationRunBaselineTruthSurfaceTest.php`
- `cd apps/platform && ./vendor/bin/sail bin pint --dirty --format agent`

## Notes
- no new Filament panel/provider registration changes
- no global-search resource changes
- no new asset registration or deployment step changes

Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #233
2026-04-13 21:17:04 +00:00

442 lines
14 KiB
YAML

openapi: 3.1.0
info:
title: Baseline Compare Strategy Extraction Internal Contract
version: 0.1.0
summary: Internal logical contract for compare strategy selection, compare launch validation, and strategy-owned subject results
description: |
This contract is an internal planning artifact for Spec 203. The affected
compare surfaces still render through Filament and Livewire, and compare
execution continues to run through the existing Laravel services and jobs.
The paths below are logical boundary identifiers for existing service, job,
and surface entry points only; they do not imply new HTTP controllers or
routes.
x-logical-artifact: true
x-baseline-compare-strategy-consumers:
- surface: baseline.compare.start
sourceFiles:
- apps/platform/app/Services/Baselines/BaselineCompareService.php
mustConsume:
- canonical_scope_v2
- deterministic_strategy_selection
- unsupported_or_mixed_scope_rejection
- strategy_key_recorded_in_run_context
- surface: baseline.compare.fanout
sourceFiles:
- apps/platform/app/Services/Baselines/BaselineCompareService.php
- apps/platform/app/Filament/Pages/BaselineCompareMatrix.php
- apps/platform/app/Filament/Resources/BaselineProfileResource/Pages/ViewBaselineProfile.php
mustConsume:
- visible_set_strategy_validation
- single_strategy_per_run_rule
- tenant_owned_compare_runs_only
- surface: baseline.compare.execution
sourceFiles:
- apps/platform/app/Jobs/CompareBaselineToTenantJob.php
- apps/platform/app/Services/Baselines/CurrentStateHashResolver.php
- apps/platform/app/Services/Drift/DriftHasher.php
mustConsume:
- compare_orchestration_context
- compare_subject_result
- strategy_provided_subject_projection
- surface: baseline.compare.review
sourceFiles:
- apps/platform/app/Filament/Pages/BaselineCompareLanding.php
- apps/platform/app/Support/Baselines/BaselineCompareSummaryAssessor.php
- apps/platform/app/Support/Baselines/BaselineCompareExplanationRegistry.php
mustRender:
- unsupported_scope_truth
- incomplete_or_ambiguous_truth
- failed_strategy_truth
paths:
/internal/tenants/{tenant}/baseline-profiles/{profile}/compare/validate:
post:
summary: Resolve one compatible compare strategy family before compare is enqueued
operationId: validateBaselineCompareStrategySelection
parameters:
- name: tenant
in: path
required: true
schema:
type: integer
- name: profile
in: path
required: true
schema:
type: integer
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CompareValidationRequest'
responses:
'200':
description: One compatible strategy family was selected for the requested compare scope
content:
application/vnd.tenantpilot.baseline-compare-strategy-selection+json:
schema:
$ref: '#/components/schemas/CompareStrategySelection'
'422':
description: Scope is unsupported or requires more than one strategy family
content:
application/vnd.tenantpilot.baseline-compare-strategy-errors+json:
schema:
$ref: '#/components/schemas/CompareStrategySelection'
'403':
description: Actor is in scope but lacks capability to start compare
'404':
description: Tenant or baseline profile is outside actor scope
/internal/tenants/{tenant}/baseline-profiles/{profile}/compare:
post:
summary: Start baseline compare using one selected strategy family
operationId: startBaselineCompareWithStrategySelection
parameters:
- name: tenant
in: path
required: true
schema:
type: integer
- name: profile
in: path
required: true
schema:
type: integer
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CompareValidationRequest'
responses:
'202':
description: Compare accepted with a deterministic strategy family recorded in the existing run context
content:
application/vnd.tenantpilot.baseline-compare-run+json:
schema:
$ref: '#/components/schemas/CompareLaunchEnvelope'
'422':
description: Scope is unsupported or mixed and compare was not started
'403':
description: Actor is in scope but lacks capability to start compare
'404':
description: Tenant or baseline profile is outside actor scope
/internal/workspaces/{workspace}/baseline-profiles/{profile}/compare-visible-assignments:
post:
summary: Start compare for the visible assigned tenant set only when one strategy family supports the shared scope
operationId: startVisibleAssignmentCompareWithStrategySelection
parameters:
- name: workspace
in: path
required: true
schema:
type: integer
- name: profile
in: path
required: true
schema:
type: integer
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/VisibleAssignmentCompareRequest'
responses:
'202':
description: Visible tenant compare fan-out accepted with the same selected strategy family for each tenant-owned run
content:
application/vnd.tenantpilot.baseline-compare-fanout+json:
schema:
$ref: '#/components/schemas/VisibleAssignmentCompareEnvelope'
'422':
description: Visible set scope is unsupported or mixed and no tenant compare runs were started
'403':
description: Actor is in scope but lacks workspace baseline manage capability
'404':
description: Workspace or baseline profile is outside actor scope
/internal/baseline-compare/subjects/classify:
post:
summary: Logical strategy-owned boundary for classifying one compare subject inside the compare job
operationId: classifyCompareSubject
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CompareSubjectInput'
responses:
'200':
description: Structured compare-subject result consumable by platform orchestration
content:
application/vnd.tenantpilot.compare-subject-result+json:
schema:
$ref: '#/components/schemas/CompareSubjectResult'
components:
schemas:
CompareStrategyKey:
type: string
examples:
- intune_policy
- entra_configuration
StrategySelectionState:
type: string
enum:
- supported
- unsupported
- mixed
ScopeEntryReference:
type: object
additionalProperties: false
required:
- domain_key
- subject_class
- subject_type_keys
properties:
domain_key:
type: string
subject_class:
type: string
subject_type_keys:
type: array
items:
type: string
CompareStrategyCapability:
type: object
additionalProperties: false
required:
- strategy_key
- domain_keys
- subject_classes
- compare_supported
- active
properties:
strategy_key:
$ref: '#/components/schemas/CompareStrategyKey'
domain_keys:
type: array
items:
type: string
subject_classes:
type: array
items:
type: string
subject_type_keys:
oneOf:
- type: string
enum:
- all
- type: array
items:
type: string
compare_supported:
type: boolean
active:
type: boolean
CompareStrategySelection:
type: object
additionalProperties: false
required:
- selection_state
- matched_scope_entries
- rejected_scope_entries
- operator_reason
- diagnostics
properties:
selection_state:
$ref: '#/components/schemas/StrategySelectionState'
strategy_key:
oneOf:
- $ref: '#/components/schemas/CompareStrategyKey'
- type: 'null'
matched_scope_entries:
type: array
items:
$ref: '#/components/schemas/ScopeEntryReference'
rejected_scope_entries:
type: array
items:
$ref: '#/components/schemas/ScopeEntryReference'
operator_reason:
type: string
diagnostics:
type: object
additionalProperties: true
CompareValidationRequest:
type: object
additionalProperties: false
required:
- baseline_profile_id
- normalized_scope
properties:
baseline_profile_id:
type: integer
baseline_snapshot_id:
type:
- integer
- 'null'
normalized_scope:
type: object
additionalProperties: true
CompareLaunchEnvelope:
type: object
additionalProperties: false
required:
- operation_run_id
- strategy_selection
properties:
operation_run_id:
type: integer
strategy_selection:
$ref: '#/components/schemas/CompareStrategySelection'
VisibleAssignmentCompareRequest:
type: object
additionalProperties: false
required:
- baseline_profile_id
- normalized_scope
- visible_tenant_ids
properties:
baseline_profile_id:
type: integer
normalized_scope:
type: object
additionalProperties: true
visible_tenant_ids:
type: array
items:
type: integer
VisibleAssignmentCompareEnvelope:
type: object
additionalProperties: false
required:
- strategy_selection
- started_runs
- skipped_tenants
properties:
strategy_selection:
$ref: '#/components/schemas/CompareStrategySelection'
started_runs:
type: array
items:
type: integer
skipped_tenants:
type: array
items:
type: integer
CompareSubjectInput:
type: object
additionalProperties: false
required:
- strategy_key
- subject_identity
- baseline_state
- current_state
properties:
strategy_key:
$ref: '#/components/schemas/CompareStrategyKey'
subject_identity:
type: object
additionalProperties: true
baseline_state:
type: object
additionalProperties: true
current_state:
oneOf:
- type: object
additionalProperties: true
- type: 'null'
CompareSubjectState:
type: string
enum:
- no_drift
- drift
- unsupported
- incomplete
- ambiguous
- failed
CompareSubjectProjection:
type: object
additionalProperties: false
required:
- platform_subject_class
- domain_key
- subject_type_key
- operator_label
properties:
platform_subject_class:
type: string
domain_key:
type: string
subject_type_key:
type: string
operator_label:
type: string
summary_kind:
type:
- string
- 'null'
additional_labels:
type: object
additionalProperties:
type: string
CompareFindingCandidate:
type: object
additionalProperties: false
required:
- change_type
- severity
- fingerprint_basis
- evidence_payload
- auto_close_eligible
properties:
change_type:
type: string
severity:
type: string
fingerprint_basis:
type: object
additionalProperties: true
evidence_payload:
type: object
additionalProperties: true
auto_close_eligible:
type: boolean
CompareSubjectResult:
type: object
additionalProperties: false
required:
- subject_identity
- projection
- baseline_availability
- current_state_availability
- compare_state
- trust_level
- evidence_quality
- diagnostics
properties:
subject_identity:
type: object
additionalProperties: true
projection:
$ref: '#/components/schemas/CompareSubjectProjection'
baseline_availability:
type: string
current_state_availability:
type: string
compare_state:
$ref: '#/components/schemas/CompareSubjectState'
trust_level:
type: string
evidence_quality:
type: string
severity_recommendation:
type:
- string
- 'null'
finding_candidate:
oneOf:
- $ref: '#/components/schemas/CompareFindingCandidate'
- type: 'null'
diagnostics:
type: object
additionalProperties: true