## Summary - introduce the governance subject taxonomy registry and canonical Baseline Scope V2 normalization and persistence - update baseline profile Filament surfaces, validation, capture/compare gating, and add the optional scope backfill command with audit logging - add focused unit, feature, Filament, and browser smoke coverage for save-forward behavior, operation truth, authorization continuity, and invalid-scope rendering - remove the duplicate legacy spec plan under `specs/001-governance-subject-taxonomy/plan.md` ## Verification - `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Browser/Spec202GovernanceSubjectTaxonomySmokeTest.php` - focused Spec 202 regression pack: `56 passed (300 assertions)` - `cd apps/platform && ./vendor/bin/sail bin pint --dirty --format agent` ## Notes - no schema migration required - no new Filament asset registration required - branch includes the final browser smoke test coverage for the current feature Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de> Reviewed-on: #232
618 lines
19 KiB
YAML
618 lines
19 KiB
YAML
openapi: 3.1.0
|
|
info:
|
|
title: Governance Subject Taxonomy and Baseline Scope V2 Internal Contract
|
|
version: 0.1.0
|
|
summary: Internal logical contract for canonical baseline scope, taxonomy listing, save-forward writes, and normalized baseline operation starts
|
|
description: |
|
|
This contract is an internal planning artifact for Spec 202. The affected
|
|
surfaces still render through Filament and Livewire, and baseline capture or
|
|
compare continues to run through the existing Laravel services and jobs. The
|
|
schemas below define the canonical baseline scope document, the governance
|
|
subject taxonomy registry, legacy normalization behavior, and the effective
|
|
scope that capture and compare must consume. The path entries below are
|
|
logical boundary identifiers for existing Filament, Livewire, and service
|
|
entry points only; they do not imply new HTTP controllers or routes.
|
|
x-logical-artifact: true
|
|
x-governance-subject-taxonomy-consumers:
|
|
- surface: baseline.profile.form
|
|
sourceFiles:
|
|
- apps/platform/app/Filament/Resources/BaselineProfileResource.php
|
|
- apps/platform/app/Filament/Resources/BaselineProfileResource/Pages/CreateBaselineProfile.php
|
|
- apps/platform/app/Filament/Resources/BaselineProfileResource/Pages/EditBaselineProfile.php
|
|
mustRender:
|
|
- normalized_scope_summary
|
|
- active_subject_groups
|
|
- support_readiness
|
|
- invalid_scope_feedback
|
|
mustAccept:
|
|
- legacy_scope_input
|
|
- canonical_scope_v2
|
|
- surface: baseline.profile.detail
|
|
sourceFiles:
|
|
- apps/platform/app/Filament/Resources/BaselineProfileResource/Pages/ViewBaselineProfile.php
|
|
mustRender:
|
|
- canonical_scope_summary
|
|
- support_readiness
|
|
- normalization_lineage_on_demand
|
|
- surface: baseline.scope.backfill.command
|
|
sourceFiles:
|
|
- apps/platform/app/Console/Commands/BackfillBaselineScopeV2.php
|
|
mustAccept:
|
|
- preview_mode_by_default
|
|
- explicit_write_confirmation
|
|
mustProduce:
|
|
- candidate_rewrite_summary
|
|
- committed_rewrite_summary
|
|
- committed_write_audit_logging
|
|
- surface: baseline.capture.start
|
|
sourceFiles:
|
|
- apps/platform/app/Services/Baselines/BaselineCaptureService.php
|
|
mustConsume:
|
|
- effective_scope_v2
|
|
- capture_eligible_subject_types
|
|
- compatibility_projection_if_needed
|
|
- surface: baseline.compare.start
|
|
sourceFiles:
|
|
- apps/platform/app/Services/Baselines/BaselineCompareService.php
|
|
mustConsume:
|
|
- effective_scope_v2
|
|
- compare_eligible_subject_types
|
|
- compatibility_projection_if_needed
|
|
paths:
|
|
/internal/workspaces/{workspace}/governance-subject-taxonomy/baseline-subject-types:
|
|
get:
|
|
summary: List active baseline-selectable governance subject types for the current workspace context
|
|
operationId: listBaselineSubjectTypes
|
|
parameters:
|
|
- name: workspace
|
|
in: path
|
|
required: true
|
|
schema:
|
|
type: integer
|
|
responses:
|
|
'200':
|
|
description: Grouped taxonomy metadata for baseline scope selection and validation
|
|
content:
|
|
application/vnd.tenantpilot.governance-taxonomy+json:
|
|
schema:
|
|
$ref: '#/components/schemas/GovernanceSubjectTaxonomyRegistry'
|
|
'403':
|
|
description: Actor is in scope but lacks workspace baseline view capability
|
|
'404':
|
|
description: Workspace is outside actor scope
|
|
/internal/workspaces/{workspace}/baseline-scope/normalize:
|
|
post:
|
|
summary: Normalize legacy or canonical scope input into Baseline Scope V2
|
|
operationId: normalizeBaselineScope
|
|
parameters:
|
|
- name: workspace
|
|
in: path
|
|
required: true
|
|
schema:
|
|
type: integer
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/LegacyOrV2ScopeInput'
|
|
responses:
|
|
'200':
|
|
description: Canonical V2 scope plus summary and validation detail
|
|
content:
|
|
application/vnd.tenantpilot.baseline-scope-normalized+json:
|
|
schema:
|
|
$ref: '#/components/schemas/BaselineScopeNormalizationResult'
|
|
'422':
|
|
description: Scope input is invalid or ambiguous after normalization
|
|
content:
|
|
application/vnd.tenantpilot.baseline-scope-errors+json:
|
|
schema:
|
|
$ref: '#/components/schemas/BaselineScopeValidationErrors'
|
|
'403':
|
|
description: Actor is in scope but lacks workspace baseline manage capability
|
|
'404':
|
|
description: Workspace is outside actor scope
|
|
/internal/workspaces/{workspace}/baseline-scope/backfill:
|
|
post:
|
|
summary: Logical maintenance boundary for previewing or committing baseline profile scope backfill
|
|
operationId: backfillBaselineProfileScopeV2
|
|
description: Logical-only maintenance contract for `baseline_profiles.scope_jsonb`; compare assignment overrides remain tolerant-read only in this release.
|
|
parameters:
|
|
- name: workspace
|
|
in: path
|
|
required: true
|
|
schema:
|
|
type: integer
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/BaselineScopeBackfillRequest'
|
|
responses:
|
|
'200':
|
|
description: Preview or commit summary for the baseline profile scope backfill command
|
|
content:
|
|
application/vnd.tenantpilot.baseline-scope-backfill+json:
|
|
schema:
|
|
$ref: '#/components/schemas/BaselineScopeBackfillResult'
|
|
'403':
|
|
description: Actor is in scope but lacks workspace baseline manage capability
|
|
'404':
|
|
description: Workspace is outside actor scope
|
|
/admin/baseline-profiles:
|
|
post:
|
|
summary: Create a baseline profile using a scope request that is canonicalized to V2 before persistence
|
|
operationId: createBaselineProfileWithCanonicalScope
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/BaselineProfileWriteRequest'
|
|
responses:
|
|
'201':
|
|
description: Baseline profile created with canonical V2 scope persisted
|
|
content:
|
|
application/vnd.tenantpilot.baseline-profile+json:
|
|
schema:
|
|
$ref: '#/components/schemas/BaselineProfileScopeEnvelope'
|
|
'422':
|
|
description: Scope validation failed
|
|
'403':
|
|
description: Actor is in scope but lacks workspace baseline manage capability
|
|
'404':
|
|
description: Workspace is outside actor scope
|
|
/admin/baseline-profiles/{profile}:
|
|
patch:
|
|
summary: Update a baseline profile and save scope forward as canonical V2
|
|
operationId: updateBaselineProfileWithCanonicalScope
|
|
parameters:
|
|
- name: profile
|
|
in: path
|
|
required: true
|
|
schema:
|
|
type: integer
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/BaselineProfileWriteRequest'
|
|
responses:
|
|
'200':
|
|
description: Baseline profile updated with canonical V2 scope persisted
|
|
content:
|
|
application/vnd.tenantpilot.baseline-profile+json:
|
|
schema:
|
|
$ref: '#/components/schemas/BaselineProfileScopeEnvelope'
|
|
'422':
|
|
description: Scope validation failed
|
|
'403':
|
|
description: Actor is in scope but lacks workspace baseline manage capability
|
|
'404':
|
|
description: Workspace or baseline profile is outside actor scope
|
|
/internal/tenants/{tenant}/baseline-profiles/{profile}/capture:
|
|
post:
|
|
summary: Start baseline capture using normalized effective scope
|
|
operationId: startBaselineCaptureWithNormalizedScope
|
|
parameters:
|
|
- name: tenant
|
|
in: path
|
|
required: true
|
|
schema:
|
|
type: integer
|
|
- name: profile
|
|
in: path
|
|
required: true
|
|
schema:
|
|
type: integer
|
|
responses:
|
|
'202':
|
|
description: Baseline capture accepted with canonical effective scope recorded in operation context
|
|
content:
|
|
application/vnd.tenantpilot.baseline-operation+json:
|
|
schema:
|
|
$ref: '#/components/schemas/BaselineOperationEnvelope'
|
|
'422':
|
|
description: Requested scope is invalid or includes unsupported capture subject types
|
|
'403':
|
|
description: Actor is in scope but lacks capability to start capture
|
|
'404':
|
|
description: Tenant or profile is outside actor scope
|
|
/internal/tenants/{tenant}/baseline-profiles/{profile}/compare:
|
|
post:
|
|
summary: Start baseline compare using normalized effective scope
|
|
operationId: startBaselineCompareWithNormalizedScope
|
|
parameters:
|
|
- name: tenant
|
|
in: path
|
|
required: true
|
|
schema:
|
|
type: integer
|
|
- name: profile
|
|
in: path
|
|
required: true
|
|
schema:
|
|
type: integer
|
|
responses:
|
|
'202':
|
|
description: Baseline compare accepted with canonical effective scope recorded in operation context
|
|
content:
|
|
application/vnd.tenantpilot.baseline-operation+json:
|
|
schema:
|
|
$ref: '#/components/schemas/BaselineOperationEnvelope'
|
|
'422':
|
|
description: Requested scope is invalid or includes unsupported compare subject types
|
|
'403':
|
|
description: Actor is in scope but lacks capability to start compare
|
|
'404':
|
|
description: Tenant or profile is outside actor scope
|
|
components:
|
|
schemas:
|
|
GovernanceDomainKey:
|
|
type: string
|
|
description: Current active values are `intune` and `platform_foundation`; additional domain keys may be introduced later without changing the V2 contract shape.
|
|
examples:
|
|
- intune
|
|
- platform_foundation
|
|
- entra
|
|
GovernanceSubjectClass:
|
|
type: string
|
|
enum:
|
|
- policy
|
|
- configuration_resource
|
|
- posture_dimension
|
|
- control
|
|
GovernanceSubjectType:
|
|
type: object
|
|
additionalProperties: false
|
|
required:
|
|
- domain_key
|
|
- subject_class
|
|
- subject_type_key
|
|
- label
|
|
- description
|
|
- capture_supported
|
|
- compare_supported
|
|
- inventory_supported
|
|
- active
|
|
properties:
|
|
domain_key:
|
|
$ref: '#/components/schemas/GovernanceDomainKey'
|
|
subject_class:
|
|
$ref: '#/components/schemas/GovernanceSubjectClass'
|
|
subject_type_key:
|
|
type: string
|
|
label:
|
|
type: string
|
|
description:
|
|
type:
|
|
- string
|
|
- 'null'
|
|
capture_supported:
|
|
type: boolean
|
|
compare_supported:
|
|
type: boolean
|
|
inventory_supported:
|
|
type: boolean
|
|
active:
|
|
type: boolean
|
|
support_mode:
|
|
type:
|
|
- string
|
|
- 'null'
|
|
legacy_bucket:
|
|
type:
|
|
- string
|
|
- 'null'
|
|
GovernanceSubjectTaxonomyRegistry:
|
|
type: object
|
|
additionalProperties: false
|
|
required:
|
|
- subject_types
|
|
properties:
|
|
subject_types:
|
|
type: array
|
|
items:
|
|
$ref: '#/components/schemas/GovernanceSubjectType'
|
|
LegacyBaselineScopePayload:
|
|
type: object
|
|
additionalProperties: false
|
|
minProperties: 1
|
|
properties:
|
|
policy_types:
|
|
type: array
|
|
description: Empty or omitted means all supported Intune policy subject types when the other legacy bucket is present.
|
|
items:
|
|
type: string
|
|
foundation_types:
|
|
type: array
|
|
description: Empty or omitted means no foundation subject types when the other legacy bucket is present.
|
|
items:
|
|
type: string
|
|
BaselineScopeEntryV2:
|
|
type: object
|
|
additionalProperties: false
|
|
required:
|
|
- domain_key
|
|
- subject_class
|
|
- subject_type_keys
|
|
properties:
|
|
domain_key:
|
|
$ref: '#/components/schemas/GovernanceDomainKey'
|
|
subject_class:
|
|
$ref: '#/components/schemas/GovernanceSubjectClass'
|
|
subject_type_keys:
|
|
type: array
|
|
minItems: 1
|
|
items:
|
|
type: string
|
|
filters:
|
|
type: object
|
|
additionalProperties: true
|
|
default: {}
|
|
BaselineScopeDocumentV2:
|
|
type: object
|
|
additionalProperties: false
|
|
required:
|
|
- version
|
|
- entries
|
|
properties:
|
|
version:
|
|
type: integer
|
|
enum:
|
|
- 2
|
|
entries:
|
|
type: array
|
|
minItems: 1
|
|
items:
|
|
$ref: '#/components/schemas/BaselineScopeEntryV2'
|
|
LegacyOrV2ScopeInput:
|
|
oneOf:
|
|
- $ref: '#/components/schemas/LegacyBaselineScopePayload'
|
|
- $ref: '#/components/schemas/BaselineScopeDocumentV2'
|
|
BaselineScopeSummaryGroup:
|
|
type: object
|
|
additionalProperties: false
|
|
required:
|
|
- domain_key
|
|
- subject_class
|
|
- group_label
|
|
- selected_subject_types
|
|
- capture_supported_count
|
|
- compare_supported_count
|
|
properties:
|
|
domain_key:
|
|
$ref: '#/components/schemas/GovernanceDomainKey'
|
|
subject_class:
|
|
$ref: '#/components/schemas/GovernanceSubjectClass'
|
|
group_label:
|
|
type: string
|
|
selected_subject_types:
|
|
description: Operator-facing selected subject labels for the group, not raw subject type keys.
|
|
type: array
|
|
items:
|
|
type: string
|
|
capture_supported_count:
|
|
type: integer
|
|
compare_supported_count:
|
|
type: integer
|
|
inactive_count:
|
|
type: integer
|
|
BaselineScopeNormalizationResult:
|
|
type: object
|
|
additionalProperties: false
|
|
required:
|
|
- canonical_scope
|
|
- normalization_lineage
|
|
- summary
|
|
properties:
|
|
canonical_scope:
|
|
$ref: '#/components/schemas/BaselineScopeDocumentV2'
|
|
normalization_lineage:
|
|
$ref: '#/components/schemas/BaselineScopeNormalizationLineage'
|
|
summary:
|
|
type: array
|
|
items:
|
|
$ref: '#/components/schemas/BaselineScopeSummaryGroup'
|
|
legacy_projection:
|
|
type:
|
|
- object
|
|
- 'null'
|
|
additionalProperties:
|
|
type: array
|
|
items:
|
|
type: string
|
|
BaselineScopeValidationError:
|
|
type: object
|
|
additionalProperties: false
|
|
required:
|
|
- code
|
|
- message
|
|
properties:
|
|
code:
|
|
type: string
|
|
message:
|
|
type: string
|
|
path:
|
|
type:
|
|
- string
|
|
- 'null'
|
|
BaselineScopeValidationErrors:
|
|
type: object
|
|
additionalProperties: false
|
|
required:
|
|
- errors
|
|
properties:
|
|
errors:
|
|
type: array
|
|
items:
|
|
$ref: '#/components/schemas/BaselineScopeValidationError'
|
|
BaselineProfileWriteRequest:
|
|
type: object
|
|
additionalProperties: false
|
|
required:
|
|
- name
|
|
- status
|
|
- capture_mode
|
|
- requested_scope
|
|
properties:
|
|
name:
|
|
type: string
|
|
description:
|
|
type:
|
|
- string
|
|
- 'null'
|
|
status:
|
|
type: string
|
|
capture_mode:
|
|
type: string
|
|
version_label:
|
|
type:
|
|
- string
|
|
- 'null'
|
|
requested_scope:
|
|
$ref: '#/components/schemas/LegacyOrV2ScopeInput'
|
|
BaselineProfileScopeEnvelope:
|
|
type: object
|
|
additionalProperties: false
|
|
required:
|
|
- profile_id
|
|
- persisted_scope
|
|
- normalization_lineage
|
|
- summary
|
|
properties:
|
|
profile_id:
|
|
type: integer
|
|
persisted_scope:
|
|
$ref: '#/components/schemas/BaselineScopeDocumentV2'
|
|
normalization_lineage:
|
|
$ref: '#/components/schemas/BaselineScopeNormalizationLineage'
|
|
summary:
|
|
type: array
|
|
items:
|
|
$ref: '#/components/schemas/BaselineScopeSummaryGroup'
|
|
BaselineScopeNormalizationLineage:
|
|
type: object
|
|
additionalProperties: false
|
|
required:
|
|
- source_shape
|
|
- normalized_on_read
|
|
- legacy_keys_present
|
|
- save_forward_required
|
|
properties:
|
|
source_shape:
|
|
type: string
|
|
enum:
|
|
- legacy
|
|
- canonical_v2
|
|
normalized_on_read:
|
|
type: boolean
|
|
legacy_keys_present:
|
|
type: array
|
|
items:
|
|
type: string
|
|
save_forward_required:
|
|
type: boolean
|
|
EffectiveBaselineScope:
|
|
type: object
|
|
additionalProperties: false
|
|
required:
|
|
- canonical_scope
|
|
- selected_type_keys
|
|
- allowed_type_keys
|
|
- limited_type_keys
|
|
- unsupported_type_keys
|
|
properties:
|
|
canonical_scope:
|
|
$ref: '#/components/schemas/BaselineScopeDocumentV2'
|
|
selected_type_keys:
|
|
type: array
|
|
items:
|
|
type: string
|
|
allowed_type_keys:
|
|
type: array
|
|
items:
|
|
type: string
|
|
limited_type_keys:
|
|
type: array
|
|
items:
|
|
type: string
|
|
unsupported_type_keys:
|
|
type: array
|
|
items:
|
|
type: string
|
|
capabilities_by_type:
|
|
type: object
|
|
additionalProperties: true
|
|
legacy_projection:
|
|
type:
|
|
- object
|
|
- 'null'
|
|
additionalProperties:
|
|
type: array
|
|
items:
|
|
type: string
|
|
BaselineOperationEnvelope:
|
|
type: object
|
|
additionalProperties: false
|
|
required:
|
|
- profile_id
|
|
- tenant_id
|
|
- operation_type
|
|
- effective_scope
|
|
properties:
|
|
profile_id:
|
|
type: integer
|
|
tenant_id:
|
|
type: integer
|
|
operation_type:
|
|
type: string
|
|
enum:
|
|
- baseline_capture
|
|
- baseline_compare
|
|
effective_scope:
|
|
$ref: '#/components/schemas/EffectiveBaselineScope'
|
|
run_id:
|
|
type:
|
|
- integer
|
|
- 'null'
|
|
BaselineScopeBackfillRequest:
|
|
type: object
|
|
additionalProperties: false
|
|
required:
|
|
- dry_run
|
|
properties:
|
|
dry_run:
|
|
type: boolean
|
|
default: true
|
|
write_confirmed:
|
|
type: boolean
|
|
default: false
|
|
BaselineScopeBackfillResult:
|
|
type: object
|
|
additionalProperties: false
|
|
required:
|
|
- mode
|
|
- candidate_count
|
|
- rewritten_count
|
|
- audit_logged
|
|
- scope_surface
|
|
properties:
|
|
mode:
|
|
type: string
|
|
enum:
|
|
- preview
|
|
- commit
|
|
candidate_count:
|
|
type: integer
|
|
rewritten_count:
|
|
type: integer
|
|
audit_logged:
|
|
type: boolean
|
|
scope_surface:
|
|
type: string
|
|
enum:
|
|
- baseline_profiles_only |