openapi: 3.0.3
info:
  title: ORAG API
  version: 0.1.0-beta.1
  description: Go RAG framework API backed by Hertz, Eino Graph, Ark/Doubao, Qdrant, and PostgreSQL.
servers:
  - url: http://localhost:8080
tags:
  - name: System
  - name: Auth
  - name: APIKeys
  - name: Projects
  - name: Tutorials
  - name: KnowledgeBases
  - name: Ingestion
  - name: Query
  - name: Trace
  - name: Evaluation
  - name: OfflineKnowledge
x-orag-agent-capabilities:
  version: 1
  source: openapi
  generation_boundary:
    mcp_tool_schema: true
    skill_manifests: true
    runtime_handlers: false
    note: Ralph Loop is a planned-only agent capability in this manifest; MCP/Skill artifacts can be generated from it, but the ORAG HTTP router intentionally does not register POST /v1/ralph-loop until a real runtime handler is implemented.
  capabilities:
    - id: ralph-loop
      name: Ralph Loop
      description: Run or resume a bounded Ralph Loop verification workflow for an ORAG task/spec and return the verdict, artifacts, and trace identifier.
      status: planned
      maturity: experimental
      source:
        kind: planned_http
        method: POST
        path: /v1/ralph-loop
        operation_id: runRalphLoop
        backing_services:
          - optimization service
          - evaluation service
          - trace service
      auth:
        required: true
        scheme: bearerAuth
        env:
          - ORAG_API_BASE_URL
          - ORAG_API_TOKEN
          - ORAG_TENANT_ID
      trace:
        request_header: X-Trace-ID
        response_header: X-Trace-ID
        response_field: trace_id
      schemas:
        input: "#/components/schemas/RalphLoopRequest"
        output: "#/components/schemas/RalphLoopResponse"
        error: "#/components/schemas/ErrorResponse"
      mcp:
        tool_name: ralph_loop_run
        description: Execute a Ralph Loop task verification workflow through the ORAG API contract.
        input_schema: "#/components/schemas/RalphLoopRequest"
        output_schema: "#/components/schemas/RalphLoopResponse"
      skills:
        manifest_name: ralph-loop
        description: Use when an agent needs to run bounded ORAG Ralph Loop verification from a spec/task path and report a PASS/FAIL verdict with trace evidence.
      examples:
        - name: verify-task1
          input:
            task_spec_path: .trae/specs/add-ralph-loop-mcp-skills/tasks.md
            task_id: Task 1
            mode: focused
            max_rounds: 1
          expected_output:
            status: completed
            verdict: pass
            trace_id: trace_ralph_loop_example
