openapi: 3.1.0
info:
  title: Workspace Recovery Posture Visibility Internal Surface Contract
  version: 0.1.0
  summary: Internal logical contract for backup-health and recovery-evidence visibility on the workspace overview
  description: |
    This contract is an internal planning artifact for Spec 185. It documents how
    the existing workspace overview must derive backup-health and recovery-evidence
    metrics, attention items, calmness, and tenant-dashboard drillthroughs from
    visible tenant truth. 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-overview-consumers:
  - surface: workspace.overview.summary_stats
    summarySource:
      - workspace_overview_builder
      - tenant_backup_health_resolver
      - restore_safety_resolver_dashboard_recovery_evidence
      - existing_governance_metrics
      - existing_activity_metrics
    guardScope:
      - app/Support/Workspaces/WorkspaceOverviewBuilder.php
      - app/Filament/Widgets/Workspace/WorkspaceSummaryStats.php
    expectedContract:
      - backup_attention_and_recovery_attention_are_separate_metrics
      - counts_are_visible_tenant_counts_not_raw_issue_totals
      - single_tenant_metric_destinations_may_open_that_tenant_dashboard
      - multi_tenant_metric_destinations_fall_back_to_choose_tenant
  - surface: workspace.overview.needs_attention
    summarySource:
      - workspace_overview_builder
      - tenant_backup_health_resolver
      - restore_safety_resolver_dashboard_recovery_evidence
      - existing_governance_attention
      - existing_operations_attention
    guardScope:
      - app/Support/Workspaces/WorkspaceOverviewBuilder.php
      - app/Filament/Widgets/Workspace/WorkspaceNeedsAttention.php
      - resources/views/filament/widgets/workspace/workspace-needs-attention.blade.php
    expectedContract:
      - backup_health_and_recovery_evidence_items_are_tenant_bound
      - new_items_use_tenant_dashboard_as_primary_destination
      - backup_and_recovery_items_rank_above_activity_only_signals
      - no_item_claims_workspace_recovery_is_proven
      - hidden_tenants_never_leak_through_labels_or_reason_text
  - surface: workspace.overview.calmness
    summarySource:
      - workspace_overview_builder
      - tenant_backup_health_resolver
      - restore_safety_resolver_dashboard_recovery_evidence
      - existing_workspace_calmness_inputs
    guardScope:
      - app/Support/Workspaces/WorkspaceOverviewBuilder.php
      - resources/views/filament/pages/workspace-overview.blade.php
    expectedContract:
      - checked_domains_include_backup_health_and_recovery_evidence
      - calmness_is_false_when_any_visible_backup_or_recovery_issue_exists
      - calmness_is_explicitly_bounded_to_visible_tenants
paths:
  /admin:
    get:
      summary: Render the workspace overview with backup-health and recovery-evidence visibility
      operationId: viewWorkspaceRecoveryPostureOverview
      responses:
        '200':
          description: Workspace overview rendered with visible-tenant backup and recovery metrics, attention, and calmness semantics
          content:
            text/html:
              schema:
                type: string
            application/vnd.tenantpilot.workspace-recovery-posture-visibility+json:
              schema:
                $ref: '#/components/schemas/WorkspaceRecoveryOverviewBundle'
        '302':
          description: No workspace context is active yet, so the request redirects to `/admin/choose-workspace`
        '404':
          description: Workspace is outside entitlement scope
  /admin/choose-tenant:
    get:
      summary: Deliberate tenant-entry destination used by multi-tenant workspace backup or recovery metrics
      operationId: openChooseTenantFromWorkspaceRecoveryOverview
      responses:
        '200':
          description: Choose-tenant page opened inside the current workspace context so the operator can pick a tenant deliberately
  /admin/t/{tenant}:
    get:
      summary: Canonical tenant-dashboard drillthrough for workspace backup or recovery posture items
      operationId: openTenantDashboardFromWorkspaceRecoveryOverview
      parameters:
        - name: tenant
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Tenant dashboard opened for the visible tenant named by the workspace item
        '404':
          description: Tenant is outside entitlement scope
