TenantAtlas/specs/168-tenant-governance-aggregate-contract/contracts/tenant-governance-aggregate.openapi.yaml
ahmido 807d574d31 feat: add tenant governance aggregate contract and action surface follow-ups (#199)
## Summary
- amend the operator UI constitution and related SpecKit templates for the new UI/UX governance rules
- add Spec 168 artifacts plus the tenant governance aggregate implementation used by the tenant dashboard, banner, and baseline compare landing surfaces
- normalize Filament action surfaces around clickable-row inspection, grouped secondary actions, and explicit action-surface declarations across enrolled resources and pages
- fix post-suite regressions in membership cache priming, finding workflow state refresh, tenant review derived-state invalidation, and tenant-bound backup-set related navigation

## Commit Series
- `docs: amend operator UI constitution`
- `spec: add tenant governance aggregate contract`
- `feat: add tenant governance aggregate contract`
- `refactor: normalize filament action surfaces`
- `fix: resolve post-suite state regressions`

## Testing
- `vendor/bin/sail artisan test --compact`
- Result: `3176 passed, 8 skipped (17384 assertions)`

## Notes
- Livewire v4 / Filament v5 stack remains unchanged
- no provider registration changes; `bootstrap/providers.php` remains the relevant location
- no new global-search resources or asset-registration changes in this branch

Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #199
2026-03-29 21:14:17 +00:00

335 lines
9.6 KiB
YAML

openapi: 3.1.0
info:
title: Tenant Governance Aggregate Internal Surface Contract
version: 0.1.0
summary: Internal logical contract for the shared tenant-governance summary aggregate
description: |
This contract is an internal planning artifact for Spec 168. It documents the
derived tenant-governance aggregate and the way tenant dashboard, banner, and
Baseline Compare landing surfaces consume it. The rendered routes still return
HTML. The structured schemas below describe the internal page and widget models
that must be derivable before rendering. This does not add a public HTTP API.
servers:
- url: /internal
x-governance-consumers:
- surface: tenant.dashboard.needs_attention
summarySource: tenant_governance_aggregate
accessPattern: widget_safe
guardScope:
- app/Filament/Widgets/Dashboard/NeedsAttention.php
requiredMarkers:
- 'TenantGovernanceAggregate'
- 'Needs Attention'
maxOccurrences:
- needle: 'Finding::query()'
max: 0
- surface: tenant.dashboard.baseline_governance
summarySource: tenant_governance_aggregate
accessPattern: widget_safe
guardScope:
- app/Filament/Widgets/Dashboard/BaselineCompareNow.php
requiredMarkers:
- 'TenantGovernanceAggregate'
- 'Baseline Governance'
- surface: tenant.banner.baseline_compare_coverage
summarySource: tenant_governance_aggregate
accessPattern: widget_safe
guardScope:
- app/Filament/Widgets/Tenant/BaselineCompareCoverageBanner.php
requiredMarkers:
- 'TenantGovernanceAggregate'
- 'nextActionUrl'
- surface: tenant.page.baseline_compare_landing
summarySource: tenant_governance_aggregate
accessPattern: page_safe
guardScope:
- app/Filament/Pages/BaselineCompareLanding.php
requiredMarkers:
- 'TenantGovernanceAggregate'
- 'Compare now'
paths:
/tenants/{tenant}/governance-aggregate:
get:
summary: Resolve the derived tenant-governance aggregate for one tenant
operationId: resolveTenantGovernanceAggregate
parameters:
- name: tenant
in: path
required: true
schema:
type: string
description: Tenant route key or canonical tenant identifier for the current tenant scope.
- name: surface
in: query
required: false
schema:
$ref: '#/components/schemas/GovernanceSurface'
responses:
'200':
description: Aggregate resolved for the current tenant scope
content:
application/vnd.tenantpilot.tenant-governance-aggregate+json:
schema:
$ref: '#/components/schemas/TenantGovernanceAggregate'
'403':
description: Actor is in scope but lacks the capability required to view the target surface
'404':
description: Tenant is outside workspace or tenant entitlement scope
/admin/t/{tenant}:
get:
summary: Tenant dashboard governance summary surfaces
operationId: viewTenantDashboardGovernance
parameters:
- name: tenant
in: path
required: true
schema:
type: string
responses:
'200':
description: Rendered tenant dashboard with shared governance summary surfaces
content:
text/html:
schema:
type: string
application/vnd.tenantpilot.tenant-dashboard-governance+json:
schema:
$ref: '#/components/schemas/TenantDashboardGovernanceBundle'
'403':
description: Actor is in scope but lacks the required tenant capability
'404':
description: Tenant is not visible in the current workspace or membership scope
/admin/t/{tenant}/baseline-compare-landing:
get:
summary: Baseline Compare landing governance summary and diagnostics split
operationId: viewBaselineCompareLandingGovernance
parameters:
- name: tenant
in: path
required: true
schema:
type: string
responses:
'200':
description: Rendered Baseline Compare landing page
content:
text/html:
schema:
type: string
application/vnd.tenantpilot.baseline-compare-landing-governance+json:
schema:
$ref: '#/components/schemas/BaselineCompareLandingGovernanceView'
'403':
description: Actor is in scope but lacks the required tenant capability
'404':
description: Tenant is not visible in the current workspace or membership scope
components:
schemas:
GovernanceSurface:
type: string
enum:
- dashboard_needs_attention
- dashboard_baseline_governance
- baseline_compare_landing
- coverage_banner
NextActionTarget:
type: string
enum:
- findings
- run
- landing
- none
GovernanceCounts:
type: object
additionalProperties: false
required:
- visibleDriftFindingsCount
- overdueOpenFindingsCount
- expiringGovernanceCount
- lapsedGovernanceCount
- activeNonNewFindingsCount
- highSeverityActiveFindingsCount
properties:
visibleDriftFindingsCount:
type: integer
minimum: 0
overdueOpenFindingsCount:
type: integer
minimum: 0
expiringGovernanceCount:
type: integer
minimum: 0
lapsedGovernanceCount:
type: integer
minimum: 0
activeNonNewFindingsCount:
type: integer
minimum: 0
highSeverityActiveFindingsCount:
type: integer
minimum: 0
NextActionIntent:
type: object
additionalProperties: false
required:
- label
- target
properties:
label:
type: string
target:
$ref: '#/components/schemas/NextActionTarget'
ComparePosture:
type: object
additionalProperties: false
required:
- compareState
- stateFamily
- tone
- headline
- positiveClaimAllowed
properties:
compareState:
type: string
enum:
- no_tenant
- no_assignment
- no_snapshot
- idle
- comparing
- failed
- ready
stateFamily:
type: string
enum:
- positive
- caution
- stale
- action_required
- in_progress
- unavailable
tone:
type: string
enum:
- success
- warning
- danger
- info
- gray
headline:
type: string
supportingMessage:
type:
- string
- 'null'
reasonCode:
type:
- string
- 'null'
lastComparedLabel:
type:
- string
- 'null'
positiveClaimAllowed:
type: boolean
TenantGovernanceAggregate:
type: object
additionalProperties: false
required:
- tenantId
- workspaceId
- posture
- counts
- nextAction
properties:
tenantId:
type: integer
workspaceId:
type: integer
profileName:
type:
- string
- 'null'
posture:
$ref: '#/components/schemas/ComparePosture'
counts:
$ref: '#/components/schemas/GovernanceCounts'
nextAction:
$ref: '#/components/schemas/NextActionIntent'
operationRunId:
type:
- integer
- 'null'
diagnosticsOwner:
type: string
enum:
- baseline_compare_stats
NeedsAttentionItem:
type: object
additionalProperties: false
required:
- title
- badge
- badgeColor
properties:
title:
type: string
body:
type:
- string
- 'null'
supportingMessage:
type:
- string
- 'null'
badge:
type: string
badgeColor:
type: string
nextStep:
type:
- string
- 'null'
TenantDashboardGovernanceBundle:
type: object
additionalProperties: false
required:
- aggregate
properties:
aggregate:
$ref: '#/components/schemas/TenantGovernanceAggregate'
needsAttentionItems:
type: array
items:
$ref: '#/components/schemas/NeedsAttentionItem'
baselineGovernanceCard:
type: object
additionalProperties: false
required:
- aggregate
properties:
aggregate:
$ref: '#/components/schemas/TenantGovernanceAggregate'
BaselineCompareLandingGovernanceView:
type: object
additionalProperties: false
required:
- aggregate
- diagnosticsPolicy
properties:
aggregate:
$ref: '#/components/schemas/TenantGovernanceAggregate'
diagnosticsPolicy:
type: object
additionalProperties: false
required:
- compareDiagnosticsRemainLocal
- evidenceGapDetailsRemainLocal
- compareActionUnchanged
properties:
compareDiagnosticsRemainLocal:
type: boolean
evidenceGapDetailsRemainLocal:
type: boolean
compareActionUnchanged:
type: boolean