paths:
  /healthz:
    get:
      tags: [System]
      operationId: healthz
      x-orag-maturity: experimental
      responses:
        "200":
          description: Process is alive.
          content:
            application/json:
              schema:
                type: object
                required: [status]
                properties:
                  status:
                    type: string
                    example: ok
  /readyz:
    get:
      tags: [System]
      operationId: readyz
      x-orag-maturity: experimental
      responses:
        "200":
          description: Service dependencies are ready.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ReadinessResponse"
        "503":
          description: One or more dependencies are not ready.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ReadinessResponse"
  /metrics:
    get:
      tags: [System]
      operationId: metrics
      x-orag-maturity: experimental
      responses:
        "200":
          description: Prometheus text metrics.
          content:
            text/plain:
              schema:
                type: string
  /version:
    get:
      tags: [System]
      operationId: version
      x-orag-maturity: beta
      responses:
        "200":
          description: Immutable build and release identity.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/BuildInfo"
  /docs:
    get:
      tags: [System]
      operationId: docs
      x-orag-maturity: beta
      responses:
        "200":
          description: Interactive API documentation powered by the canonical OpenAPI specification.
          content:
            text/html:
              schema:
                type: string
  /openapi.yaml:
    get:
      tags: [System]
      operationId: getOpenAPISpec
      x-orag-maturity: beta
      responses:
        "200":
          description: Canonical OpenAPI 3 specification.
          content:
            application/yaml:
              schema:
                type: string
  /v1/auth/login:
    post:
      tags: [Auth]
      operationId: login
      x-orag-maturity: experimental
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/LoginRequest"
      responses:
        "200":
          description: Bearer token.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/LoginResponse"
        "400":
          $ref: "#/components/responses/Error"
        "401":
          $ref: "#/components/responses/Error"
  /v1/knowledge-bases:
    get:
      tags: [KnowledgeBases]
      operationId: listKnowledgeBases
      x-orag-maturity: experimental
      security:
        - bearerAuth: []
      responses:
        "200":
          description: Knowledge base list.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/KnowledgeBaseListResponse"
        "401":
          $ref: "#/components/responses/Error"
        "500":
          $ref: "#/components/responses/Error"
    post:
      tags: [KnowledgeBases]
      operationId: createKnowledgeBase
      x-orag-maturity: experimental
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateKnowledgeBaseRequest"
      responses:
        "201":
          description: Created knowledge base.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/KnowledgeBase"
        "400":
          $ref: "#/components/responses/Error"
        "401":
          $ref: "#/components/responses/Error"
        "500":
          $ref: "#/components/responses/Error"
  /v1/knowledge-bases/{id}:
    get:
      tags: [KnowledgeBases]
      operationId: getKnowledgeBase
      x-orag-maturity: experimental
      security:
        - bearerAuth: []
      parameters:
        - $ref: "#/components/parameters/ID"
      responses:
        "200":
          description: Knowledge base.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/KnowledgeBase"
        "401":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
        "503":
          $ref: "#/components/responses/Error"
        "500":
          $ref: "#/components/responses/Error"
    delete:
      tags: [KnowledgeBases]
      operationId: deleteKnowledgeBase
      x-orag-maturity: experimental
      security:
        - bearerAuth: []
      parameters:
        - $ref: "#/components/parameters/ID"
      responses:
        "204":
          description: Tenant-scoped knowledge base deleted; directly owned documents, chunks, ingestion jobs, vector points, and semantic-cache entries are removed.
        "401":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
        "500":
          $ref: "#/components/responses/Error"
  /v1/knowledge-bases/{id}/documents:
    post:
      tags: [Ingestion]
      operationId: uploadDocument
      x-orag-maturity: experimental
      security:
        - bearerAuth: []
      parameters:
        - $ref: "#/components/parameters/ID"
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              required: [file]
              properties:
                file:
                  type: string
                  format: binary
      responses:
        "202":
          description: Document ingestion accepted.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/IngestionResponse"
        "400":
          $ref: "#/components/responses/Error"
        "401":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
        "413":
          $ref: "#/components/responses/Error"
  /v1/knowledge-bases/{id}/uploads:
    post:
      tags: [Ingestion]
      operationId: createUploadSession
      x-orag-maturity: experimental
      security:
        - bearerAuth: []
      parameters:
        - $ref: "#/components/parameters/ID"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateUploadRequest"
      responses:
        "201":
          description: Resumable upload session created.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/UploadSession"
        "400":
          $ref: "#/components/responses/Error"
        "401":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
        "413":
          $ref: "#/components/responses/Error"
  /v1/uploads/{id}:
    get:
      tags: [Ingestion]
      operationId: getUploadSession
      x-orag-maturity: experimental
      security:
        - bearerAuth: []
      parameters:
        - $ref: "#/components/parameters/ID"
      responses:
        "200":
          description: Current resumable upload session state.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/UploadSession"
        "401":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
    put:
      tags: [Ingestion]
      operationId: appendUploadChunk
      x-orag-maturity: experimental
      security:
        - bearerAuth: []
      parameters:
        - $ref: "#/components/parameters/ID"
        - in: header
          name: Upload-Offset
          required: true
          schema:
            type: integer
            minimum: 0
          description: Zero-based byte offset where this chunk must be appended.
      requestBody:
        required: true
        content:
          application/octet-stream:
            schema:
              type: string
              format: binary
      responses:
        "200":
          description: Chunk accepted and session offset advanced.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/UploadSession"
        "400":
          $ref: "#/components/responses/Error"
        "401":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
        "409":
          $ref: "#/components/responses/Error"
        "413":
          $ref: "#/components/responses/Error"
    delete:
      tags: [Ingestion]
      operationId: cancelUploadSession
      x-orag-maturity: experimental
      security:
        - bearerAuth: []
      parameters:
        - $ref: "#/components/parameters/ID"
      responses:
        "204":
          description: Upload session canceled and temporary content removed.
        "401":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
  "/v1/uploads/{id}:complete":
    post:
      tags: [Ingestion]
      operationId: completeUploadSession
      x-orag-maturity: experimental
      security:
        - bearerAuth: []
      parameters:
        - $ref: "#/components/parameters/ID"
      responses:
        "202":
          description: Upload completed and document ingestion accepted.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CompleteUploadResponse"
        "401":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
        "409":
          $ref: "#/components/responses/Error"
  "/v1/knowledge-bases/{id}/documents:import":
    post:
      tags: [Ingestion]
      operationId: importDocument
      x-orag-maturity: experimental
      security:
        - bearerAuth: []
      parameters:
        - $ref: "#/components/parameters/ID"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ImportDocumentRequest"
      responses:
        "202":
          description: Document ingestion accepted.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/IngestionResponse"
        "400":
          $ref: "#/components/responses/Error"
        "401":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
        "413":
          $ref: "#/components/responses/Error"
  /v1/ingestion-jobs/{id}:
    get:
      tags: [Ingestion]
      operationId: getIngestionJob
      x-orag-maturity: experimental
      security:
        - bearerAuth: []
      parameters:
        - $ref: "#/components/parameters/ID"
      responses:
        "200":
          description: Ingestion job.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/IngestionJob"
        "401":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
  /v1/query:
    post:
      tags: [Query]
      operationId: query
      x-orag-maturity: experimental
      description: |
        Executes a RAG query. The response includes `trace_id`, which is the
        same request trace propagated through logs and persisted PostgreSQL
        RAG trace records. Use `GET /v1/traces/{trace_id}` or
        `oragctl trace --trace-id <trace_id>` to inspect persisted trace
        metadata and node spans. For a project-owned knowledge base, execution
        is resolved server-side from that project's active production version;
        callers cannot select a version or pipeline definition.
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/QueryRequest"
      responses:
        "200":
          description: RAG query response.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/QueryResponse"
        "400":
          $ref: "#/components/responses/Error"
        "401":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
        "409":
          description: The project has no active production version, or its frozen definition is invalid.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "500":
          $ref: "#/components/responses/Error"
  "/v1/query:stream":
    post:
      tags: [Query]
      operationId: queryStream
      x-orag-maturity: experimental
      description: |
        Server-Sent Events stream. Events are emitted as `trace`, `chunk`,
        `citations`, `done`, or `error`. The first `trace` event carries the
        request `trace_id`; if the RAG phase fails, the `error` event uses the
        same `trace_id` as JSON error responses and service logs.
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/QueryRequest"
      responses:
        "200":
          description: SSE query stream.
          content:
            text/event-stream:
              schema:
                type: string
        "400":
          $ref: "#/components/responses/Error"
        "401":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
        "409":
          description: SSE error event for a missing or invalid active production version.
          content:
            text/event-stream:
              schema:
                type: string
        "500":
          description: SSE error event.
          content:
            text/event-stream:
              schema:
                type: string
  /v1/traces:
    get:
      tags: [Trace]
      operationId: listTraces
      x-orag-maturity: experimental
      description: Lists persisted RAG traces for the current tenant.
      security:
        - bearerAuth: []
      parameters:
        - name: profile
          in: query
          schema:
            $ref: "#/components/schemas/Profile"
        - name: since
          in: query
          schema:
            type: string
            format: date-time
        - name: until
          in: query
          schema:
            type: string
            format: date-time
        - name: has_error
          in: query
          schema:
            type: boolean
        - name: slow_ms
          in: query
          schema:
            type: integer
            format: int64
            minimum: 0
        - name: limit
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 500
      responses:
        "200":
          description: Trace list.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TraceListResponse"
        "400":
          $ref: "#/components/responses/Error"
        "401":
          $ref: "#/components/responses/Error"
        "500":
          $ref: "#/components/responses/Error"
  /v1/traces:stats:
    get:
      tags: [Trace]
      operationId: traceStats
      x-orag-maturity: experimental
      description: Returns node-level latency and error statistics for persisted RAG traces in the current tenant.
      security:
        - bearerAuth: []
      parameters:
        - name: profile
          in: query
          schema:
            $ref: "#/components/schemas/Profile"
        - name: since
          in: query
          schema:
            type: string
            format: date-time
        - name: until
          in: query
          schema:
            type: string
            format: date-time
        - name: has_error
          in: query
          schema:
            type: boolean
        - name: slow_ms
          in: query
          schema:
            type: integer
            format: int64
            minimum: 0
        - name: limit
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 500
      responses:
        "200":
          description: Trace node statistics.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TraceStatsResponse"
        "400":
          $ref: "#/components/responses/Error"
        "401":
          $ref: "#/components/responses/Error"
        "500":
          $ref: "#/components/responses/Error"
  /v1/traces/{trace_id}:
    get:
      tags: [Trace]
      operationId: getTrace
      x-orag-maturity: experimental
      description: Returns a persisted RAG trace and its node spans for the current tenant.
      security:
        - bearerAuth: []
      parameters:
        - $ref: "#/components/parameters/TraceID"
      responses:
        "200":
          description: Trace record.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TraceRecord"
        "401":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
        "500":
          $ref: "#/components/responses/Error"
  /v1/api-keys:
    get:
      tags: [APIKeys]
      operationId: listAPIKeys
      x-orag-maturity: experimental
      description: Lists machine credential metadata for the current tenant. Secrets and hashes are never returned.
      security:
        - bearerAuth: []
      responses:
        "200":
          description: Tenant API key metadata ordered by newest first.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/APIKeyListResponse"
        "401":
          $ref: "#/components/responses/Error"
        "403":
          $ref: "#/components/responses/Error"
        "500":
          $ref: "#/components/responses/Error"
    post:
      tags: [APIKeys]
      operationId: createAPIKey
      x-orag-maturity: experimental
      description: Creates a machine credential. The full secret is returned exactly once in this response.
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateAPIKeyRequest"
      responses:
        "201":
          description: API key metadata and its one-time secret.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CreateAPIKeyResponse"
        "400":
          $ref: "#/components/responses/Error"
        "401":
          $ref: "#/components/responses/Error"
        "403":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
        "500":
          $ref: "#/components/responses/Error"
  /v1/api-keys/{api_key_id}:
    parameters:
      - name: api_key_id
        in: path
        required: true
        schema:
          type: string
    delete:
      tags: [APIKeys]
      operationId: revokeAPIKey
      x-orag-maturity: experimental
      description: Idempotently revokes an API key owned by the current tenant.
      security:
        - bearerAuth: []
      responses:
        "204":
          description: API key revoked.
        "401":
          $ref: "#/components/responses/Error"
        "403":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
        "500":
          $ref: "#/components/responses/Error"
  /v1/api-keys/{api_key_id}/rotate:
    parameters:
      - name: api_key_id
        in: path
        required: true
        schema:
          type: string
    post:
      tags: [APIKeys]
      operationId: rotateAPIKey
      x-orag-maturity: experimental
      description: Atomically creates a successor and immediately revokes the source key. The full replacement secret is returned exactly once.
      security:
        - bearerAuth: []
      responses:
        "201":
          description: Replacement API key metadata and its one-time secret.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CreateAPIKeyResponse"
        "401":
          $ref: "#/components/responses/Error"
        "403":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
        "500":
          $ref: "#/components/responses/Error"
  /v1/projects:
    get:
      tags: [Projects]
      operationId: listProjects
      x-orag-maturity: experimental
      security:
        - bearerAuth: []
      responses:
        "200":
          description: Tenant-scoped projects.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProjectListResponse"
        "401":
          $ref: "#/components/responses/Error"
        "403":
          $ref: "#/components/responses/Error"
        "500":
          $ref: "#/components/responses/Error"
    post:
      tags: [Projects]
      operationId: createProject
      x-orag-maturity: experimental
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateProjectRequest"
      responses:
        "201":
          description: Project created with default development, staging, and production environments.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Project"
        "400":
          $ref: "#/components/responses/Error"
        "401":
          $ref: "#/components/responses/Error"
        "403":
          $ref: "#/components/responses/Error"
        "409":
          $ref: "#/components/responses/Error"
        "500":
          $ref: "#/components/responses/Error"
  /v1/projects/{project_id}:
    parameters:
      - name: project_id
        in: path
        required: true
        schema:
          type: string
    get:
      tags: [Projects]
      operationId: getProject
      x-orag-maturity: experimental
      description: Returns `404 project_not_found` when the project does not exist or belongs to another tenant.
      security:
        - bearerAuth: []
      responses:
        "200":
          description: Project.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Project"
        "401":
          $ref: "#/components/responses/Error"
        "403":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
        "500":
          $ref: "#/components/responses/Error"
    patch:
      tags: [Projects]
      operationId: updateProject
      x-orag-maturity: experimental
      description: Returns `404 project_not_found` when the project does not exist or belongs to another tenant.
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/UpdateProjectRequest"
      responses:
        "200":
          description: Updated project.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Project"
        "400":
          $ref: "#/components/responses/Error"
        "401":
          $ref: "#/components/responses/Error"
        "403":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
        "409":
          $ref: "#/components/responses/Error"
        "500":
          $ref: "#/components/responses/Error"
  /v1/projects/{project_id}/environments:
    parameters:
      - name: project_id
        in: path
        required: true
        schema: {type: string}
    get:
      tags: [Releases]
      operationId: listProjectEnvironments
      x-orag-maturity: experimental
      security: [{bearerAuth: []}]
      responses:
        "200":
          description: Ordered development, staging, and production environments.
          content: {application/json: {schema: {$ref: "#/components/schemas/EnvironmentListResponse"}}}
        "401": {$ref: "#/components/responses/Error"}
        "403": {$ref: "#/components/responses/Error"}
        "404": {$ref: "#/components/responses/Error"}
  /v1/projects/{project_id}/environments/{environment}/binding:
    parameters:
      - name: project_id
        in: path
        required: true
        schema: {type: string}
      - name: environment
        in: path
        required: true
        schema: {type: string, enum: [development, staging, production]}
    put:
      tags: [Releases]
      operationId: bindProjectEnvironment
      x-orag-maturity: experimental
      description: Stores an opaque server-side environment resource reference. The reference is write-only and never returned by this API.
      security: [{bearerAuth: []}]
      requestBody:
        required: true
        content: {application/json: {schema: {$ref: "#/components/schemas/BindEnvironmentRequest"}}}
      responses:
        "200": {description: Environment binding state without the stored reference., content: {application/json: {schema: {$ref: "#/components/schemas/Environment"}}}}
        "400": {$ref: "#/components/responses/Error"}
        "401": {$ref: "#/components/responses/Error"}
        "403": {$ref: "#/components/responses/Error"}
        "404": {$ref: "#/components/responses/Error"}
  /v1/pipeline-node-definitions:
    get:
      tags: [Pipelines]
      operationId: listPipelineNodeDefinitions
      x-orag-maturity: beta
      security: [{bearerAuth: []}]
      responses:
        "200":
          description: Server-owned node definitions allowed in RAG Studio.
          content: {application/json: {schema: {$ref: "#/components/schemas/PipelineNodeDefinitionListResponse"}}}
        "401": {$ref: "#/components/responses/Error"}
        "403": {$ref: "#/components/responses/Error"}
  /v1/projects/{project_id}/pipelines:
    parameters:
      - name: project_id
        in: path
        required: true
        schema: {type: string}
    get:
      tags: [Pipelines]
      operationId: listProjectPipelines
      x-orag-maturity: experimental
      security: [{bearerAuth: []}]
      responses:
        "200": {description: Project pipelines., content: {application/json: {schema: {$ref: "#/components/schemas/PipelineListResponse"}}}}
        "401": {$ref: "#/components/responses/Error"}
        "403": {$ref: "#/components/responses/Error"}
    post:
      tags: [Pipelines]
      operationId: createProjectPipeline
      x-orag-maturity: experimental
      security: [{bearerAuth: []}]
      requestBody:
        required: true
        content: {application/json: {schema: {$ref: "#/components/schemas/CreatePipelineRequest"}}}
      responses:
        "201": {description: Created pipeline., content: {application/json: {schema: {$ref: "#/components/schemas/Pipeline"}}}}
        "400": {$ref: "#/components/responses/Error"}
        "401": {$ref: "#/components/responses/Error"}
        "403": {$ref: "#/components/responses/Error"}
  /v1/projects/{project_id}/pipelines/{pipeline_id}/draft:
    parameters:
      - name: project_id
        in: path
        required: true
        schema: {type: string}
      - name: pipeline_id
        in: path
        required: true
        schema: {type: string}
    get:
      tags: [Pipelines]
      operationId: getProjectPipelineDraft
      x-orag-maturity: experimental
      security: [{bearerAuth: []}]
      responses:
        "200": {description: Current revisioned draft., content: {application/json: {schema: {$ref: "#/components/schemas/PipelineDraft"}}}}
        "401": {$ref: "#/components/responses/Error"}
        "403": {$ref: "#/components/responses/Error"}
        "404": {$ref: "#/components/responses/Error"}
    put:
      tags: [Pipelines]
      operationId: saveProjectPipelineDraft
      x-orag-maturity: experimental
      security: [{bearerAuth: []}]
      requestBody:
        required: true
        content: {application/json: {schema: {$ref: "#/components/schemas/SavePipelineDraftRequest"}}}
      responses:
        "200": {description: Saved draft., content: {application/json: {schema: {$ref: "#/components/schemas/PipelineDraft"}}}}
        "409": {$ref: "#/components/responses/Error"}
        "422": {$ref: "#/components/responses/Error"}
  /v1/projects/{project_id}/pipelines/{pipeline_id}/versions:
    parameters:
      - name: project_id
        in: path
        required: true
        schema: {type: string}
      - name: pipeline_id
        in: path
        required: true
        schema: {type: string}
    post:
      tags: [Releases]
      operationId: createPipelineVersionFromDraft
      x-orag-maturity: beta
      description: Freezes the requested draft revision, including its complete DAG definition and source pipeline, into an immutable content-hashed pipeline version.
      security: [{bearerAuth: []}]
      requestBody:
        required: true
        content: {application/json: {schema: {$ref: "#/components/schemas/CreatePipelineVersionFromDraftRequest"}}}
      responses:
        "201": {description: Immutable pipeline version., content: {application/json: {schema: {$ref: "#/components/schemas/PipelineVersionFromDraftResponse"}}}}
        "401": {$ref: "#/components/responses/Error"}
        "403": {$ref: "#/components/responses/Error"}
        "404": {$ref: "#/components/responses/Error"}
        "409": {$ref: "#/components/responses/Error"}
  /v1/projects/{project_id}/query:debug:
    parameters:
      - name: project_id
        in: path
        required: true
        schema: {type: string}
    post:
      tags: [Pipelines]
      operationId: debugProjectQuery
      x-orag-maturity: beta
      description: Executes a frozen development pipeline draft and returns safe ordered diagnostic events.
      security: [{bearerAuth: []}]
      requestBody:
        required: true
        content: {application/json: {schema: {$ref: "#/components/schemas/PipelineDebugRequest"}}}
      responses:
        "200": {description: Debug response with trace-linked diagnostics., content: {application/json: {schema: {$ref: "#/components/schemas/PipelineDebugResponse"}}}}
        "400": {$ref: "#/components/responses/Error"}
        "401": {$ref: "#/components/responses/Error"}
        "403": {$ref: "#/components/responses/Error"}
        "404": {$ref: "#/components/responses/Error"}
        "409": {$ref: "#/components/responses/Error"}
        "422": {$ref: "#/components/responses/Error"}
  /v1/projects/{project_id}/debug-runs/{run_id}/save-case:
    parameters:
      - name: project_id
        in: path
        required: true
        schema: {type: string}
      - name: run_id
        in: path
        required: true
        schema: {type: string}
    post:
      tags: [Evaluation]
      operationId: saveDebugRunAsCase
      x-orag-maturity: beta
      description: Saves a debug request as an evaluation dataset item while preserving its run/trace identifier.
      security: [{bearerAuth: []}]
      requestBody:
        required: true
        content: {application/json: {schema: {$ref: "#/components/schemas/SaveDebugCaseRequest"}}}
      responses:
        "201": {description: Saved evaluation case., content: {application/json: {schema: {$ref: "#/components/schemas/SavedDebugCase"}}}}
        "400": {$ref: "#/components/responses/Error"}
        "401": {$ref: "#/components/responses/Error"}
        "403": {$ref: "#/components/responses/Error"}
        "404": {$ref: "#/components/responses/Error"}
  /v1/projects/{project_id}/evaluation-policies:
    parameters:
      - name: project_id
        in: path
        required: true
        schema: {type: string}
    get:
      tags: [Evaluation]
      operationId: listProjectEvaluationPolicies
      x-orag-maturity: experimental
      description: Lists immutable metric-gate policies for one project.
      security: [{bearerAuth: []}]
      responses:
        "200": {description: Project evaluation policies., content: {application/json: {schema: {$ref: "#/components/schemas/EvaluationPolicyListResponse"}}}}
        "401": {$ref: "#/components/responses/Error"}
        "403": {$ref: "#/components/responses/Error"}
    post:
      tags: [Evaluation]
      operationId: createProjectEvaluationPolicy
      x-orag-maturity: experimental
      description: Creates an immutable policy revision with server-validated metric gates and a project-owned dataset.
      security: [{bearerAuth: []}]
      requestBody:
        required: true
        content: {application/json: {schema: {$ref: "#/components/schemas/CreateEvaluationPolicyRequest"}}}
      responses:
        "201": {description: Immutable evaluation policy., content: {application/json: {schema: {$ref: "#/components/schemas/EvaluationPolicy"}}}}
        "401": {$ref: "#/components/responses/Error"}
        "403": {$ref: "#/components/responses/Error"}
        "404": {$ref: "#/components/responses/Error"}
        "422": {$ref: "#/components/responses/Error"}
  /v1/projects/{project_id}/releases:
    parameters:
      - name: project_id
        in: path
        required: true
        schema: {type: string}
    get:
      tags: [Releases]
      operationId: listProjectReleases
      x-orag-maturity: experimental
      security: [{bearerAuth: []}]
      responses:
        "200":
          description: Append-only promotion and rollback history.
          content: {application/json: {schema: {$ref: "#/components/schemas/ReleaseListResponse"}}}
        "401": {$ref: "#/components/responses/Error"}
        "403": {$ref: "#/components/responses/Error"}
        "404": {$ref: "#/components/responses/Error"}
  /v1/projects/{project_id}/versions/{version_id}/evaluation-evidence:
    parameters:
      - name: project_id
        in: path
        required: true
        schema: {type: string}
      - name: version_id
        in: path
        required: true
        schema: {type: string}
    post:
      tags: [Evaluation]
      operationId: recordProjectEvaluationEvidence
      x-orag-maturity: experimental
      description: Derives append-only gate evidence and the target-environment release validation for a pipeline version from a completed server-side evaluation run and immutable policy revision.
      security: [{bearerAuth: []}]
      requestBody:
        required: true
        content: {application/json: {schema: {$ref: "#/components/schemas/RecordEvaluationEvidenceRequest"}}}
      responses:
        "201": {description: Server-derived immutable evaluation evidence., content: {application/json: {schema: {$ref: "#/components/schemas/EvaluationEvidence"}}}}
        "401": {$ref: "#/components/responses/Error"}
        "403": {$ref: "#/components/responses/Error"}
        "404": {$ref: "#/components/responses/Error"}
        "422": {$ref: "#/components/responses/Error"}
  /v1/projects/{project_id}/versions:
    parameters:
      - name: project_id
        in: path
        required: true
        schema: {type: string}
    get:
      tags: [Releases]
      operationId: listProjectPipelineVersions
      x-orag-maturity: experimental
      security: [{bearerAuth: []}]
      responses:
        "200":
          description: Immutable pipeline versions for the project.
          content: {application/json: {schema: {$ref: "#/components/schemas/PipelineVersionListResponse"}}}
        "401": {$ref: "#/components/responses/Error"}
        "403": {$ref: "#/components/responses/Error"}
        "404": {$ref: "#/components/responses/Error"}
    post:
      tags: [Releases]
      operationId: createProjectPipelineVersion
      x-orag-maturity: experimental
      security: [{bearerAuth: []}]
      requestBody:
        required: true
        content: {application/json: {schema: {$ref: "#/components/schemas/CreatePipelineVersionRequest"}}}
      responses:
        "201":
          description: Immutable pipeline version.
          content: {application/json: {schema: {$ref: "#/components/schemas/PipelineVersion"}}}
        "400": {$ref: "#/components/responses/Error"}
        "401": {$ref: "#/components/responses/Error"}
        "403": {$ref: "#/components/responses/Error"}
        "409": {$ref: "#/components/responses/Error"}
  /v1/projects/{project_id}/versions/{version_id}/validations:
    parameters:
      - name: project_id
        in: path
        required: true
        schema: {type: string}
      - name: version_id
        in: path
        required: true
        schema: {type: string}
    post:
      tags: [Releases]
      operationId: validateProjectPipelineVersion
      x-orag-maturity: experimental
      security: [{bearerAuth: []}]
      requestBody:
        required: true
        content: {application/json: {schema: {$ref: "#/components/schemas/ValidatePipelineVersionRequest"}}}
      responses:
        "201":
          description: Recorded release evidence.
          content: {application/json: {schema: {$ref: "#/components/schemas/PipelineValidation"}}}
        "400": {$ref: "#/components/responses/Error"}
        "401": {$ref: "#/components/responses/Error"}
        "403": {$ref: "#/components/responses/Error"}
        "404": {$ref: "#/components/responses/Error"}
        "422": {$ref: "#/components/responses/Error"}
  /v1/projects/{project_id}/releases:promote:
    parameters:
      - name: project_id
        in: path
        required: true
        schema: {type: string}
    post:
      tags: [Releases]
      operationId: promoteProjectRelease
      x-orag-maturity: experimental
      security: [{bearerAuth: []}]
      requestBody:
        required: true
        content: {application/json: {schema: {$ref: "#/components/schemas/PromoteReleaseRequest"}}}
      responses:
        "201":
          description: Promotion audit record.
          content: {application/json: {schema: {$ref: "#/components/schemas/Release"}}}
        "400": {$ref: "#/components/responses/Error"}
        "401": {$ref: "#/components/responses/Error"}
        "403": {$ref: "#/components/responses/Error"}
        "409": {$ref: "#/components/responses/Error"}
        "422": {$ref: "#/components/responses/Error"}
  /v1/projects/{project_id}/environments/development/activate:
    parameters:
      - name: project_id
        in: path
        required: true
        schema: {type: string}
    post:
      tags: [Releases]
      operationId: activateProjectDevelopment
      x-orag-maturity: experimental
      description: Activates an immutable pipeline version in development after server-derived development evidence passes. The fixed path prevents using activation to skip the ordered environment lifecycle.
      security: [{bearerAuth: []}]
      requestBody:
        required: true
        content: {application/json: {schema: {$ref: "#/components/schemas/ActivateDevelopmentRequest"}}}
      responses:
        "201":
          description: Initial development activation audit record.
          content: {application/json: {schema: {$ref: "#/components/schemas/Release"}}}
        "400": {$ref: "#/components/responses/Error"}
        "401": {$ref: "#/components/responses/Error"}
        "403": {$ref: "#/components/responses/Error"}
        "404": {$ref: "#/components/responses/Error"}
        "409": {$ref: "#/components/responses/Error"}
        "422": {$ref: "#/components/responses/Error"}
  /v1/projects/{project_id}/environments/{environment}/rollback:
    parameters:
      - name: project_id
        in: path
        required: true
        schema: {type: string}
      - name: environment
        in: path
        required: true
        schema: {type: string, enum: [development, staging, production]}
    post:
      tags: [Releases]
      operationId: rollbackProjectEnvironment
      x-orag-maturity: experimental
      security: [{bearerAuth: []}]
      requestBody:
        required: true
        content: {application/json: {schema: {$ref: "#/components/schemas/RollbackReleaseRequest"}}}
      responses:
        "201":
          description: Rollback audit record.
          content: {application/json: {schema: {$ref: "#/components/schemas/Release"}}}
        "400": {$ref: "#/components/responses/Error"}
        "401": {$ref: "#/components/responses/Error"}
        "403": {$ref: "#/components/responses/Error"}
        "409": {$ref: "#/components/responses/Error"}
        "422": {$ref: "#/components/responses/Error"}
  /v1/tutorials:
    get:
      tags: [Tutorials]
      operationId: listTutorials
      x-orag-maturity: experimental
      description: Lists the latest immutable version of each system tutorial template.
      security:
        - bearerAuth: []
      responses:
        "200":
          description: The three end-to-end tutorial templates.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TutorialListResponse"
        "401":
          $ref: "#/components/responses/Error"
        "500":
          $ref: "#/components/responses/Error"
  /v1/tutorials/{template_id}:
    parameters:
      - name: template_id
        in: path
        required: true
        schema:
          type: string
          enum: [text-rag, visual-document-rag, video-rag]
    get:
      tags: [Tutorials]
      operationId: getTutorial
      x-orag-maturity: experimental
      description: Returns the latest immutable version, or `404 tutorial_not_found`.
      security:
        - bearerAuth: []
      responses:
        "200":
          description: Current tutorial template.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TutorialTemplate"
        "401":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
        "500":
          $ref: "#/components/responses/Error"
  /v1/tutorials/{template_id}/versions/{version}:
    parameters:
      - name: template_id
        in: path
        required: true
        schema:
          type: string
          enum: [text-rag, visual-document-rag, video-rag]
      - name: version
        in: path
        required: true
        schema:
          type: string
          pattern: '^[0-9]+\.[0-9]+\.[0-9]+$'
    get:
      tags: [Tutorials]
      operationId: getTutorialVersion
      x-orag-maturity: experimental
      description: Returns an immutable template version. Missing versions return `404 tutorial_version_not_found`.
      security:
        - bearerAuth: []
      responses:
        "200":
          description: Requested tutorial template version.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TutorialTemplate"
        "401":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
        "500":
          $ref: "#/components/responses/Error"
  /v1/tutorials/{template_id}/replay:
    parameters:
      - name: template_id
        in: path
        required: true
        schema:
          type: string
          enum: [text-rag, visual-document-rag, video-rag]
    get:
      tags: [Tutorials]
      operationId: getTutorialReplay
      x-orag-maturity: beta
      description: >-
        Returns the immutable, offline official Replay for a tutorial. It contains
        public aggregate facts from a controlled environment only; it never starts
        a model, creates a project, or returns private storage coordinates.
      security:
        - bearerAuth: []
      responses:
        "200":
          description: Immutable official tutorial Replay.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TutorialReplaySnapshot"
        "401":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
        "500":
          $ref: "#/components/responses/Error"
  /v1/tutorials/{template_id}/clones:
    parameters:
      - name: template_id
        in: path
        required: true
        schema:
          type: string
          enum: [text-rag, visual-document-rag, video-rag]
    post:
      tags: [Tutorials]
      operationId: startTutorialClone
      x-orag-maturity: experimental
      description: >-
        Creates an idempotent tutorial experiment project and asynchronously installs
        the selected immutable public pack into the configured private store. The API
        response never contains public manifest URLs, private object keys, or credentials.
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/StartTutorialCloneRequest"
      responses:
        "202":
          description: Clone job accepted. Poll the returned relative URL for progress.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TutorialCloneAcceptedResponse"
        "400":
          $ref: "#/components/responses/Error"
        "401":
          $ref: "#/components/responses/Error"
        "403":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
        "500":
          $ref: "#/components/responses/Error"
  /v1/tutorial-clone-jobs/{job_id}:
    parameters:
      - name: job_id
        in: path
        required: true
        schema:
          type: string
    get:
      tags: [Tutorials]
      operationId: getTutorialCloneJob
      x-orag-maturity: experimental
      description: Returns tenant-scoped clone progress and redacted failure codes.
      security:
        - bearerAuth: []
      responses:
        "200":
          description: Durable clone job.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TutorialCloneJob"
        "401":
          $ref: "#/components/responses/Error"
        "403":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
        "500":
          $ref: "#/components/responses/Error"
  /v1/tutorial-clone-jobs/{job_id}:retry:
    parameters:
      - name: job_id
        in: path
        required: true
        schema:
          type: string
    post:
      tags: [Tutorials]
      operationId: retryTutorialClone
      x-orag-maturity: experimental
      description: Requeues a failed tutorial clone job from the safe resume stage.
      security:
        - bearerAuth: []
      responses:
        "202":
          description: Clone job requeued.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TutorialCloneJob"
        "401":
          $ref: "#/components/responses/Error"
        "403":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
        "409":
          $ref: "#/components/responses/Error"
        "500":
          $ref: "#/components/responses/Error"
  /v1/projects/{project_id}/tutorial-experiment:
    parameters:
      - name: project_id
        in: path
        required: true
        schema:
          type: string
    get:
      tags: [Tutorials]
      operationId: getProjectTutorialExperiment
      x-orag-maturity: experimental
      description: Returns the tutorial experiment attached to a project, if one exists.
      security:
        - bearerAuth: []
      responses:
        "200":
          description: Tutorial experiment metadata, without storage locations.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TutorialExperiment"
        "401":
          $ref: "#/components/responses/Error"
        "403":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
        "500":
          $ref: "#/components/responses/Error"
  /v1/projects/{project_id}/tutorial-experiment/video-source:
    parameters:
      - name: project_id
        in: path
        required: true
        schema:
          type: string
    post:
      tags: [Tutorials]
      operationId: importTutorialVideoSource
      x-orag-maturity: experimental
      description: >-
        Imports an owner-authorized Video-MME source into the project's private
        store. The server verifies the supplied SHA-256 and byte count, derives
        fixed-cadence temporal evidence, and never accepts a URL, object key, or
        browser-selected sampling coordinate. This endpoint does not publish or
        redistribute media, subtitles, annotations, questions, or answers.
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              $ref: "#/components/schemas/ImportTutorialVideoSourceRequest"
      responses:
        "201":
          description: Verified private video source imported and deterministic temporal evidence staged.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ImportTutorialVideoSourceResponse"
        "400":
          $ref: "#/components/responses/Error"
        "401":
          $ref: "#/components/responses/Error"
        "403":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
        "500":
          $ref: "#/components/responses/Error"
  /v1/projects/{project_id}/tutorial-experiment/video-evaluation:
    parameters:
      - name: project_id
        in: path
        required: true
        schema: {type: string}
    post:
      tags: [Tutorials]
      operationId: activateTutorialVideoEvaluation
      x-orag-maturity: experimental
      description: >-
        Freezes an owner-authorized project evaluation dataset into a Video RAG
        experiment. Every expected evidence identifier must resolve to a private,
        deterministic temporal segment created by this project’s video import.
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ActivateTutorialVideoEvaluationRequest"
      responses:
        "200":
          description: Immutable evaluation snapshot and temporal P0 resource roots are ready.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ActivateTutorialVideoEvaluationResponse"
        "400":
          $ref: "#/components/responses/Error"
        "401":
          $ref: "#/components/responses/Error"
        "403":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
        "500":
          $ref: "#/components/responses/Error"
  /v1/projects/{project_id}/tutorial-experiments/{experiment_id}/runs:
    parameters:
      - name: project_id
        in: path
        required: true
        schema: {type: string}
      - name: experiment_id
        in: path
        required: true
        schema: {type: string}
    post:
      tags: [Tutorials]
      operationId: startTutorialExperimentRun
      x-orag-maturity: experimental
      description: >-
        Starts an idempotent server-declared Live Run for a text Pack whose verified
        runtime declaration has created project-owned resource roots. Candidate variants
        require a compatible completed baseline. The server reads only private Pack copies
        and derives all parser, resource, model, and evaluation inputs.
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/StartTutorialExperimentRunRequest"
      responses:
        "202":
          description: Durable experiment run accepted.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TutorialExperimentRunAcceptedResponse"
        "400":
          $ref: "#/components/responses/Error"
        "401":
          $ref: "#/components/responses/Error"
        "403":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
        "409":
          $ref: "#/components/responses/Error"
        "500":
          $ref: "#/components/responses/Error"
  /v1/projects/{project_id}/tutorial-experiments/{experiment_id}/runs/{run_id}:
    parameters:
      - name: project_id
        in: path
        required: true
        schema: {type: string}
      - name: experiment_id
        in: path
        required: true
        schema: {type: string}
      - name: run_id
        in: path
        required: true
        schema: {type: string}
    get:
      tags: [Tutorials]
      operationId: getTutorialExperimentRun
      x-orag-maturity: experimental
      description: Returns durable Live Run progress and redacted failure codes.
      security:
        - bearerAuth: []
      responses:
        "200":
          description: Tutorial experiment run.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TutorialExperimentRun"
        "401":
          $ref: "#/components/responses/Error"
        "403":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
        "500":
          $ref: "#/components/responses/Error"
  /v1/projects/{project_id}/tutorial-experiments/{experiment_id}/runs/{run_id}/comparison:
    parameters:
      - name: project_id
        in: path
        required: true
        schema: {type: string}
      - name: experiment_id
        in: path
        required: true
        schema: {type: string}
      - name: run_id
        in: path
        required: true
        schema: {type: string}
    get:
      tags: [Tutorials]
      operationId: getTutorialExperimentRunComparison
      x-orag-maturity: experimental
      description: >-
        Returns the server-verified P0/P1 comparison for a completed candidate. The
        response contains only persisted standard evaluation metrics and audit lineage.
      security:
        - bearerAuth: []
      responses:
        "200":
          description: Auditable tutorial experiment comparison.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TutorialExperimentRunComparison"
        "401":
          $ref: "#/components/responses/Error"
        "403":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
        "409":
          $ref: "#/components/responses/Error"
        "500":
          $ref: "#/components/responses/Error"
  /v1/projects/{project_id}/tutorial-experiments/{experiment_id}/runs/{run_id}:cancel:
    parameters:
      - name: project_id
        in: path
        required: true
        schema: {type: string}
      - name: experiment_id
        in: path
        required: true
        schema: {type: string}
      - name: run_id
        in: path
        required: true
        schema: {type: string}
    post:
      tags: [Tutorials]
      operationId: cancelTutorialExperimentRun
      x-orag-maturity: experimental
      description: Requests cancellation of a queued or running Live Run.
      security:
        - bearerAuth: []
      responses:
        "202":
          description: Cancellation state accepted.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TutorialExperimentRun"
        "401":
          $ref: "#/components/responses/Error"
        "403":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
        "409":
          $ref: "#/components/responses/Error"
        "500":
          $ref: "#/components/responses/Error"
  /v1/datasets:
    post:
      tags: [Evaluation]
      operationId: createDataset
      x-orag-maturity: experimental
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateDatasetRequest"
      responses:
        "201":
          description: Dataset.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Dataset"
        "400":
          $ref: "#/components/responses/Error"
        "401":
          $ref: "#/components/responses/Error"
  /v1/datasets/{id}/items:
    post:
      tags: [Evaluation]
      operationId: addDatasetItem
      x-orag-maturity: experimental
      description: Returns `404 dataset_not_found` when the dataset does not exist or belongs to another tenant.
      security:
        - bearerAuth: []
      parameters:
        - $ref: "#/components/parameters/ID"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateDatasetItemRequest"
      responses:
        "201":
          description: Dataset item.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DatasetItem"
        "400":
          $ref: "#/components/responses/Error"
        "401":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
  /v1/evaluations:
    post:
      tags: [Evaluation]
      operationId: runEvaluation
      x-orag-maturity: experimental
      description: Returns `404 dataset_not_found` when `dataset_id` does not exist or belongs to another tenant.
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/RunEvaluationRequest"
      responses:
        "202":
          description: Evaluation run result.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/RunEvaluationResponse"
        "400":
          $ref: "#/components/responses/Error"
        "401":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
        "500":
          $ref: "#/components/responses/Error"
  /v1/evaluations/{id}:
    get:
      tags: [Evaluation]
      operationId: getEvaluation
      x-orag-maturity: experimental
      security:
        - bearerAuth: []
      parameters:
        - $ref: "#/components/parameters/ID"
        - name: include_items
          in: query
          schema:
            type: boolean
          description: Include per-item evaluation results.
        - name: include_judge
          in: query
          schema:
            type: boolean
          description: Include LLM Judge, QAG and calibration records.
        - name: include_pairwise
          in: query
          schema:
            type: boolean
          description: Include pairwise judge records.
      responses:
        "200":
          description: Evaluation run summary or requested detail envelope.
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: "#/components/schemas/RunEvaluationResponse"
                  - $ref: "#/components/schemas/EvaluationDetail"
        "401":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
  /v1/optimizations:
    post:
      tags: [Evaluation]
      operationId: optimize
      x-orag-maturity: experimental
      description: Returns `404 dataset_not_found` when `dataset_id` does not exist or belongs to another tenant.
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/OptimizeRequest"
      responses:
        "202":
          description: Optimization accepted for asynchronous execution.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/OptimizationAcceptedResponse"
        "400":
          $ref: "#/components/responses/Error"
        "401":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
        "500":
          $ref: "#/components/responses/Error"
  /v1/optimizations/{id}:
    get:
      tags: [Evaluation]
      operationId: getOptimization
      x-orag-maturity: experimental
      security:
        - bearerAuth: []
      parameters:
        - $ref: "#/components/parameters/ID"
      responses:
        "200":
          description: Optimization run status and candidates.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/OptimizationStatusResponse"
        "401":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
        "500":
          $ref: "#/components/responses/Error"
  "/v1/optimizations/{id}:cancel":
    post:
      tags: [Evaluation]
      operationId: cancelOptimization
      x-orag-maturity: experimental
      security:
        - bearerAuth: []
      parameters:
        - $ref: "#/components/parameters/ID"
      requestBody:
        description: |
          Optional resume body. It is accepted only for compatible resume/control
          changes. Changes to candidate-defining optimization configuration are
          rejected with `400`.
        required: false
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/OptimizationCancelRequest"
      responses:
        "202":
          description: Cancel request accepted.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/OptimizationAcceptedResponse"
        "401":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
        "500":
          $ref: "#/components/responses/Error"
  "/v1/optimizations/{id}:resume":
    post:
      tags: [Evaluation]
      operationId: resumeOptimization
      x-orag-maturity: experimental
      security:
        - bearerAuth: []
      parameters:
        - $ref: "#/components/parameters/ID"
      requestBody:
        required: false
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/OptimizeRequest"
      responses:
        "202":
          description: Resume request accepted.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/OptimizationAcceptedResponse"
        "400":
          $ref: "#/components/responses/Error"
        "401":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
        "409":
          $ref: "#/components/responses/Error"
        "500":
          $ref: "#/components/responses/Error"
  /v1/offline-knowledge/runs:
    get:
      tags: [OfflineKnowledge]
      operationId: listOfflineKnowledgeRuns
      x-orag-maturity: experimental
      description: Lists offline knowledge mining runs for the current tenant.
      security:
        - bearerAuth: []
      parameters:
        - name: kb_id
          in: query
          schema:
            type: string
          description: Optional knowledge base filter. `knowledge_base_id` is accepted as a compatibility alias by the runtime.
        - name: status
          in: query
          schema:
            $ref: "#/components/schemas/OfflineKnowledgeRunStatus"
        - name: limit
          in: query
          schema:
            type: integer
            minimum: 1
      responses:
        "200":
          description: Offline knowledge run list.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/OfflineKnowledgeRunListResponse"
        "401":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
        "500":
          $ref: "#/components/responses/Error"
    post:
      tags: [OfflineKnowledge]
      operationId: createOfflineKnowledgeRun
      x-orag-maturity: experimental
      description: Creates a tenant-scoped offline knowledge mining run for a historical query window.
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateOfflineKnowledgeRunRequest"
      responses:
        "202":
          description: Offline knowledge run accepted.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/OfflineKnowledgeRunCreateResponse"
        "200":
          description: Existing matching run returned when the window and config hash already exist.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/OfflineKnowledgeRunCreateResponse"
        "400":
          $ref: "#/components/responses/Error"
        "401":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
        "503":
          $ref: "#/components/responses/Error"
        "500":
          $ref: "#/components/responses/Error"
  /v1/offline-knowledge/scheduler:trigger:
    post:
      tags: [OfflineKnowledge]
      operationId: triggerOfflineKnowledgeScheduler
      x-orag-maturity: experimental
      description: |
        Manually triggers the real offline knowledge scheduler for its configured
        tenant/kb targets. Returns `503` when the scheduler is disabled,
        unconfigured, or a required runtime dependency is unavailable.
      security:
        - bearerAuth: []
      requestBody:
        required: false
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/OfflineKnowledgeSchedulerTriggerRequest"
      responses:
        "202":
          description: Scheduler trigger accepted.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/OfflineKnowledgeSchedulerTriggerResponse"
        "400":
          $ref: "#/components/responses/Error"
        "401":
          $ref: "#/components/responses/Error"
        "503":
          $ref: "#/components/responses/Error"
  /v1/offline-knowledge/runs/{id}:
    get:
      tags: [OfflineKnowledge]
      operationId: getOfflineKnowledgeRun
      x-orag-maturity: experimental
      security:
        - bearerAuth: []
      parameters:
        - $ref: "#/components/parameters/ID"
      responses:
        "200":
          description: Offline knowledge run.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/OfflineKnowledgeRun"
        "401":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
        "503":
          $ref: "#/components/responses/Error"
        "500":
          $ref: "#/components/responses/Error"
  /v1/offline-knowledge/runs/{id}/execute:
    post:
      tags: [OfflineKnowledge]
      operationId: executeOfflineKnowledgeRun
      x-orag-maturity: experimental
      description: |
        Executes an existing `pending` or `failed` offline knowledge run with
        real history extraction, question clustering, recall replay, Codex
        analysis, evidence validation, and metrics updates. Returns `409` for
        non-executable run statuses and `503` for disabled/unavailable
        dependencies such as Codex, validator, source reader, or retriever.
      security:
        - bearerAuth: []
      parameters:
        - $ref: "#/components/parameters/ID"
      responses:
        "202":
          description: Run execution accepted and completed synchronously by the runtime.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/OfflineKnowledgeRunResult"
        "401":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
        "409":
          $ref: "#/components/responses/Error"
        "503":
          $ref: "#/components/responses/Error"
        "500":
          $ref: "#/components/responses/Error"
  /v1/offline-knowledge/runs/{id}/questions:
    get:
      tags: [OfflineKnowledge]
      operationId: listOfflineKnowledgeQuestions
      x-orag-maturity: experimental
      description: Lists question clusters produced by an offline knowledge run.
      security:
        - bearerAuth: []
      parameters:
        - $ref: "#/components/parameters/ID"
        - name: kb_id
          in: query
          schema:
            type: string
          description: Optional knowledge base filter. `knowledge_base_id` is accepted as a compatibility alias by the runtime.
        - name: limit
          in: query
          schema:
            type: integer
            minimum: 1
      responses:
        "200":
          description: Question cluster list.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/QuestionClusterListResponse"
        "401":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
        "500":
          $ref: "#/components/responses/Error"
  /v1/optimization-items:
    get:
      tags: [OfflineKnowledge]
      operationId: listOptimizationItems
      x-orag-maturity: experimental
      description: Lists offline knowledge optimization items for review, shadow enablement, publishing, or revalidation.
      security:
        - bearerAuth: []
      parameters:
        - name: kb_id
          in: query
          schema:
            type: string
          description: Optional knowledge base filter. `knowledge_base_id` is accepted as a compatibility alias by the runtime.
        - name: run_id
          in: query
          schema:
            type: string
        - name: status
          in: query
          schema:
            $ref: "#/components/schemas/OptimizationItemStatus"
        - name: item_type
          in: query
          schema:
            $ref: "#/components/schemas/OptimizationItemType"
        - name: limit
          in: query
          schema:
            type: integer
            minimum: 1
      responses:
        "200":
          description: Optimization item list.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/OptimizationItemListResponse"
        "401":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
        "500":
          $ref: "#/components/responses/Error"
  /v1/optimization-items/{id}:
    get:
      tags: [OfflineKnowledge]
      operationId: getOptimizationItem
      x-orag-maturity: experimental
      security:
        - bearerAuth: []
      parameters:
        - $ref: "#/components/parameters/ID"
      responses:
        "200":
          description: Optimization item.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/OptimizationItem"
        "401":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
        "500":
          $ref: "#/components/responses/Error"
  /v1/optimization-items/{id}/{action}:
    post:
      tags: [OfflineKnowledge]
      operationId: actOnOptimizationItem
      x-orag-maturity: experimental
      description: |
        Applies a workflow action to an optimization item. Supported actions are
        `verify`, `reject`, `enable-shadow`, `publish`, `disable`,
        `revalidate`, and `run-regression`. State-transition actions return the
        updated `OptimizationItem`; `revalidate` returns `RevalidateResult`.
        `run-regression` uses the configured real regression dataset id and
        returns `503` when regression is disabled or unavailable.
      security:
        - bearerAuth: []
      parameters:
        - $ref: "#/components/parameters/ID"
        - name: action
          in: path
          required: true
          schema:
            type: string
            enum: [verify, reject, enable-shadow, publish, disable, revalidate, run-regression]
      responses:
        "202":
          description: Action accepted.
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: "#/components/schemas/OptimizationItem"
                  - $ref: "#/components/schemas/RevalidateResult"
        "401":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
        "409":
          $ref: "#/components/responses/Error"
        "503":
          $ref: "#/components/responses/Error"
        "500":
          $ref: "#/components/responses/Error"
  /v1/optimization-items/revalidate:
    post:
      tags: [OfflineKnowledge]
      operationId: bulkRevalidateOptimizationItems
      x-orag-maturity: experimental
      description: Revalidates stale optimization items that match optional source filters.
      security:
        - bearerAuth: []
      requestBody:
        required: false
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/BulkRevalidateRequest"
      responses:
        "202":
          description: Bulk revalidation result.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/BulkRevalidateResult"
        "401":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
        "503":
          $ref: "#/components/responses/Error"
        "500":
          $ref: "#/components/responses/Error"