components:
  schemas:
    MetricCategory:
      type: string
      enum:
        - scope
        - governance_risk
        - backup_health
        - recovery_evidence
        - activity
        - alerts
    WorkspaceMetricKey:
      type: string
      enum:
        - accessible_tenants
        - governance_attention_tenants
        - backup_attention_tenants
        - recovery_attention_tenants
        - active_operations
        - alert_failures
    AttentionFamily:
      type: string
      enum:
        - governance
        - findings
        - compare
        - backup_health
        - recovery_evidence
        - operations
        - alerts
        - evidence
        - review
    AttentionUrgency:
      type: string
      enum:
        - critical
        - high
        - medium
        - supporting
    DestinationKind:
      type: string
      enum:
        - choose_tenant
        - switch_workspace
        - tenant_dashboard
        - tenant_findings
        - baseline_compare_landing
        - operations_index
        - alerts_overview
        - tenant_evidence
        - tenant_reviews
    CalmnessCheckedDomain:
      type: string
      enum:
        - tenant_access
        - governance
        - findings
        - compare
        - backup_health
        - recovery_evidence
        - operations
        - alerts
    WorkspaceRecoveryOverviewBundle:
      type: object
      required:
        - accessible_tenant_count
        - summary_metrics
        - attention_items
        - calmness
      properties:
        accessible_tenant_count:
          type: integer
          minimum: 0
        summary_metrics:
          type: array
          items:
            $ref: '#/components/schemas/WorkspaceSummaryMetric'
        attention_items:
          type: array
          items:
            $ref: '#/components/schemas/WorkspaceAttentionItem'
        calmness:
          $ref: '#/components/schemas/WorkspaceCalmness'
    WorkspaceSummaryMetric:
      type: object
      required:
        - key
        - label
        - value
        - category
        - description
      properties:
        key:
          $ref: '#/components/schemas/WorkspaceMetricKey'
        label:
          type: string
        value:
          type: integer
          minimum: 0
        category:
          $ref: '#/components/schemas/MetricCategory'
        description:
          type: string
        destination:
          anyOf:
            - $ref: '#/components/schemas/Destination'
            - type: 'null'
    WorkspaceAttentionItem:
      type: object
      required:
        - key
        - tenant_id
        - tenant_label
        - family
        - urgency
        - title
        - body
        - destination
      properties:
        key:
          type: string
        tenant_id:
          type: integer
        tenant_label:
          type: string
        family:
          $ref: '#/components/schemas/AttentionFamily'
        urgency:
          $ref: '#/components/schemas/AttentionUrgency'
        title:
          type: string
        body:
          type: string
        supporting_message:
          anyOf:
            - type: string
            - type: 'null'
        reason_context:
          anyOf:
            - $ref: '#/components/schemas/ReasonContext'
            - type: 'null'
        destination:
          $ref: '#/components/schemas/Destination'
    ReasonContext:
      type: object
      required:
        - family
        - state
      properties:
        family:
          $ref: '#/components/schemas/AttentionFamily'
        state:
          type: string
          enum:
            - absent
            - stale
            - degraded
            - weakened
            - unvalidated
        reason:
          anyOf:
            - type: string
            - type: 'null'
    Destination:
      type: object
      required:
        - kind
        - label
        - disabled
      properties:
        kind:
          $ref: '#/components/schemas/DestinationKind'
        label:
          type: string
        url:
          anyOf:
            - type: string
            - type: 'null'
        disabled:
          type: boolean
        helper_text:
          anyOf:
            - type: string
            - type: 'null'
    WorkspaceCalmness:
      type: object
      required:
        - is_calm
        - checked_domains
        - title
        - body
        - next_action
      properties:
        is_calm:
          type: boolean
        checked_domains:
          type: array
          items:
            $ref: '#/components/schemas/CalmnessCheckedDomain'
        title:
          type: string
        body:
          type: string
        next_action:
          $ref: '#/components/schemas/Destination'