openapi: 3.1.0
info:
  title: Reason Resolution Logical Contract
  version: 0.1.0
  summary: Logical contract for resolving stable internal reason codes into operator-facing explanation envelopes.
  description: |
    This contract is logical rather than transport-prescriptive. It describes the
    expected behavior of existing presenters, notifications, banners, summaries,
    and Filament surfaces that consume translated reason state.
servers:
  - url: https://tenantpilot.local
paths:
  /contracts/reasons/resolve:
    post:
      summary: Resolve one internal reason code into an operator-facing explanation envelope
      operationId: resolveReason
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ReasonResolutionRequest'
      responses:
        '200':
          description: Operator-facing reason resolved
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ReasonResolutionEnvelope'
              examples:
                providerConsentMissing:
                  value:
                    internalCode: provider_consent_missing
                    operatorLabel: Admin consent required
                    shortExplanation: The provider connection cannot continue until admin consent is granted.
                    actionability: prerequisite_missing
                    showNoActionNeeded: false
                    nextSteps:
                      - label: Grant admin consent
                        kind: link
                        destination: admin-consent-url
                        authorizationRequired: true
                        scope: tenant
                    diagnosticCodeLabel: provider_consent_missing
  /contracts/reasons/validate-adoption:
    post:
      summary: Validate an adopted surface's translated reason payloads
      operationId: validateReasonAdoption
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ReasonAdoptionValidationRequest'
      responses:
        '200':
          description: Validation result for an adopted reason surface
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ReasonAdoptionValidationResponse'
components:
  schemas:
    ReasonResolutionRequest:
      type: object
      additionalProperties: false
      required:
        - artifactKey
        - internalCode
        - surfaceType
      properties:
        artifactKey:
          type: string
          example: provider_reason_codes
        internalCode:
          type: string
          example: provider_consent_missing
        surfaceType:
          type: string
          enum:
            - notification
            - run_detail
            - banner
            - summary_line
            - table
            - helper_copy
        includeDiagnostics:
          type: boolean
          default: false
        actorIsEntitled:
          type: boolean
          default: true
    ReasonResolutionEnvelope:
      type: object
      additionalProperties: false
      required:
        - internalCode
        - operatorLabel
        - shortExplanation
        - actionability
        - showNoActionNeeded
        - nextSteps
      properties:
        internalCode:
          type: string
          example: provider_consent_missing
        operatorLabel:
          type: string
          example: Admin consent required
        shortExplanation:
          type: string
          example: The provider connection cannot continue until admin consent is granted.
        actionability:
          type: string
          enum:
            - retryable_transient
            - permanent_configuration
            - prerequisite_missing
            - non_actionable
        showNoActionNeeded:
          type: boolean
        nextSteps:
          type: array
          items:
            $ref: '#/components/schemas/NextStepOption'
        diagnosticCodeLabel:
          type:
            - string
            - 'null'
    NextStepOption:
      type: object
      additionalProperties: false
      required:
        - label
        - kind
        - authorizationRequired
        - scope
      properties:
        label:
          type: string
        kind:
          type: string
          enum:
            - link
            - instruction
            - diagnostic_only
        destination:
          type:
            - string
            - 'null'
        authorizationRequired:
          type: boolean
        scope:
          type: string
          enum:
            - tenant
            - workspace
            - system
            - none
    ReasonAdoptionValidationRequest:
      type: object
      additionalProperties: false
      required:
        - target
        - envelopes
      properties:
        target:
          type: string
          example: operations_notifications
        envelopes:
          type: array
          items:
            $ref: '#/components/schemas/ReasonResolutionEnvelope'
    ReasonAdoptionValidationResponse:
      type: object
      additionalProperties: false
      required:
        - valid
        - violations
      properties:
        valid:
          type: boolean
        violations:
          type: array
          items:
            type: object
            additionalProperties: false
            required:
              - code
              - message
            properties:
              code:
                type: string
                enum:
                  - raw_code_primary_exposure
                  - missing_actionability_class
                  - missing_required_next_step
                  - unauthorized_next_step_exposure
                  - fallback_overuse
              message:
                type: string