components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: ORAG user token or orag_sk_ API key
  responses:
    Error:
      description: |
        Unified error response. The `trace_id` is always present, is echoed from
        the incoming `X-Trace-ID` header when supplied, and is also returned in
        the `X-Trace-ID` response header for log and persisted trace correlation.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"
  parameters:
    ID:
      name: id
      in: path
      required: true
      schema:
        type: string
    TraceID:
      name: trace_id
      in: path
      required: true
      schema:
        type: string
  schemas:
    BuildInfo:
      type: object
      required: [version, commit, build_time]
      properties:
        version:
          type: string
          example: v0.1.0-beta.1
        commit:
          type: string
          example: 0123456789abcdef
        build_time:
          type: string
          example: 2026-07-14T00:00:00Z
    Profile:
      type: string
      enum: [realtime, high_precision]
    ErrorResponse:
      type: object
      required: [error]
      properties:
        error:
          $ref: "#/components/schemas/ErrorBody"
    ErrorBody:
      type: object
      required: [code, message, trace_id]
      properties:
        code:
          type: string
        message:
          type: string
        trace_id:
          type: string
          description: Request trace identifier used for logs and trace lookup.
        details:
          type: object
          additionalProperties: true
    LoginRequest:
      type: object
      required: [username, password]
      properties:
        username:
          type: string
        password:
          type: string
          format: password
    LoginResponse:
      type: object
      required: [access_token, token_type, expires_in]
      properties:
        access_token:
          type: string
        token_type:
          type: string
          example: Bearer
        expires_in:
          type: integer
    APIKeyRole:
      type: string
      enum: [tenant_admin, project_editor, project_viewer]
    CreateAPIKeyRequest:
      type: object
      required: [name, role]
      properties:
        name:
          type: string
        role:
          $ref: "#/components/schemas/APIKeyRole"
        project_id:
          type: string
          description: Required for project_editor and project_viewer. Optional project constraint for tenant_admin.
        expires_at:
          type: string
          format: date-time
          description: Optional future expiry instant.
    APIKey:
      type: object
      required: [id, tenant_id, name, prefix, role, created_by, created_at]
      properties:
        id:
          type: string
        tenant_id:
          type: string
        project_id:
          type: string
        name:
          type: string
        prefix:
          type: string
          description: Non-secret display prefix. It cannot authenticate a request.
        role:
          $ref: "#/components/schemas/APIKeyRole"
        created_by:
          type: string
        created_at:
          type: string
          format: date-time
        expires_at:
          type: string
          format: date-time
        revoked_at:
          type: string
          format: date-time
        last_used_at:
          type: string
          format: date-time
        rotated_from_key_id:
          type: string
          description: Source key ID when this key was created by rotation.
    CreateAPIKeyResponse:
      type: object
      required: [api_key, secret]
      properties:
        api_key:
          $ref: "#/components/schemas/APIKey"
        secret:
          type: string
          format: password
          description: One-time API key secret. It is never available from list or get operations.
    APIKeyListResponse:
      type: object
      required: [api_keys]
      properties:
        api_keys:
          type: array
          items:
            $ref: "#/components/schemas/APIKey"
    CreateProjectRequest:
      type: object
      required: [name]
      properties:
        name:
          type: string
        description:
          type: string
    UpdateProjectRequest:
      type: object
      required: [name]
      properties:
        name:
          type: string
        description:
          type: string
    Project:
      type: object
      required: [id, tenant_id, name, description, created_at, updated_at]
      properties:
        id:
          type: string
        tenant_id:
          type: string
        name:
          type: string
        description:
          type: string
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
    ProjectListResponse:
      type: object
      required: [projects]
      properties:
        projects:
          type: array
          items:
            $ref: "#/components/schemas/Project"
    Environment:
      type: object
      required: [id, project_id, kind, revision, bound]
      properties:
        id: {type: string}
        project_id: {type: string}
        kind: {type: string, enum: [development, staging, production]}
        active_version_id: {type: string}
        active_release_id: {type: string}
        revision: {type: integer, format: int64}
        bound: {type: boolean}
    BindEnvironmentRequest:
      type: object
      required: [binding_ref]
      properties:
        binding_ref:
          type: string
          minLength: 1
          writeOnly: true
          description: Opaque server-side reference to the environment's resources or credentials. This value is never returned.
    EnvironmentListResponse:
      type: object
      required: [items]
      properties:
        items:
          type: array
          items: {$ref: "#/components/schemas/Environment"}
    PipelineVersion:
      type: object
      required: [id, project_id, content_hash, created_at]
      properties:
        id: {type: string}
        project_id: {type: string}
        pipeline_id:
          type: string
          description: Source pipeline when the version was frozen from a draft. Omitted for legacy manually recorded versions.
        content_hash: {type: string}
        created_at: {type: string, format: date-time}
    PipelineNodeDefinition:
      type: object
      required: [type, schema_version, config_schema]
      properties:
        type: {type: string}
        display_name: {type: string}
        category: {type: string}
        description: {type: string}
        schema_version: {type: integer}
        config_schema: {type: object}
        default_config: {type: object}
        singleton: {type: boolean}
        entry: {type: boolean}
        produces_answer: {type: boolean}
        allows_cycles: {type: boolean}
        allowed_targets:
          type: array
          items: {type: string}
    PipelineNodeDefinitionListResponse:
      type: object
      required: [items]
      properties:
        items:
          type: array
          items: {$ref: "#/components/schemas/PipelineNodeDefinition"}
    Pipeline:
      type: object
      required: [id, project_id, name, created_at, updated_at]
      properties:
        id: {type: string}
        project_id: {type: string}
        name: {type: string}
        created_at: {type: string, format: date-time}
        updated_at: {type: string, format: date-time}
    PipelineListResponse:
      type: object
      required: [items]
      properties:
        items:
          type: array
          items: {$ref: "#/components/schemas/Pipeline"}
    PipelineDraft:
      type: object
      required: [pipeline_id, project_id, revision, schema_version, definition, updated_at]
      properties:
        pipeline_id: {type: string}
        project_id: {type: string}
        revision: {type: integer, format: int64}
        schema_version: {type: integer}
        definition: {type: object}
        updated_at: {type: string, format: date-time}
    CreatePipelineRequest:
      type: object
      required: [name]
      properties:
        name: {type: string}
    SavePipelineDraftRequest:
      type: object
      required: [expected_revision, definition]
      properties:
        expected_revision: {type: integer, format: int64}
        definition: {type: object}
    PipelineDebugRequest:
      type: object
      required: [pipeline_id, expected_revision, query]
      properties:
        pipeline_id: {type: string}
        expected_revision: {type: integer, format: int64, minimum: 0}
        environment: {type: string, enum: [development, staging, production], default: development}
        query:
          $ref: "#/components/schemas/QueryRequest"
    DiagnosticEvent:
      type: object
      required: [sequence, node_id, node_type, latency_ms]
      properties:
        sequence: {type: integer}
        node_id: {type: string}
        node_type: {type: string}
        latency_ms: {type: integer, format: int64}
        error: {type: string}
    PipelineDebugResponse:
      type: object
      required: [revision, trace_id, response, events]
      properties:
        revision: {type: integer, format: int64}
        trace_id: {type: string}
        response:
          $ref: "#/components/schemas/QueryResponse"
        events:
          type: array
          items: {$ref: "#/components/schemas/DiagnosticEvent"}
    SaveDebugCaseRequest:
      type: object
      required: [dataset_id, query, ground_truth]
      properties:
        dataset_id: {type: string}
        query: {type: string}
        ground_truth: {type: string}
        expected_evidence:
          type: array
          items: {type: string}
    SavedDebugCase:
      type: object
      required: [run_id, item]
      properties:
        run_id: {type: string}
        item:
          $ref: "#/components/schemas/DatasetItem"
    PipelineVersionListResponse:
      type: object
      required: [items]
      properties:
        items:
          type: array
          items: {$ref: "#/components/schemas/PipelineVersion"}
    CreatePipelineVersionRequest:
      type: object
      required: [content_hash]
      properties:
        id: {type: string}
        content_hash: {type: string}
    CreatePipelineVersionFromDraftRequest:
      type: object
      required: [expected_revision]
      properties:
        expected_revision: {type: integer, format: int64, minimum: 0}
    PipelineVersionFromDraftResponse:
      type: object
      required: [version, draft_revision]
      properties:
        version:
          $ref: "#/components/schemas/PipelineVersion"
        draft_revision: {type: integer, format: int64}
    ValidatePipelineVersionRequest:
      type: object
      required: [environment, content_hash]
      properties:
        environment: {type: string, enum: [development, staging, production]}
        passed: {type: boolean}
        content_hash: {type: string}
    PipelineValidation:
      type: object
      required: [version_id, environment, passed, content_hash]
      properties:
        version_id: {type: string}
        environment: {type: string, enum: [development, staging, production]}
        passed: {type: boolean}
        content_hash: {type: string}
    EvaluationGate:
      type: object
      required: [metric, comparator, threshold]
      properties:
        metric: {type: string}
        comparator: {type: string, enum: [gte, lte]}
        threshold: {type: number, format: double}
    EvaluationPolicy:
      type: object
      required: [id, tenant_id, project_id, dataset_id, name, version, gates, created_at]
      properties:
        id: {type: string}
        tenant_id: {type: string}
        project_id: {type: string}
        dataset_id: {type: string}
        name: {type: string}
        version: {type: integer, minimum: 1}
        gates:
          type: array
          minItems: 1
          items: {$ref: "#/components/schemas/EvaluationGate"}
        created_at: {type: string, format: date-time}
    CreateEvaluationPolicyRequest:
      type: object
      required: [dataset_id, name, gates]
      properties:
        dataset_id: {type: string}
        name: {type: string}
        gates:
          type: array
          minItems: 1
          items: {$ref: "#/components/schemas/EvaluationGate"}
    EvaluationPolicyListResponse:
      type: object
      required: [items]
      properties:
        items:
          type: array
          items: {$ref: "#/components/schemas/EvaluationPolicy"}
    RecordEvaluationEvidenceRequest:
      type: object
      required: [policy_id, evaluation_run_id, environment]
      properties:
        policy_id: {type: string}
        evaluation_run_id: {type: string}
        environment: {type: string, enum: [development, staging, production]}
    EvaluationGateResult:
      type: object
      required: [metric, comparator, threshold, present, passed]
      properties:
        metric: {type: string}
        comparator: {type: string, enum: [gte, lte]}
        threshold: {type: number, format: double}
        actual: {type: number, format: double}
        present: {type: boolean}
        passed: {type: boolean}
    EvaluationFrozenInput:
      type: object
      required: [policy_id, policy_version, project_id, dataset_id, evaluation_run_id, pipeline_version_id, content_hash, gates, metrics]
      properties:
        policy_id: {type: string}
        policy_version: {type: integer, minimum: 1}
        project_id: {type: string}
        dataset_id: {type: string}
        evaluation_run_id: {type: string}
        pipeline_version_id: {type: string}
        content_hash: {type: string}
        environment: {type: string, enum: [development, staging, production]}
        gates:
          type: array
          items: {$ref: "#/components/schemas/EvaluationGate"}
        metrics:
          type: object
          additionalProperties: {type: number, format: double}
    EvaluationEvidence:
      type: object
      required: [id, tenant_id, project_id, policy_id, policy_version, evaluation_run_id, pipeline_version_id, content_hash, frozen_input, gate_results, passed, created_at]
      properties:
        id: {type: string}
        tenant_id: {type: string}
        project_id: {type: string}
        policy_id: {type: string}
        policy_version: {type: integer, minimum: 1}
        evaluation_run_id: {type: string}
        pipeline_version_id: {type: string}
        content_hash: {type: string}
        environment: {type: string, enum: [development, staging, production]}
        frozen_input: {$ref: "#/components/schemas/EvaluationFrozenInput"}
        gate_results:
          type: array
          items: {$ref: "#/components/schemas/EvaluationGateResult"}
        passed: {type: boolean}
        created_at: {type: string, format: date-time}
    Release:
      type: object
      required: [id, project_id, target_version_id, source_environment, target_environment, action, actor, created_at]
      properties:
        id: {type: string}
        project_id: {type: string}
        source_version_id: {type: string}
        target_version_id: {type: string}
        source_environment: {type: string, enum: [development, staging, production]}
        target_environment: {type: string, enum: [development, staging, production]}
        action: {type: string, enum: [activate, promote, rollback]}
        actor: {type: string}
        reason: {type: string}
        created_at: {type: string, format: date-time}
    ReleaseListResponse:
      type: object
      required: [items]
      properties:
        items:
          type: array
          items: {$ref: "#/components/schemas/Release"}
    PromoteReleaseRequest:
      type: object
      required: [source_environment, target_environment, target_version_id]
      properties:
        source_environment: {type: string, enum: [development, staging]}
        target_environment: {type: string, enum: [staging, production]}
        target_version_id: {type: string}
        expected_active_version_id: {type: string}
    ActivateDevelopmentRequest:
      type: object
      required: [target_version_id]
      properties:
        target_version_id: {type: string}
        expected_active_version_id:
          type: string
          description: The development active version observed by the caller. Omit or use an empty string for the first activation.
    RollbackReleaseRequest:
      type: object
      required: [target_version_id, reason]
      properties:
        target_version_id: {type: string}
        expected_active_version_id: {type: string}
        reason: {type: string}
    TutorialModality:
      type: string
      enum: [text, visual_document, video]
    TutorialPackRef:
      type: object
      required: [tier, manifest_url, estimated_bytes, estimated_minutes, requires_license_check]
      properties:
        tier:
          type: string
          enum: [quick, benchmark]
        manifest_url:
          type: string
          format: uri
          description: Anonymous-read HTTPS URL for the public package manifest.
        estimated_bytes:
          type: integer
          format: int64
          minimum: 0
        estimated_minutes:
          type: integer
          minimum: 0
        requires_license_check:
          type: boolean
    TutorialTemplate:
      type: object
      required: [id, slug, title, summary, version, status, modality, difficulty, estimated_duration_minutes, source_benchmark, source_url, scenario_dimensions, pipeline_stages, required_capabilities, packs, replay_available]
      properties:
        id:
          type: string
          example: text-rag
        slug:
          type: string
          example: text-rag
        title:
          type: string
          example: 中文文本 RAG
        summary:
          type: string
        version:
          type: string
          example: 1.0.0
        status:
          type: string
          enum: [published]
        modality:
          $ref: "#/components/schemas/TutorialModality"
        difficulty:
          type: string
          enum: [intermediate, advanced]
        estimated_duration_minutes:
          type: integer
          minimum: 1
        source_benchmark:
          type: string
          enum: [CRUD-RAG, ViDoSeek, Video-MME]
        source_url:
          type: string
          format: uri
        scenario_dimensions:
          type: array
          items:
            type: string
        pipeline_stages:
          type: array
          items:
            type: string
        required_capabilities:
          type: array
          items:
            type: string
        packs:
          type: array
          minItems: 2
          maxItems: 2
          items:
            $ref: "#/components/schemas/TutorialPackRef"
        replay_available:
          type: boolean
      example:
        id: video-rag
        slug: video-rag
        title: 视频 RAG
        summary: 对比视频理解、时间片段索引、多路召回和重排效果。
        version: 1.0.0
        status: published
        modality: video
        difficulty: advanced
        estimated_duration_minutes: 75
        source_benchmark: Video-MME
        source_url: https://video-mme.github.io/home_page.html
        scenario_dimensions: [短视频, 长视频, 时间否定, 信息不足]
        pipeline_stages: [P0 基线, P1 视频理解与打标, P8 组合策略]
        required_capabilities: [doubao_seed_video_understanding, temporal_index, embedding, rerank]
        packs:
          - tier: quick
            manifest_url: https://orag.oss-cn-guangzhou.aliyuncs.com/tutorial-packs/video-rag/1.0.0/quick/manifest.json
            estimated_bytes: 2147483648
            estimated_minutes: 30
            requires_license_check: true
          - tier: benchmark
            manifest_url: https://orag.oss-cn-guangzhou.aliyuncs.com/tutorial-packs/video-rag/1.0.0/benchmark/manifest.json
            estimated_bytes: 21474836480
            estimated_minutes: 180
            requires_license_check: true
        replay_available: false
    TutorialListResponse:
      type: object
      required: [tutorials]
      properties:
        tutorials:
          type: array
          items:
            $ref: "#/components/schemas/TutorialTemplate"
    TutorialReplayMetric:
      type: object
      required: [name, value]
      properties:
        name:
          type: string
        value:
          type: number
          minimum: 0
    TutorialReplayVariant:
      type: object
      required: [variant, profile, top_k, context_pack_top_n, context_pack_max_tokens, metrics, index_metrics]
      properties:
        variant:
          type: string
          enum: [baseline, p8_context_pack]
        profile:
          type: string
          enum: [high_precision]
        top_k:
          type: integer
          minimum: 1
        context_pack_top_n:
          type: integer
          minimum: 1
        context_pack_max_tokens:
          type: integer
          minimum: 1
        metrics:
          type: array
          minItems: 1
          items:
            $ref: "#/components/schemas/TutorialReplayMetric"
        index_metrics:
          type: array
          minItems: 1
          items:
            $ref: "#/components/schemas/TutorialReplayMetric"
    TutorialReplaySnapshot:
      type: object
      required: [id, template_id, template_version, pack_tier, pack_manifest_sha256, runtime_environment_sha256, build_revision, evaluator_version, generated_at, summary, baseline, candidate, fingerprint]
      properties:
        id:
          type: string
          example: text-rag/1.0.0/benchmark/replay-v1
        template_id:
          type: string
          enum: [text-rag]
        template_version:
          type: string
          pattern: '^[0-9]+\.[0-9]+\.[0-9]+$'
        pack_tier:
          type: string
          enum: [benchmark]
        pack_manifest_sha256:
          type: string
          pattern: '^[0-9a-f]{64}$'
        runtime_environment_sha256:
          type: string
          pattern: '^[0-9a-f]{64}$'
        build_revision:
          type: string
        evaluator_version:
          type: string
        generated_at:
          type: string
          format: date-time
        summary:
          type: string
        baseline:
          $ref: "#/components/schemas/TutorialReplayVariant"
        candidate:
          $ref: "#/components/schemas/TutorialReplayVariant"
        fingerprint:
          type: string
          pattern: '^[0-9a-f]{64}$'
    TutorialCloneProjectInput:
      type: object
      required: [name]
      properties:
        name:
          type: string
          minLength: 1
          maxLength: 120
        description:
          type: string
          maxLength: 2000
    StartTutorialCloneRequest:
      type: object
      required: [version, pack_tier, project, idempotency_key, license_accepted]
      properties:
        version:
          type: string
          pattern: '^[0-9]+\.[0-9]+\.[0-9]+$'
        pack_tier:
          type: string
          enum: [quick, benchmark]
        project:
          $ref: "#/components/schemas/TutorialCloneProjectInput"
        idempotency_key:
          type: string
          minLength: 1
          maxLength: 200
          description: Reuse the same value to safely resume the same request.
        license_accepted:
          type: boolean
          description: Must be true when the selected pack requires license acceptance.
    TutorialCloneStage:
      type: string
      enum: [create_project, validate_manifest, download_pack, verify_pack, write_private_store, create_runtime_resources, pack_installed]
    TutorialCloneStatus:
      type: string
      enum: [queued, running, failed, completed]
    TutorialCloneStageEvent:
      type: object
      required: [stage, outcome, occurred_at]
      properties:
        stage:
          $ref: "#/components/schemas/TutorialCloneStage"
        outcome:
          type: string
        detail_code:
          type: string
          description: Stable redacted code only; never a provider response or object location.
        occurred_at:
          type: string
          format: date-time
    TutorialCloneJob:
      type: object
      required: [id, tenant_id, project_id, project_name, project_description, template_id, template_version, pack_tier, stage, status, attempt, events, created_at, updated_at]
      properties:
        id:
          type: string
        tenant_id:
          type: string
        project_id:
          type: string
        project_name:
          type: string
        project_description:
          type: string
        template_id:
          type: string
        template_version:
          type: string
        pack_tier:
          type: string
          enum: [quick, benchmark]
        stage:
          $ref: "#/components/schemas/TutorialCloneStage"
        status:
          $ref: "#/components/schemas/TutorialCloneStatus"
        attempt:
          type: integer
          minimum: 1
        failure_code:
          type: string
        events:
          type: array
          items:
            $ref: "#/components/schemas/TutorialCloneStageEvent"
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
    TutorialCloneAcceptedResponse:
      type: object
      required: [job_id, project_id, poll_url, job]
      properties:
        job_id:
          type: string
        project_id:
          type: string
        poll_url:
          type: string
          description: Relative ORAG API path to poll the durable clone job.
        job:
          $ref: "#/components/schemas/TutorialCloneJob"
    TutorialExperiment:
      type: object
      required: [id, tenant_id, project_id, template_id, template_version, pack_tier, pack_status, runtime_status, variants, created_at, updated_at]
      properties:
        id:
          type: string
        tenant_id:
          type: string
        project_id:
          type: string
        template_id:
          type: string
        template_version:
          type: string
        pack_tier:
          type: string
          enum: [quick, benchmark]
        pack_status:
          type: string
          enum: [pending, installing, pack_installed, failed]
        runtime_status:
          type: string
          enum: [pending, runtime_unavailable, temporal_index_pending_evaluation, ready]
        knowledge_base_id:
          type: string
          description: Server-derived project knowledge-base identifier. Never an object storage location.
        dataset_id:
          type: string
          description: Server-derived project evaluation dataset identifier.
        baseline_profile:
          type: string
          enum: [realtime, high_precision, visual_page, temporal_page]
        baseline_top_k:
          type: integer
          minimum: 1
        variants:
          type: array
          description: Immutable tutorial variants declared by the installed Pack. Variant configuration remains server-owned.
          items:
            $ref: "#/components/schemas/TutorialExperimentVariant"
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
    TutorialExperimentVariant:
      type: object
      required: [id, parser_method, available]
      properties:
        id:
          type: string
          description: Server-declared immutable variant identifier.
        chapter:
          type: string
        parser_method:
          type: string
          description: Parser method selected by the server for this declared variant.
        chunk_size_tokens:
          type: integer
          readOnly: true
          description: Server-owned recursive chunk size in deterministic text units.
        chunk_overlap_tokens:
          type: integer
          readOnly: true
          description: Server-owned recursive chunk overlap in deterministic text units.
        contextual_retrieval:
          type: boolean
          readOnly: true
          description: Whether this immutable Pack-declared variant uses server-owned contextual retrieval.
        retrieval_strategy:
          type: string
          readOnly: true
          enum: [hybrid, sparse, graph]
          description: Immutable server-owned retriever strategy for this Pack-declared variant.
        reuse_baseline_index:
          type: boolean
          readOnly: true
          description: Whether this variant evaluates the compatible P0 index without a second indexing pass.
        multi_query_count:
          type: integer
          readOnly: true
          minimum: 0
          description: Fixed number of retrieval queries generated by this immutable variant.
        rerank_enabled:
          type: boolean
          readOnly: true
          description: Whether this immutable variant enables the server-owned reranker.
        graph_retrieval_enabled:
          type: boolean
          readOnly: true
          description: Whether this immutable variant builds graph relations and uses graph expansion during evaluation.
        context_pack_top_n:
          type: integer
          readOnly: true
          minimum: 0
          description: Fixed maximum number of retrieved evidence chunks packed into the generation context.
        context_pack_max_tokens:
          type: integer
          readOnly: true
          minimum: 0
          description: Fixed maximum context-pack token budget in deterministic text units.
        available:
          type: boolean
          description: Whether this installed Pack currently has a runnable runtime root.
    ImportTutorialVideoSourceRequest:
      type: object
      required: [license_confirmed, file, alias, sha256, content_type, duration_ms]
      properties:
        license_confirmed:
          type: string
          enum: ["true"]
          description: Must be the literal string true; confirms the caller is authorized to privately import this source.
        file:
          type: string
          format: binary
          description: Private video bytes. They are SHA-256 verified before storage.
        alias:
          type: string
          minLength: 1
          maxLength: 200
          description: Caller-owned label used only in private temporal evidence identifiers.
        sha256:
          type: string
          pattern: '^[0-9a-f]{64}$'
          description: Lowercase SHA-256 for the uploaded bytes.
        content_type:
          type: string
          enum: [video/mp4, video/webm, video/quicktime]
          description: Declared video MIME type; the server validates it against the fixed import allowlist.
        duration_ms:
          type: integer
          format: int64
          minimum: 1
          maximum: 86400000
          description: Authorized source duration in milliseconds, used only with the protocol's fixed cadence.
    ImportTutorialVideoSourceResponse:
      type: object
      required: [source_alias, temporal_segment_count]
      properties:
        source_alias:
          type: string
        temporal_segment_count:
          type: integer
          minimum: 1
    ActivateTutorialVideoEvaluationRequest:
      type: object
      required: [dataset_id, license_confirmed]
      properties:
        dataset_id:
          type: string
          description: Existing project-owned dataset containing owner-authorized evaluation items.
        license_confirmed:
          type: boolean
          enum: [true]
          description: Confirms the caller is authorized to use this dataset for private evaluation.
    ActivateTutorialVideoEvaluationResponse:
      type: object
      required: [runtime_status, knowledge_base_id, dataset_id]
      properties:
        runtime_status:
          type: string
          enum: [ready]
        knowledge_base_id:
          type: string
          description: Server-derived temporal evidence knowledge-base root.
        dataset_id:
          type: string
          description: Server-derived immutable evaluation snapshot, not the mutable source dataset.
    StartTutorialExperimentRunRequest:
      type: object
      required: [idempotency_key]
      properties:
        variant:
          type: string
          default: baseline
          description: >-
            Immutable variant identifier from TutorialExperiment.variants. The server
            rejects undeclared values and never accepts parser or model configuration.
        idempotency_key:
          type: string
          minLength: 1
          maxLength: 200
          description: Reuse the same value to safely resume the same immutable variant request.
    TutorialExperimentRunStage:
      type: string
      enum: [index_private_pack, run_evaluation, completed]
    TutorialExperimentRunStatus:
      type: string
      enum: [queued, running, cancel_requested, cancelled, failed, completed]
    TutorialExperimentRunEvent:
      type: object
      required: [stage, outcome, occurred_at]
      properties:
        stage:
          $ref: "#/components/schemas/TutorialExperimentRunStage"
        outcome:
          type: string
        detail_code:
          type: string
          description: Stable redacted code only; never an object location or provider response.
        occurred_at:
          type: string
          format: date-time
    TutorialExperimentRun:
      type: object
      required: [id, tenant_id, project_id, experiment_id, variant, stage, status, events, created_at, updated_at]
      properties:
        id:
          type: string
        tenant_id:
          type: string
        project_id:
          type: string
        experiment_id:
          type: string
        variant:
          type: string
          description: Immutable Pack-declared variant identifier.
        baseline_run_id:
          type: string
          readOnly: true
          description: Server-selected compatible P0 parent for a candidate run.
        comparison_fingerprint:
          type: string
          readOnly: true
          description: SHA-256 of redacted inputs that must match between P0 and P1.
        definition_fingerprint:
          type: string
          readOnly: true
          description: SHA-256 of the variant's server-derived execution definition.
        pack_manifest_sha256:
          type: string
          readOnly: true
          description: SHA-256 of the verified immutable Pack Manifest used by this run.
        runtime_environment_sha256:
          type: string
          readOnly: true
          description: SHA-256 of the server-owned model and evaluator environment snapshot.
        build_revision:
          type: string
          readOnly: true
          description: Non-secret immutable server build revision recorded for reproducibility.
        knowledge_base_id:
          type: string
          readOnly: true
        dataset_id:
          type: string
          readOnly: true
        profile:
          type: string
          readOnly: true
        top_k:
          type: integer
          readOnly: true
        parser_method:
          type: string
          readOnly: true
        chunk_size_tokens:
          type: integer
          readOnly: true
        chunk_overlap_tokens:
          type: integer
          readOnly: true
        contextual_retrieval_enabled:
          type: boolean
          readOnly: true
          description: Whether the stored server-derived execution definition enabled contextual retrieval.
        retrieval_strategy:
          type: string
          readOnly: true
          enum: [hybrid, sparse, graph]
          description: Stored server-derived retriever strategy.
        reused_baseline_index:
          type: boolean
          readOnly: true
          description: Whether this run inherited measured index facts from its compatible P0 parent.
        query_expansion_mode:
          type: string
          readOnly: true
          enum: [none, multi_query]
          description: Stored server-derived query-expansion mode for this run.
        multi_query_count:
          type: integer
          readOnly: true
          minimum: 0
          description: Stored fixed retrieval-query count; zero means no multi-query expansion.
        rerank_enabled:
          type: boolean
          readOnly: true
          description: Stored server-derived reranker state for this run.
        graph_retrieval_enabled:
          type: boolean
          readOnly: true
          description: Stored server-derived graph retrieval state for this run.
        context_pack_top_n:
          type: integer
          readOnly: true
          minimum: 0
          description: Stored server-derived maximum evidence chunks packed into generation context.
        context_pack_max_tokens:
          type: integer
          readOnly: true
          minimum: 0
          description: Stored server-derived context-pack token budget in deterministic text units.
        indexed_chunk_count:
          type: integer
          readOnly: true
          description: Measured chunks stored during this run's private-pack indexing stage.
        average_chunk_tokens:
          type: number
          format: double
          readOnly: true
          description: Measured average deterministic text units per indexed chunk.
        contextualized_chunk_count:
          type: integer
          readOnly: true
          description: Measured indexed chunks with non-empty server-generated contextual text.
        average_context_tokens:
          type: number
          format: double
          readOnly: true
          description: Measured average deterministic text units in non-empty contextual text.
        stage:
          $ref: "#/components/schemas/TutorialExperimentRunStage"
        status:
          $ref: "#/components/schemas/TutorialExperimentRunStatus"
        evaluation_run_id:
          type: string
        failure_code:
          type: string
        events:
          type: array
          items:
            $ref: "#/components/schemas/TutorialExperimentRunEvent"
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
    TutorialExperimentRunAcceptedResponse:
      type: object
      required: [run_id, poll_url, run]
      properties:
        run_id:
          type: string
        poll_url:
          type: string
          description: Relative ORAG API path to poll the durable experiment run.
        run:
          $ref: "#/components/schemas/TutorialExperimentRun"
    TutorialExperimentMetricDelta:
      type: object
      required: [name, baseline, candidate, absolute_delta]
      properties:
        name:
          type: string
        baseline:
          type: number
          format: double
        candidate:
          type: number
          format: double
        absolute_delta:
          type: number
          format: double
        relative_delta:
          type: number
          format: double
          nullable: true
    TutorialExperimentRunComparison:
      type: object
      required: [baseline, candidate, comparable]
      properties:
        baseline:
          $ref: "#/components/schemas/TutorialExperimentRun"
        candidate:
          $ref: "#/components/schemas/TutorialExperimentRun"
        comparable:
          type: boolean
        metrics:
          type: array
          items:
            $ref: "#/components/schemas/TutorialExperimentMetricDelta"
        index_metrics:
          type: array
          description: Direct measurements from the two persisted private-pack indexes; these are not quality estimates.
          items:
            $ref: "#/components/schemas/TutorialExperimentMetricDelta"
    ReadinessResponse:
      type: object
      required: [status, checks]
      properties:
        status:
          type: string
          enum: [ready, not_ready]
        checks:
          type: object
          additionalProperties:
            $ref: "#/components/schemas/ReadinessCheck"
    ReadinessCheck:
      type: object
      required: [status]
      properties:
        status:
          type: string
        error:
          type: string
    CreateKnowledgeBaseRequest:
      type: object
      required: [name]
      properties:
        project_id:
          type: string
          description: Owning project. Omission is a deprecated beta compatibility path.
        name:
          type: string
        description:
          type: string
        metadata:
          type: object
          additionalProperties:
            type: string
    KnowledgeBase:
      type: object
      required: [id, tenant_id, name, description, created_at, updated_at]
      properties:
        id:
          type: string
        tenant_id:
          type: string
        project_id:
          type: string
        name:
          type: string
        description:
          type: string
        metadata:
          type: object
          additionalProperties:
            type: string
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
    KnowledgeBaseListResponse:
      type: object
      required: [items]
      properties:
        items:
          type: array
          items:
            $ref: "#/components/schemas/KnowledgeBase"
    ImportDocumentRequest:
      type: object
      required: [content]
      properties:
        source_uri:
          type: string
        name:
          type: string
        content:
          type: string
    CreateUploadRequest:
      type: object
      required: [name]
      properties:
        name:
          type: string
        source_uri:
          type: string
        total_bytes:
          type: integer
          format: int64
          minimum: 0
          description: Optional declared full object size. Completion requires received_bytes to match when provided.
    UploadSession:
      type: object
      required: [id, tenant_id, knowledge_base_id, name, source_uri, total_bytes, received_bytes, status, created_at, updated_at, upload_url, complete_url]
      properties:
        id:
          type: string
        tenant_id:
          type: string
        knowledge_base_id:
          type: string
        name:
          type: string
        source_uri:
          type: string
        total_bytes:
          type: integer
          format: int64
        received_bytes:
          type: integer
          format: int64
        status:
          type: string
          enum: [uploading, completed, canceled]
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
        upload_url:
          type: string
          example: /v1/uploads/upl_xxx
        complete_url:
          type: string
          example: /v1/uploads/upl_xxx:complete
    CompleteUploadResponse:
      type: object
      required: [upload, document, chunks, job]
      properties:
        upload:
          $ref: "#/components/schemas/UploadSession"
        document:
          $ref: "#/components/schemas/Document"
        chunks:
          type: integer
        job:
          $ref: "#/components/schemas/IngestionJob"
    IngestionResponse:
      type: object
      required: [document, chunks, job]
      properties:
        document:
          $ref: "#/components/schemas/Document"
        chunks:
          type: integer
        job:
          $ref: "#/components/schemas/IngestionJob"
    IngestionJob:
      type: object
      required: [id, tenant_id, knowledge_base_id, status, source_uri, chunk_count, created_at, updated_at]
      properties:
        id:
          type: string
        tenant_id:
          type: string
        knowledge_base_id:
          type: string
        status:
          type: string
          enum: [running, succeeded, failed]
        source_uri:
          type: string
        document_id:
          type: string
        chunk_count:
          type: integer
        error:
          type: string
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
    Document:
      type: object
      required: [id, tenant_id, knowledge_base_id, source_uri, title, content_hash, created_at]
      properties:
        id:
          type: string
        tenant_id:
          type: string
        knowledge_base_id:
          type: string
        source_uri:
          type: string
        title:
          type: string
        content_hash:
          type: string
        metadata:
          type: object
          additionalProperties:
            type: string
        created_at:
          type: string
          format: date-time
    Chunk:
      type: object
      required: [id, tenant_id, knowledge_base_id, document_id, content, source_uri]
      properties:
        id:
          type: string
        tenant_id:
          type: string
        knowledge_base_id:
          type: string
        document_id:
          type: string
        content:
          type: string
        source_uri:
          type: string
        page:
          type: integer
        section:
          type: string
        offset:
          type: integer
        metadata:
          type: object
          additionalProperties:
            type: string
    SearchResult:
      type: object
      required: [chunk, score, rank, from]
      properties:
        chunk:
          $ref: "#/components/schemas/Chunk"
        score:
          type: number
          format: double
        rank:
          type: integer
        from:
          type: string
    Citation:
      type: object
      required: [chunk_id, document_id, source_uri]
      properties:
        chunk_id:
          type: string
        document_id:
          type: string
        source_uri:
          type: string
        section:
          type: string
        quote:
          type: string
    QueryRequest:
      type: object
      required: [knowledge_base_id, query]
      properties:
        knowledge_base_id:
          type: string
        query:
          type: string
        profile:
          $ref: "#/components/schemas/Profile"
        session_id:
          type: string
        top_k:
          type: integer
          minimum: 1
          maximum: 100
          description: Number of final fused/reranked retrieved_chunks to return. When omitted, the service top-k is used, defaulting from RAG_DENSE_TOP_K; RAG_CONTEXT_TOP_N only limits prompt context and citations.
    QueryResponse:
      type: object
      required: [answer, citations, retrieved_chunks, trace_id, cache_status, profile, latency_ms, created_at]
      properties:
        answer:
          type: string
        citations:
          type: array
          items:
            $ref: "#/components/schemas/Citation"
        retrieved_chunks:
          type: array
          description: Final fused/reranked retrieval results sized by top_k. Prompt context and citations may include fewer chunks because they are independently limited by RAG_CONTEXT_TOP_N and token budget.
          items:
            $ref: "#/components/schemas/SearchResult"
        trace_id:
          type: string
          description: Request trace identifier. Query traces can be inspected with `oragctl trace --trace-id <trace_id>`.
        cache_status:
          type: string
          description: Semantic cache state. `bypass` is returned when the query router sends a request through the direct route without retrieval or cache lookup.
          enum: [hit, miss, error, bypass]
        profile:
          $ref: "#/components/schemas/Profile"
        warnings:
          type: array
          items:
            type: string
        latency_ms:
          type: integer
          format: int64
        created_at:
          type: string
          format: date-time
    TraceListResponse:
      type: object
      required: [items]
      properties:
        items:
          type: array
          items:
            $ref: "#/components/schemas/TraceRecord"
    TraceStatsResponse:
      type: object
      required: [tenant_id, items]
      properties:
        tenant_id:
          type: string
        items:
          type: array
          items:
            $ref: "#/components/schemas/TraceNodeStat"
    TraceNodeStat:
      type: object
      required: [node_name, count, avg_latency_ms, p95_latency_ms, p99_latency_ms, error_count]
      properties:
        node_name:
          type: string
        count:
          type: integer
          format: int64
        avg_latency_ms:
          type: number
          format: double
        p95_latency_ms:
          type: number
          format: double
        p99_latency_ms:
          type: number
          format: double
        error_count:
          type: integer
          format: int64
    TraceRecord:
      type: object
      required: [trace_id, tenant_id, profile, latency_ms, created_at, has_error, error_count, node_spans]
      properties:
        trace_id:
          type: string
        tenant_id:
          type: string
        kb_id:
          type: string
        project_id:
          type: string
        pipeline_id:
          type: string
        pipeline_version_id:
          type: string
        release_id:
          type: string
        environment:
          type: string
          enum: [development, staging, production]
        dataset_id:
          type: string
        evaluation_run_id:
          type: string
        retrieval_params:
          type: object
          properties:
            top_k:
              type: integer
            requested_profile:
              $ref: "#/components/schemas/Profile"
        profile:
          $ref: "#/components/schemas/Profile"
        latency_ms:
          type: integer
          format: int64
        created_at:
          type: string
          format: date-time
        has_error:
          type: boolean
        error_count:
          type: integer
        node_spans:
          type: array
          items:
            $ref: "#/components/schemas/TraceNodeSpan"
    TraceNodeSpan:
      type: object
      required: [id, node_name, sequence, latency_ms, started_at, ended_at, created_at]
      properties:
        id:
          type: string
        node_name:
          type: string
        sequence:
          type: integer
        latency_ms:
          type: integer
          format: int64
        error:
          type: string
        started_at:
          type: string
          format: date-time
        ended_at:
          type: string
          format: date-time
        created_at:
          type: string
          format: date-time
    CreateDatasetRequest:
      type: object
      required: [name]
      properties:
        project_id:
          type: string
          description: Owning project. Omission is a deprecated beta compatibility path.
        name:
          type: string
        kind:
          type: string
          default: golden
    Dataset:
      type: object
      required: [id, tenant_id, name, kind, version, created_at]
      properties:
        id:
          type: string
        tenant_id:
          type: string
        project_id:
          type: string
        name:
          type: string
        kind:
          type: string
        version:
          type: string
        created_at:
          type: string
          format: date-time
    CreateDatasetItemRequest:
      type: object
      required: [query, ground_truth]
      properties:
        query:
          type: string
        ground_truth:
          type: string
        relevant_doc_ids:
          type: array
          items:
            type: string
        diversity_annotations:
          type: array
          items:
            type: object
            additionalProperties: true
    DatasetItem:
      type: object
      required: [id, dataset_id, query, ground_truth, relevant_doc_ids]
      properties:
        id:
          type: string
        dataset_id:
          type: string
        query:
          type: string
        ground_truth:
          type: string
        relevant_doc_ids:
          type: array
          items:
            type: string
        diversity_annotations:
          type: array
          items:
            type: object
            additionalProperties: true
    RunEvaluationRequest:
      type: object
      required: [dataset_id, knowledge_base_id]
      properties:
        dataset_id:
          type: string
        knowledge_base_id:
          type: string
        profile:
          $ref: "#/components/schemas/Profile"
        top_k:
          type: integer
        scoped_shadow_item_id:
          type: string
          description: Optional offline regression scoped shadow item id. When set, candidate retrieval may only use the matching offline optimization item.
        split:
          type: string
          enum: [train, eval, holdout, gold]
          description: Optional dataset split filter. When omitted, all dataset items participate.
        holdout_gate:
          $ref: "#/components/schemas/HoldoutGateConfig"
        judge:
          $ref: "#/components/schemas/JudgeConfig"
        qag:
          $ref: "#/components/schemas/JudgeConfig"
        pairwise:
          $ref: "#/components/schemas/JudgeConfig"
    RunEvaluationResponse:
      type: object
      required: [id, dataset_id, profile, total, hit_rate, accuracy, created_at]
      description: |
        Evaluation run summary. `accuracy` and `hit_rate` are answer-focused
        compatibility fields equal to `metrics.answer_accuracy` for new runs.
        Rule-only runs write deterministic answer matching as
        `metrics.deterministic_answer_match`; `metrics.pairwise_accuracy` is only
        written for real pairwise judge win-rate/accuracy output. Historical
        stored runs may still contain rule-based `pairwise_accuracy`.
      example:
        id: eval_xxx
        project_id: prj_xxx
        dataset_id: ds_xxx
        profile: realtime
        total: 1
        hit_rate: 1
        accuracy: 1
        weighted_sample_count: 1
        unweighted_sample_count: 1
        split: holdout
        split_summary:
          holdout:
            unweighted_sample_count: 1
            weighted_sample_count: 1
        holdout_gate:
          enabled: true
          passed: true
          split: holdout
          quality_metric: deterministic_answer_match
          quality: 1
          min_quality: 0.8
          sample_count: 1
          weighted_sample_count: 1
        metrics:
          deterministic_answer_match: 1
          answer_accuracy: 1
          accuracy: 1
          hit_rate: 1
          citation_hit_rate: 1
          context_recall: 1
          citation_precision: 1
          ndcg_at_k: 1
          recall_at_k: 1
          mrr: 1
          map: 1
          coverage: 1
          retrieval_failure_rate: 0
          redundancy_rate: 0
          duplicate_count: 0
          deduped_top_k_count: 1
          alpha_ndcg: 1
          aspect_coverage: 1
          latency_p95_ms: 42
          cache_hit_rate: 0
        created_at: "2026-06-29T10:04:00Z"
      properties:
        project_id:
          type: string
        id:
          type: string
        dataset_id:
          type: string
        profile:
          type: string
        total:
          type: integer
        hit_rate:
          type: number
          format: double
          description: Answer-focused hit rate. New runs keep this equal to `answer_accuracy`.
        accuracy:
          type: number
          format: double
          description: Answer-focused compatibility alias for `answer_accuracy`.
        weighted_sample_count:
          type: number
          format: double
          description: Sum of normalized dataset item weights included in this run.
        unweighted_sample_count:
          type: integer
          description: Number of dataset items included in this run before weighting.
        split:
          type: string
          enum: [train, eval, holdout, gold]
          description: Requested dataset split used for this run, if any.
        split_summary:
          type: object
          additionalProperties:
            $ref: "#/components/schemas/SplitSummary"
          description: Per-split weighted and unweighted sample counts for the source dataset.
        missing_split:
          type: boolean
          description: True when a requested split had no matching dataset items.
        holdout_gate:
          $ref: "#/components/schemas/HoldoutGateResult"
        metrics:
          $ref: "#/components/schemas/EvaluationMetrics"
        created_at:
          type: string
          format: date-time
    SplitSummary:
      type: object
      required: [unweighted_sample_count, weighted_sample_count]
      properties:
        unweighted_sample_count:
          type: integer
        weighted_sample_count:
          type: number
          format: double
    HoldoutGateConfig:
      type: object
      properties:
        enabled:
          type: boolean
        min_sample_count:
          type: integer
        min_weighted_sample_count:
          type: number
          format: double
        quality_metric:
          type: string
          description: Quality metric to gate on. Defaults to deterministic_answer_match.
        min_quality:
          type: number
          format: double
    HoldoutGateResult:
      type: object
      properties:
        enabled:
          type: boolean
        passed:
          type: boolean
        reasons:
          type: array
          items:
            type: string
            enum: [missing_split, insufficient_sample, metric_missing, quality_below_threshold]
        split:
          type: string
          enum: [train, eval, holdout, gold]
        quality_metric:
          type: string
        quality:
          type: number
          format: double
        min_quality:
          type: number
          format: double
        sample_count:
          type: integer
        min_sample_count:
          type: integer
        weighted_sample_count:
          type: number
          format: double
        min_weighted_sample_count:
          type: number
          format: double
        missing_split:
          type: boolean
    EvaluationMetrics:
      type: object
      description: |
        Aggregated evaluation metrics. Item-level metrics are weighted by
        dataset item `weight` when present; `latency_p95_ms` is the weighted
        run-level P95 latency. Rule-only answer matching is reported as
        `deterministic_answer_match`. `pairwise_accuracy` has real pairwise
        judge win-rate/accuracy semantics and is retained for historical runs
        plus real pairwise judge output only.
      additionalProperties:
        type: number
        format: double
      properties:
        deterministic_answer_match:
          type: number
          format: double
          description: Deterministic rule-based answer match against ground truth. This replaces rule-only pairwise_accuracy.
        answer_accuracy:
          type: number
          format: double
          description: Answer-focused deterministic rule score; citations do not increase this metric.
        accuracy:
          type: number
          format: double
          description: Deterministic rule-based hit rate for backward compatibility.
        hit_rate:
          type: number
          format: double
          description: Alias for deterministic rule-based accuracy.
        pairwise_accuracy:
          type: number
          format: double
          description: Real pairwise judge win-rate/accuracy metric. Historical stored runs may contain rule-based values for compatibility.
          deprecated: true
        citation_hit_rate:
          type: number
          format: double
          description: Fraction of items where the response includes at least one citation; does not imply answer correctness.
        context_recall:
          type: number
          format: double
          description: Fraction of expected relevant documents observed in retrieved context.
        citation_precision:
          type: number
          format: double
          description: Fraction of citations that point to expected relevant documents.
        ndcg_at_k:
          type: number
          format: double
          description: Ranking quality normalized discounted cumulative gain at requested top_k.
        recall_at_k:
          type: number
          format: double
          description: Fraction of relevant documents retrieved within requested top_k.
        mrr:
          type: number
          format: double
          description: Mean reciprocal rank of the first relevant retrieved document.
        map:
          type: number
          format: double
          description: Mean average precision over relevant retrieved documents.
        coverage:
          type: number
          format: double
          description: Fraction of items with at least one relevant retrieved document.
        retrieval_failure_rate:
          type: number
          format: double
          description: Fraction of items with no relevant retrieved documents.
        redundancy_rate:
          type: number
          format: double
          description: Duplicate retrieved chunk ratio.
        duplicate_count:
          type: number
          format: double
          description: Average duplicate retrieved chunk count.
        deduped_top_k_count:
          type: number
          format: double
          description: Average non-duplicate retrieved chunk count.
        alpha_ndcg:
          type: number
          format: double
          description: Diversity-aware ranking quality when aspect annotations are available.
        aspect_coverage:
          type: number
          format: double
          description: Fraction of annotated aspects covered by retrieved context.
        latency_p95_ms:
          type: number
          format: double
          description: P95 end-to-end query latency in milliseconds.
        cache_hit_rate:
          type: number
          format: double
          description: Fraction of evaluation queries served from semantic cache.
    JudgeMetric:
      type: string
      enum:
        - faithfulness
        - groundedness
        - answer_relevance
        - hallucination
        - completeness
        - citation_support
        - instruction_following
        - safety
    JudgeConfig:
      type: object
      description: Optional LLM-as-Judge or QAG configuration. When supplied, the evaluation runner records raw/parsed judge outputs, token usage and cost.
      properties:
        provider:
          type: string
        model:
          type: string
        prompt_version:
          type: string
        metrics:
          type: array
          items:
            $ref: "#/components/schemas/JudgeMetric"
        rubric:
          $ref: "#/components/schemas/JudgeRubric"
        temperature:
          type: number
          format: double
        max_tokens:
          type: integer
        repeat:
          type: integer
        pairwise_swap:
          type: boolean
        strict_json:
          type: boolean
        ensemble:
          type: array
          items:
            $ref: "#/components/schemas/JudgeModelConfig"
        max_judge_calls:
          type: integer
        extra_params:
          type: object
          additionalProperties: true
        prompt_template:
          type: string
    JudgeModelConfig:
      type: object
      properties:
        provider:
          type: string
        model:
          type: string
        temperature:
          type: number
          format: double
        max_tokens:
          type: integer
        params:
          type: object
          additionalProperties: true
    JudgeRubric:
      type: object
      properties:
        name:
          type: string
        version:
          type: string
        criteria:
          type: array
          items:
            $ref: "#/components/schemas/RubricCriterion"
    RubricCriterion:
      type: object
      required: [metric]
      properties:
        metric:
          $ref: "#/components/schemas/JudgeMetric"
        description:
          type: string
        weight:
          type: number
          format: double
        scale:
          type: array
          items:
            type: string
    EvaluationDetail:
      type: object
      required: [run]
      properties:
        run:
          $ref: "#/components/schemas/RunEvaluationResponse"
        items:
          type: array
          items:
            $ref: "#/components/schemas/EvaluationItemDetail"
        judge_runs:
          type: array
          items:
            type: object
            additionalProperties: true
        judge_results:
          type: array
          items:
            type: object
            additionalProperties: true
        pairwise_judge_results:
          type: array
          items:
            type: object
            additionalProperties: true
        judge_calibration_runs:
          type: array
          items:
            type: object
            additionalProperties: true
    EvaluationItemDetail:
      type: object
      required: [run_id, dataset_item_id, answer]
      properties:
        run_id:
          type: string
        dataset_item_id:
          type: string
        answer:
          type: string
        metrics:
          $ref: "#/components/schemas/EvaluationMetrics"
    CreateOfflineKnowledgeRunRequest:
      type: object
      required: [window_start, window_end]
      properties:
        knowledge_base_id:
          type: string
          description: Optional compatibility alias for `kb_id`.
        kb_id:
          type: string
          description: Knowledge base scope. Empty scope uses the service default/all-knowledge-bases behavior.
        window_start:
          type: string
          format: date-time
        window_end:
          type: string
          format: date-time
        config_hash:
          type: string
          description: Optional idempotency hash for the organizer configuration. If omitted, the service derives it from `config_json`.
        config_json:
          type: object
          additionalProperties: true
        max_questions:
          type: integer
          minimum: 1
        max_clusters:
          type: integer
          minimum: 1
    OfflineKnowledgeRunCreateResponse:
      type: object
      required: [run, deduplicated]
      properties:
        run:
          $ref: "#/components/schemas/OfflineKnowledgeRun"
        deduplicated:
          type: boolean
          description: True when an existing run matched tenant, knowledge base, time window, and config hash.
    OfflineKnowledgeRunResult:
      type: object
      required: [run, deduplicated, processed_cluster, created_items]
      properties:
        run:
          $ref: "#/components/schemas/OfflineKnowledgeRun"
        deduplicated:
          type: boolean
        processed_cluster:
          type: integer
        created_items:
          type: array
          items:
            $ref: "#/components/schemas/OptimizationItem"
    OfflineKnowledgeSchedulerTriggerRequest:
      type: object
      properties:
        scheduled_at:
          type: string
          format: date-time
          description: Optional trigger time. Defaults to the server clock when omitted.
    OfflineKnowledgeSchedulerTriggerResponse:
      type: object
      required: [items]
      properties:
        items:
          type: array
          items:
            type: object
            required: [target, request, result, deduplicated]
            properties:
              target:
                type: object
                required: [tenant_id, kb_id]
                properties:
                  tenant_id:
                    type: string
                  kb_id:
                    type: string
              request:
                $ref: "#/components/schemas/CreateOfflineKnowledgeRunRequest"
              result:
                $ref: "#/components/schemas/OfflineKnowledgeRunResult"
              deduplicated:
                type: boolean
              err:
                type: object
                additionalProperties: true
    OfflineKnowledgeRunListResponse:
      type: object
      required: [items]
      properties:
        items:
          type: array
          items:
            $ref: "#/components/schemas/OfflineKnowledgeRun"
    QuestionClusterListResponse:
      type: object
      required: [items]
      properties:
        items:
          type: array
          items:
            $ref: "#/components/schemas/QuestionCluster"
    OptimizationItemListResponse:
      type: object
      required: [items]
      properties:
        items:
          type: array
          items:
            $ref: "#/components/schemas/OptimizationItem"
    OfflineKnowledgeRunStatus:
      type: string
      enum: [pending, running, completed, failed]
    OptimizationItemStatus:
      type: string
      enum: [candidate, evidence_validating, needs_review, verified, shadow_enabled, regression_passed, regression_failed, published, knowledge_gap, rejected, stale, deprecated]
    OptimizationItemType:
      type: string
      enum: [answer_item, query_rewrite_item, knowledge_gap_item]
    RecallQuality:
      type: string
      enum: [hit, partial_hit, miss, bad_answer, no_answer_in_kb, ambiguous, duplicate]
    FailureType:
      type: string
      enum: [keyword_mismatch, semantic_gap, chunk_boundary, rerank_error, graph_missing, generation_error, knowledge_gap, unclear_question]
    OfflineKnowledgeRun:
      type: object
      required: [id, tenant_id, kb_id, status, window_start, window_end, config_hash, started_at]
      properties:
        id:
          type: string
        tenant_id:
          type: string
        kb_id:
          type: string
        status:
          $ref: "#/components/schemas/OfflineKnowledgeRunStatus"
        window_start:
          type: string
          format: date-time
        window_end:
          type: string
          format: date-time
        config_hash:
          type: string
        config_json:
          type: object
          additionalProperties: true
        started_at:
          type: string
          format: date-time
        finished_at:
          type: string
          format: date-time
        error:
          type: string
    QuestionCluster:
      type: object
      required: [id, tenant_id, run_id, kb_id, canonical_question, normalized_question, question_hash, occurrence_count, sample_questions, trace_ids, created_at]
      properties:
        id:
          type: string
        tenant_id:
          type: string
        run_id:
          type: string
        kb_id:
          type: string
        canonical_question:
          type: string
        normalized_question:
          type: string
        question_hash:
          type: string
        embedding_ref:
          type: string
        embedding_json:
          type: array
          items:
            type: number
            format: double
        occurrence_count:
          type: integer
        sample_questions:
          type: array
          items:
            type: string
        trace_ids:
          type: array
          items:
            type: string
        created_at:
          type: string
          format: date-time
    OptimizationItem:
      type: object
      required: [id, tenant_id, run_id, kb_id, question_cluster_id, item_type, status, canonical_question, recall_quality, confidence, source_fingerprints, evidence, deep_search_steps, created_at, updated_at]
      description: |
        Offline knowledge recommendation item. `answer_item` values are recall
        guidance only: shadow retrieval may surface source fingerprints and
        evidence metadata, but the runtime must still ground answers in recalled
        chunks rather than injecting `final_answer` as a direct response.
      properties:
        id:
          type: string
        tenant_id:
          type: string
        run_id:
          type: string
        kb_id:
          type: string
        question_cluster_id:
          type: string
        item_type:
          $ref: "#/components/schemas/OptimizationItemType"
        status:
          $ref: "#/components/schemas/OptimizationItemStatus"
        canonical_question:
          type: string
        final_answer:
          type: string
          description: Candidate answer text retained for review and evidence validation; not a direct answer-injection payload.
        recall_quality:
          $ref: "#/components/schemas/RecallQuality"
        failure_type:
          $ref: "#/components/schemas/FailureType"
        confidence:
          type: number
          format: double
        source_fingerprints:
          type: array
          description: Source identity uses `doc_version` and `chunk_content_hash` to detect stale or mutated evidence.
          items:
            $ref: "#/components/schemas/SourceFingerprint"
        evidence:
          type: array
          items:
            $ref: "#/components/schemas/Evidence"
        deep_search_steps:
          type: array
          items:
            $ref: "#/components/schemas/DeepSearchStep"
        eval_report_json:
          $ref: "#/components/schemas/EvalReport"
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
        published_at:
          type: string
          format: date-time
    SourceFingerprint:
      type: object
      required: [doc_id, doc_version, chunk_id, chunk_content_hash]
      description: Stable source fingerprint. Validation compares both `doc_version` and `chunk_content_hash`; doc ID alone is not sufficient.
      properties:
        doc_id:
          type: string
        doc_version:
          type: string
        chunk_id:
          type: string
        chunk_content_hash:
          type: string
          example: sha256:abc123
    Evidence:
      type: object
      required: [chunk_id, doc_id, quote, supports]
      properties:
        chunk_id:
          type: string
        doc_id:
          type: string
        quote:
          type: string
          description: Exact text that must be contained in the current source chunk.
        supports:
          type: string
          description: Claim or answer fragment supported by this quote.
    DeepSearchStep:
      type: object
      required: [step, tool, query, observation, decision]
      properties:
        step:
          type: integer
        tool:
          type: string
        query:
          type: string
        observation:
          type: string
        decision:
          type: string
    EvalReport:
      type: object
      required: [result, thresholds, passed, full_dataset_required, full_dataset_used, evaluated_at]
      properties:
        result:
          $ref: "#/components/schemas/RegressionResult"
        scoped_item_id:
          type: string
          description: Offline optimization item id that was scoped into candidate regression retrieval.
        profile_neutrality:
          $ref: "#/components/schemas/ProfileNeutrality"
        profile_experiment:
          $ref: "#/components/schemas/ProfileExperiment"
        holdout_gate:
          $ref: "#/components/schemas/HoldoutGateResult"
        thresholds:
          $ref: "#/components/schemas/RegressionThresholds"
        passed:
          type: boolean
        reasons:
          type: array
          items:
            type: string
        full_dataset_required:
          type: boolean
        full_dataset_used:
          type: boolean
        evaluated_at:
          type: string
          format: date-time
    RegressionResult:
      type: object
      required: [recall_lift, answer_quality_lift, citation_coverage_lift, latency_delta_ms, token_cost_delta, hallucination_risk, full_dataset_used, passed]
      properties:
        recall_lift:
          type: number
          format: double
        answer_quality_lift:
          type: number
          format: double
        citation_coverage_lift:
          type: number
          format: double
        latency_delta_ms:
          type: integer
          format: int64
          description: Latency delta between with-optimization and baseline runs in milliseconds.
        token_cost_delta:
          type: number
          format: double
        hallucination_risk:
          type: number
          format: double
        full_dataset_used:
          type: boolean
        passed:
          type: boolean
        scoped_item_id:
          type: string
          description: Offline optimization item id used to scope candidate shadow retrieval.
        profile_neutrality:
          $ref: "#/components/schemas/ProfileNeutrality"
        profile_experiment:
          $ref: "#/components/schemas/ProfileExperiment"
        holdout_gate:
          $ref: "#/components/schemas/HoldoutGateResult"
    ProfileNeutrality:
      type: object
      properties:
        baseline_profile:
          type: string
        candidate_profile:
          type: string
        same_profile:
          type: boolean
        optimization_lift_only:
          type: boolean
          description: True when baseline and candidate use the same profile, so lift is attributable to the offline optimization item.
    ProfileExperiment:
      type: object
      properties:
        enabled:
          type: boolean
        baseline_profile:
          type: string
        candidate_profile:
          type: string
    RegressionThresholds:
      type: object
      properties:
        min_recall_lift:
          type: number
          format: double
        min_answer_quality_lift:
          type: number
          format: double
        min_citation_coverage_lift:
          type: number
          format: double
        max_latency_delta:
          type: integer
          format: int64
          description: Go duration encoded as nanoseconds in JSON.
        max_token_cost_delta:
          type: number
          format: double
        max_hallucination_risk:
          type: number
          format: double
    BulkRevalidateRequest:
      type: object
      properties:
        knowledge_base_id:
          type: string
          description: Optional compatibility alias for `kb_id`.
        kb_id:
          type: string
        status:
          $ref: "#/components/schemas/OptimizationItemStatus"
        source_fingerprint:
          $ref: "#/components/schemas/SourceFingerprint"
        source_doc_id:
          type: string
        source_chunk_id:
          type: string
        source_content_hash:
          type: string
          description: Filters by `source_fingerprints[].chunk_content_hash`.
        limit:
          type: integer
          minimum: 1
    RevalidateResult:
      type: object
      required: [item, updated, skipped, old_status, new_status]
      properties:
        item:
          $ref: "#/components/schemas/OptimizationItem"
        updated:
          type: boolean
        skipped:
          type: boolean
        old_status:
          $ref: "#/components/schemas/OptimizationItemStatus"
        new_status:
          $ref: "#/components/schemas/OptimizationItemStatus"
        error:
          type: string
          nullable: true
    BulkRevalidateResult:
      type: object
      required: [matched, updated, skipped, results]
      properties:
        matched:
          type: integer
        updated:
          type: integer
        skipped:
          type: integer
        results:
          type: array
          items:
            $ref: "#/components/schemas/RevalidateResult"
    ShadowMatch:
      type: object
      required: [item_id, item_type, source, score, rank]
      description: Shadow retrieval match. By default this is non-invasive guidance; injection requires explicit runtime configuration.
      properties:
        item_id:
          type: string
        item_type:
          $ref: "#/components/schemas/OptimizationItemType"
        source:
          type: string
          enum: [optimization_library]
        score:
          type: number
          format: double
        rank:
          type: integer
        answer_item:
          $ref: "#/components/schemas/ShadowAnswerItem"
        metadata:
          type: object
          additionalProperties: true
    ShadowAnswerItem:
      type: object
      required: [source_fingerprints, evidence]
      description: Recall guidance metadata for an `answer_item`; it is not a direct generated answer.
      properties:
        source_fingerprints:
          type: array
          items:
            $ref: "#/components/schemas/SourceFingerprint"
        evidence:
          type: array
          items:
            $ref: "#/components/schemas/Evidence"
        guidance_metadata:
          type: object
          additionalProperties: true
    ShadowRetrievalEvent:
      type: object
      required: [id, tenant_id, kb_id, item_id, trace_id, query, matched, injected, created_at]
      properties:
        id:
          type: string
        tenant_id:
          type: string
        kb_id:
          type: string
        item_id:
          type: string
        trace_id:
          type: string
        query:
          type: string
        matched:
          type: boolean
        injected:
          type: boolean
          description: False for default non-invasive shadow mode.
        rank:
          type: integer
        score:
          type: number
          format: double
        recall_lift:
          type: number
          format: double
        answer_lift:
          type: number
          format: double
        hallucination_risk:
          type: number
          format: double
        created_at:
          type: string
          format: date-time
    RalphLoopRequest:
      type: object
      required: [task_spec_path, task_id, mode, max_rounds]
      description: |
        Input contract for the Ralph Loop agent capability. The Task 1
        capability manifest marks the runtime endpoint as planned; MCP tools
        and Skill manifests use this schema as their input source of truth.
      properties:
        task_spec_path:
          type: string
          description: Repository-relative path to the spec tasks file or task directory.
          example: .trae/specs/add-ralph-loop-mcp-skills/tasks.md
        task_id:
          type: string
          description: Task label to verify, for example `Task 1`.
          example: Task 1
        mode:
          type: string
          enum: [focused, broad]
          description: Focused verifies the requested task only; broad verifies dependent surfaces too.
          example: focused
        max_rounds:
          type: integer
          minimum: 1
          maximum: 20
          description: Maximum Ralph Loop verification rounds allowed for this invocation.
          example: 1
        base_ref:
          type: string
          description: Optional git base ref used for diff-aware verification.
          example: origin/main
        notes:
          type: string
          description: Optional operator instructions that must not override the task boundary.
    RalphLoopResponse:
      type: object
      required: [run_id, status, verdict, summary, trace_id, artifacts]
      description: Ralph Loop result envelope returned to MCP clients and generated Skills.
      properties:
        run_id:
          type: string
          example: ralph_loop_20260705_task1
        status:
          type: string
          enum: [queued, running, completed, failed]
          example: completed
        verdict:
          type: string
          enum: [pass, fail, blocked]
          example: pass
        summary:
          type: string
          description: Human-readable verification summary.
        trace_id:
          type: string
          description: Request trace identifier correlated with logs and trace lookup.
          example: trace_ralph_loop_example
        artifacts:
          type: array
          description: Files or commands that provide evidence for the verdict.
          items:
            $ref: "#/components/schemas/RalphLoopArtifact"
        error:
          $ref: "#/components/schemas/ErrorBody"
    RalphLoopArtifact:
      type: object
      required: [kind, name]
      properties:
        kind:
          type: string
          enum: [file, command, trace]
        name:
          type: string
        uri:
          type: string
        content_type:
          type: string
        sha256:
          type: string
    OptimizeRequest:
      type: object
      required: [dataset_id, knowledge_base_id]
      description: |
        Asynchronous target-driven optimizer request. `profiles` and `top_ks`
        are accepted as a backward-compatible shortcut and are mapped into an
        internal RAG runner profile and retrieval top-k search space.
      properties:
        dataset_id:
          type: string
        knowledge_base_id:
          type: string
        objective:
          $ref: "#/components/schemas/ObjectiveSpec"
        search_space:
          $ref: "#/components/schemas/SearchSpace"
        search:
          $ref: "#/components/schemas/SearchSpec"
        budget:
          $ref: "#/components/schemas/OptimizerBudget"
        profile:
          $ref: "#/components/schemas/Profile"
        top_k:
          type: integer
        namespace_ttl_seconds:
          type: integer
        selection_split:
          type: string
          example: eval
        holdout_split:
          type: string
          example: holdout
        holdout_gate:
          $ref: "#/components/schemas/HoldoutGateConfig"
        runner:
          type: object
          additionalProperties: true
        profiles:
          type: array
          items:
            $ref: "#/components/schemas/Profile"
        top_ks:
          type: array
          items:
            type: integer
    ObjectiveSpec:
      type: object
      properties:
        maximize:
          type: string
          example: pairwise_accuracy
        constraints:
          type: array
          items:
            $ref: "#/components/schemas/ObjectiveConstraint"
        tie_breakers:
          type: array
          items:
            $ref: "#/components/schemas/ObjectiveTieBreaker"
        budget:
          $ref: "#/components/schemas/ObjectiveBudget"
        baseline_id:
          type: string
        bootstrap_iterations:
          type: integer
        significance_alpha:
          type: number
          format: double
    ObjectiveConstraint:
      type: object
      required: [expression]
      properties:
        expression:
          type: string
          example: latency_p95_ms <= 1000
    ObjectiveTieBreaker:
      type: object
      required: [metric]
      properties:
        metric:
          type: string
          example: latency_p95_ms
        direction:
          type: string
          enum: [asc, desc]
    ObjectiveBudget:
      type: object
      properties:
        latency_p95_limit_ms:
          type: number
          format: double
        cost_limit_usd:
          type: number
          format: double
    OptimizerBudget:
      type: object
      properties:
        max_judge_calls:
          type: integer
        max_cost_usd:
          type: number
          format: double
        max_wall_time_seconds:
          type: integer
    SearchSpec:
      type: object
      properties:
        strategy:
          type: string
          enum: [grid, seeded_random, successive_halving]
        max_candidates:
          type: integer
        seed:
          type: integer
          format: int64
        large_space_warning_threshold:
          type: integer
          format: int64
    SearchSpace:
      type: object
      properties:
        prompts:
          type: array
          items:
            type: object
            additionalProperties: true
        chunking:
          type: object
          additionalProperties: true
        embedding:
          type: object
          additionalProperties: true
        reranker:
          type: object
          additionalProperties: true
        retrieval:
          $ref: "#/components/schemas/RetrievalSearchSpace"
        graph:
          type: object
          additionalProperties: true
        harness:
          type: array
          items:
            $ref: "#/components/schemas/HarnessCandidate"
    RetrievalSearchSpace:
      type: object
      properties:
        dense_top_k:
          type: array
          items:
            type: integer
        sparse_top_k:
          type: array
          items:
            type: integer
        rrf_k:
          type: array
          items:
            type: integer
        semantic_cache_thresholds:
          type: array
          items:
            type: number
            format: double
    HarnessCandidate:
      type: object
      description: External harness config. Use argv array only; shell command strings and `${VAR}` interpolation are rejected.
      properties:
        kind:
          type: string
        argv:
          type: array
          items:
            type: string
        command:
          type: string
          deprecated: true
    CandidateResult:
      type: object
      required: [profile, top_k, score, pairwise_accuracy, run_id]
      description: |
        Deprecated optimization candidate summary. `score` is the objective
        ranking score. Rule-only fallback uses `deterministic_answer_match`;
        `pairwise_accuracy` is retained for legacy clients and real pairwise
        judge output.
      properties:
        profile:
          $ref: "#/components/schemas/Profile"
        top_k:
          type: integer
        score:
          type: number
          format: double
          description: Objective ranking score for this candidate.
        score_metric:
          type: string
          description: Metric that supplied `score`, for example deterministic_answer_match or pairwise_accuracy.
        deterministic_answer_match:
          type: number
          format: double
          description: Deterministic rule-based answer match fallback metric.
        pairwise_accuracy:
          type: number
          format: double
          description: Real pairwise judge win-rate/accuracy metric or historical compatibility field.
          deprecated: true
        ndcg_at_k:
          type: number
          format: double
        recall_at_k:
          type: number
          format: double
        mrr:
          type: number
          format: double
        map:
          type: number
          format: double
        retrieval_failure_rate:
          type: number
          format: double
        redundancy_rate:
          type: number
          format: double
        duplicate_count:
          type: number
          format: double
        deduped_top_k_count:
          type: number
          format: double
        alpha_ndcg:
          type: number
          format: double
        aspect_coverage:
          type: number
          format: double
        latency_p95_ms:
          type: number
          format: double
        run_id:
          type: string
    OptimizeResult:
      type: object
      deprecated: true
      required: [id, status, best, candidates]
      example:
        id: opt_xxx
        status: completed
        best:
          profile: high_precision
          top_k: 8
          score: 1
          pairwise_accuracy: 1
          ndcg_at_k: 1
          recall_at_k: 1
          mrr: 1
          map: 1
          retrieval_failure_rate: 0
          redundancy_rate: 0
          duplicate_count: 0
          deduped_top_k_count: 8
          alpha_ndcg: 1
          aspect_coverage: 1
          latency_p95_ms: 42
          run_id: eval_best
        candidates:
          - profile: realtime
            top_k: 5
            score: 0
            pairwise_accuracy: 0
            ndcg_at_k: 0.5
            recall_at_k: 0.5
            mrr: 0.5
            map: 0.5
            retrieval_failure_rate: 0.5
            redundancy_rate: 0.2
            duplicate_count: 1
            deduped_top_k_count: 4
            alpha_ndcg: 0.5
            aspect_coverage: 0.5
            latency_p95_ms: 30
            run_id: eval_realtime
          - profile: high_precision
            top_k: 8
            score: 1
            pairwise_accuracy: 1
            ndcg_at_k: 1
            recall_at_k: 1
            mrr: 1
            map: 1
            retrieval_failure_rate: 0
            redundancy_rate: 0
            duplicate_count: 0
            deduped_top_k_count: 8
            alpha_ndcg: 1
            aspect_coverage: 1
            latency_p95_ms: 42
            run_id: eval_best
      properties:
        id:
          type: string
        status:
          type: string
        best:
          $ref: "#/components/schemas/CandidateResult"
        candidates:
          type: array
          items:
            $ref: "#/components/schemas/CandidateResult"
    OptimizationAcceptedResponse:
      type: object
      required: [run_id, status, poll_url, cancel_url, resume_url]
      properties:
        run_id:
          type: string
        status:
          type: string
          enum: [queued, running, completed, canceling, canceled, budget_stopped, failed]
        poll_url:
          type: string
          example: /v1/optimizations/opt_xxx
        cancel_url:
          type: string
          example: /v1/optimizations/opt_xxx:cancel
        resume_url:
          type: string
          example: /v1/optimizations/opt_xxx:resume
    OptimizationCancelRequest:
      type: object
      properties:
        reason:
          type: string
    OptimizationStatusResponse:
      type: object
      required: [run, candidates]
      properties:
        run:
          $ref: "#/components/schemas/OptimizationRun"
        candidates:
          type: array
          items:
            $ref: "#/components/schemas/OptimizationCandidate"
    OptimizationRun:
      type: object
      required: [id, dataset_id, knowledge_base_id, objective, search_space, status, sampling_strategy, search_space_size, sampled_candidate_count, completed_candidate_count, checkpoint, created_at, updated_at]
      properties:
        id:
          type: string
        project_id:
          type: string
        dataset_id:
          type: string
        knowledge_base_id:
          type: string
        objective:
          $ref: "#/components/schemas/ObjectiveSpec"
        search_space:
          $ref: "#/components/schemas/SearchSpace"
        runner:
          type: object
          additionalProperties: true
        status:
          type: string
          enum: [queued, running, completed, canceling, canceled, budget_stopped, failed]
        status_reason:
          type: string
        best_candidate_id:
          type: string
        holdout_candidate_id:
          type: string
        holdout_gate:
          $ref: "#/components/schemas/HoldoutGateResult"
        sampling_strategy:
          type: string
        search_space_size:
          type: integer
          format: int64
        sampled_candidate_count:
          type: integer
        completed_candidate_count:
          type: integer
        checkpoint:
          type: object
          additionalProperties: true
        cost_usd:
          type: number
          format: double
        cost_budget_usd:
          type: number
          format: double
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
    OptimizationCandidate:
      type: object
      required: [id, optimization_run_id, config, status, created_at, updated_at]
      properties:
        id:
          type: string
        optimization_run_id:
          type: string
        config:
          type: object
          additionalProperties: true
        status:
          type: string
          enum: [queued, running, evaluated, judged, scored, promoted, holdout_evaluated, cleanup_done, failed]
        evaluation_run_id:
          type: string
        judge_run_id:
          type: string
        objective_score:
          type: number
          format: double
        holdout_score:
          type: number
          format: double
        confidence:
          type: object
          additionalProperties:
            type: number
            format: double
        metrics:
          $ref: "#/components/schemas/EvaluationMetrics"
        token_usage:
          type: object
          additionalProperties: true
        cost_usd:
          type: number
          format: double
        artifacts:
          type: object
          additionalProperties: true
        temp_namespaces:
          type: array
          items:
            type: object
            additionalProperties: true
        cleanup_status:
          type: string
          enum: [not_required, pending, done, failed]
        expires_at:
          type: string
          format: date-time
        error:
          type: string
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
