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