openapi: 3.1.0
info:
  title: Bernstein Task Server
  description: >
    Bernstein REST API - the open-source governance layer for AI agents, one git worktree per task.


    ## Authentication


    Authentication is ENABLED by default.  Include a Bearer token in all requests:


    ```

    Authorization: Bearer <token>

    ```


    To run without auth (development only), set `BERNSTEIN_AUTH_DISABLED=1` - this logs a loud
    warning and passes every request through.


    Public endpoints (no auth required): `/health`, `/health/ready`, `/health/live`, `/ready`,
    `/alive`, `/.well-known/agent.json`, `/docs`, `/openapi.json`, and the auth-flow endpoints
    (`/auth/login`, `/auth/oidc/callback`, etc.).


    Webhook and hook endpoints (`/webhook`, `/webhooks/*`, `/hooks/{session_id}`) authenticate via
    HMAC-SHA256 signatures - they do NOT accept Bearer tokens.


    ## Base URL


    Default: `http://127.0.0.1:8052`. Override with env vars `BERNSTEIN_HOST` and `BERNSTEIN_PORT`.


    ## Error Format


    All errors return JSON with a `detail` field:


    ```json

    {"detail": "Task not found: task-xyz"}

    ```


    | Status | Meaning |

    |--------|---------|

    | 400 | Bad request (validation error) |

    | 401 | Unauthorized (missing/invalid token) |

    | 403 | Forbidden (IP not in allowlist) |

    | 404 | Resource not found |

    | 409 | Conflict (task already in terminal state) |

    | 429 | Rate limited - respect the `Retry-After` header |

    | 500 | Internal server error |
  version: 3.19.0
  license:
    name: Apache 2.0
    url: https://www.apache.org/licenses/LICENSE-2.0
  contact:
    name: Alex Chernysh
    url: https://github.com/sipyourdrink-ltd/bernstein
paths:
  /:
    get:
      summary: Root
      operationId: root__get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                additionalProperties:
                  type: string
                type: object
                title: Response Root  Get
  /tasks/search:
    get:
      summary: Search Tasks
      description: |-
        Search tasks with pagination, sorting, and filtering.

        Query params:
            page: Page number (1-based, default 1).
            per_page: Items per page (1-100, default 20).
            sort: Sort field (created_at, priority, title, role, status).
            order: Sort order (asc, desc; default desc).
            status: Filter by task status.
            role: Filter by task role.
            assigned_agent: Filter by assigned agent.
      operationId: search_tasks_tasks_search_get
      parameters:
        - name: page
          in: query
          required: false
          schema:
            type: integer
            default: 1
            title: Page
        - name: per_page
          in: query
          required: false
          schema:
            type: integer
            default: 20
            title: Per Page
        - name: sort
          in: query
          required: false
          schema:
            type: string
            default: created_at
            title: Sort
        - name: order
          in: query
          required: false
          schema:
            type: string
            default: desc
            title: Order
        - name: status
          in: query
          required: false
          schema:
            anyOf:
              - type: string
              - type: "null"
            title: Status
        - name: role
          in: query
          required: false
          schema:
            anyOf:
              - type: string
              - type: "null"
            title: Role
        - name: assigned_agent
          in: query
          required: false
          schema:
            anyOf:
              - type: string
              - type: "null"
            title: Assigned Agent
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PaginatedSearchResponse"
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /agents:
    get:
      summary: List Agents
      description: |-
        Return a flat list of agent sessions for the web GUI grid.

        When ``TaskStore.agents`` is empty (e.g. only mock adapters spawned and
        they never heartbeat) we fall back to synthesising one entry per
        claimed/in-progress task, marked with ``"synthetic": true``. That keeps
        the GUI grid populated during demos and avoids the dreaded "0 sessions"
        empty state when work is obviously in flight.

        Both branches render the id and title of the task a session is on, so
        both narrow to the caller's tenant scope: the live sessions carry no
        tenant of their own and are placed by the task they name, while the
        synthesised entries are built from a scoped read in the first place.
      operationId: list_agents_agents_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                items:
                  additionalProperties: true
                  type: object
                type: array
                title: Response List Agents Agents Get
  /agents/comparison:
    get:
      tags:
        - agent-comparison
      summary: Get Agent Comparison
      description: |-
        Return per-(adapter, model) performance comparison metrics.

        Aggregates data from all agent sessions in the current run:
        success rate, average completion time, cost per task, and
        quality gate pass rate.

        Returns:
            JSON list of :class:`AgentMetrics` objects sorted by adapter
            then model.
      operationId: get_agent_comparison_agents_comparison_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                items:
                  $ref: "#/components/schemas/AgentMetrics"
                type: array
                title: Response Get Agent Comparison Agents Comparison Get
  /agents/{session_id}/logs:
    get:
      summary: Agent Logs
      description: |-
        Return log file content for a session.

        Args:
            session_id: Agent session ID.
            tail_bytes: If > 0, return only the last N bytes of the log.
      operationId: agent_logs_agents__session_id__logs_get
      parameters:
        - name: session_id
          in: path
          required: true
          schema:
            type: string
            title: Session Id
        - name: tail_bytes
          in: query
          required: false
          schema:
            type: integer
            default: 0
            title: Tail Bytes
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AgentLogsResponse"
        "404":
          description: No log file for session
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /agents/{session_id}/kill:
    post:
      summary: Agent Kill
      description: |-
        Request that an agent session be killed.

        Writes a ``.kill`` signal file that the orchestrator picks up on
        its next tick.
      operationId: agent_kill_agents__session_id__kill_post
      parameters:
        - name: session_id
          in: path
          required: true
          schema:
            type: string
            title: Session Id
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AgentKillResponse"
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /agents/{session_id}/stream:
    get:
      summary: Agent Stream
      description: SSE stream of live log output for a session.
      operationId: agent_stream_agents__session_id__stream_get
      parameters:
        - name: session_id
          in: path
          required: true
          schema:
            type: string
            title: Session Id
      responses:
        "200":
          description: Server-Sent Events stream. The response body does not terminate.
          content:
            text/event-stream:
              schema:
                type: string
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /auth/providers:
    get:
      tags:
        - authentication
      summary: Auth Providers
      description: List available authentication providers.
      operationId: auth_providers_auth_providers_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AuthProvidersResponse"
  /auth/login:
    get:
      tags:
        - authentication
      summary: Login
      description: Initiate SSO login. Redirects to IdP.
      operationId: login_auth_login_get
      parameters:
        - name: provider
          in: query
          required: false
          schema:
            $ref: "#/components/schemas/LoginProvider"
            default: oidc
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
        "400":
          description: Authentication provider not enabled
        "404":
          description: SSO authentication is not configured on this server
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /auth/oidc/callback:
    get:
      tags:
        - authentication
      summary: Oidc Callback
      description: OIDC authorization code callback.
      operationId: oidc_callback_auth_oidc_callback_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
        "400":
          description: Missing or invalid authorization code or state
        "404":
          description: SSO authentication is not configured on this server
  /auth/saml/acs:
    post:
      tags:
        - authentication
      summary: Saml Acs
      description: |-
        SAML Assertion Consumer Service (ACS) endpoint.

        Receives the SAML Response from the IdP via HTTP-POST binding.
      operationId: saml_acs_auth_saml_acs_post
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
        "400":
          description: Missing SAMLResponse
        "404":
          description: SSO authentication is not configured on this server
  /auth/saml/metadata:
    get:
      tags:
        - authentication
      summary: Saml Metadata
      description: SAML SP metadata endpoint for IdP configuration.
      operationId: saml_metadata_auth_saml_metadata_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
        "404":
          description: SSO authentication is not configured on this server
  /auth/cli/device:
    post:
      tags:
        - authentication
      summary: Device Code Request
      description: |-
        Initiate device authorization flow for CLI login.

        The CLI calls this to get a device_code and user_code.
        The user enters the user_code in the web dashboard after SSO login
        to authorize the CLI session.
      operationId: device_code_request_auth_cli_device_post
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/DeviceCodeRequest"
        required: true
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DeviceCodeResponse"
        "404":
          description: SSO authentication is not configured on this server
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /auth/cli/token:
    post:
      tags:
        - authentication
      summary: Device Token Poll
      description: |-
        Poll for device authorization status.

        Returns the access token once the user has authorized the device code.
      operationId: device_token_poll_auth_cli_token_post
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/DevicePollRequest"
        required: true
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DevicePollResponse"
        "404":
          description: SSO authentication is not configured on this server
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /auth/cli/authorize:
    post:
      tags:
        - authentication
      summary: Device Authorize
      description: |-
        Authorize a device code (called from web dashboard after SSO login).

        Requires an authenticated user session.
      operationId: device_authorize_auth_cli_authorize_post
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/DeviceAuthorizeRequest"
        required: true
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
        "400":
          description: Invalid or expired user code
        "401":
          description: Authentication required
        "404":
          description: SSO authentication is not configured on this server
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /auth/me:
    get:
      tags:
        - authentication
      summary: Get Profile
      description: Get the current authenticated user's profile.
      operationId: get_profile_auth_me_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/UserProfileResponse"
        "401":
          description: Authentication required
  /auth/logout:
    post:
      tags:
        - authentication
      summary: Logout
      description: Logout and revoke the current session.
      operationId: logout_auth_logout_post
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
        "404":
          description: SSO authentication is not configured on this server
  /auth/group-mappings:
    get:
      tags:
        - authentication
      summary: Get Group Mappings
      description: Get current SSO group → role mappings.
      operationId: get_group_mappings_auth_group_mappings_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/GroupMappingsResponse"
        "404":
          description: SSO authentication is not configured on this server
    put:
      tags:
        - authentication
      summary: Update Group Mappings
      description: Update SSO group → role mappings (admin only).
      operationId: update_group_mappings_auth_group_mappings_put
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/GroupMappingsUpdateRequest"
        required: true
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
        "400":
          description: Invalid role value
        "401":
          description: Authentication required
        "403":
          description: Admin role required
        "404":
          description: SSO authentication is not configured on this server
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /auth/users:
    get:
      tags:
        - authentication
      summary: List Users
      description: List all users (admin only).
      operationId: list_users_auth_users_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
        "401":
          description: Authentication required
        "403":
          description: Admin role required
        "404":
          description: SSO authentication is not configured on this server
  /tasks:
    post:
      summary: Create Task
      description: Create a new task.
      operationId: create_task_tasks_post
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/TaskCreate"
      responses:
        "201":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TaskResponse"
        "400":
          description: Blocked by pre-create hook
        "403":
          description: Tenant access denied
        "404":
          description: Tenant not found
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
        "429":
          description: Tenant task quota exceeded
    get:
      summary: List Tasks
      description: |-
        List tasks, optionally filtered by status, cell_id, and/or claim owner.

        When ``limit`` or ``offset`` query params are provided the response is a
        paginated envelope (``{tasks, total, limit, offset}``).  Without them,
        the legacy flat list is returned for backward compatibility, capped at
        ``_LIST_TASKS_HARD_CAP`` items and accompanied by a ``Deprecation``
        header asking callers to pass explicit pagination.

        Args:
            request: FastAPI request.
            status: If provided, only tasks with this status are returned.
            cell_id: If provided, only tasks in this cell are returned.
            tenant: Tenant scope override.
            claimed_by_session: If provided, only tasks claimed by this parent
                orchestrator session are returned.
            limit: Maximum number of tasks to return (max 500).  Triggers
                paginated response when present.
            offset: Number of tasks to skip.  Triggers paginated response
                when present.

        Returns:
            Paginated response **or** plain list of TaskResponse dicts (capped
            at ``_LIST_TASKS_HARD_CAP``).
      operationId: list_tasks_tasks_get
      parameters:
        - name: status
          in: query
          required: false
          schema:
            anyOf:
              - type: string
              - type: "null"
            title: Status
        - name: cell_id
          in: query
          required: false
          schema:
            anyOf:
              - type: string
              - type: "null"
            title: Cell Id
        - name: tenant
          in: query
          required: false
          schema:
            anyOf:
              - type: string
              - type: "null"
            title: Tenant
        - name: claimed_by_session
          in: query
          required: false
          schema:
            anyOf:
              - type: string
              - type: "null"
            title: Claimed By Session
        - name: parent_session_id
          in: query
          required: false
          schema:
            anyOf:
              - type: string
              - type: "null"
            title: Parent Session Id
        - name: limit
          in: query
          required: false
          schema:
            anyOf:
              - type: integer
              - type: "null"
            title: Limit
        - name: offset
          in: query
          required: false
          schema:
            anyOf:
              - type: integer
              - type: "null"
            title: Offset
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
        "403":
          description: Tenant scope access denied
        "404":
          description: Resource not found or tenant mismatch
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /tasks/batch:
    post:
      summary: Create Tasks Batch
      description: Create multiple tasks atomically with title dedup.
      operationId: create_tasks_batch_tasks_batch_post
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/BatchCreateRequest"
        required: true
      responses:
        "201":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/BatchCreateResponse"
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
        "503":
          description: Server is draining
  /tasks/self-create:
    post:
      summary: Self Create Subtask
      description: |-
        Create a subtask linked to a parent task.

        Agents call this to decompose work during execution.  The parent
        task is automatically transitioned to ``WAITING_FOR_SUBTASKS`` on
        the first subtask creation (if it is not already in that state).
      operationId: self_create_subtask_tasks_self_create_post
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/TaskSelfCreate"
        required: true
      responses:
        "201":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TaskResponse"
        "404":
          description: Parent task not found
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /tasks/next/{role}:
    get:
      summary: Next Task
      description: |-
        Claim the next available task for *role*.

        Pass ``claimed_by_session`` as a query param to record which parent
        orchestrator session owns the claim.

        Pass ``parent_session_id`` to restrict claiming to tasks that were
        created under that coordinator session.  Workers belonging to a
        coordinator should always pass their coordinator's session ID here
        to avoid stealing tasks from other namespaces.
      operationId: next_task_tasks_next__role__get
      parameters:
        - name: role
          in: path
          required: true
          schema:
            type: string
            title: Role
        - name: claimed_by_session
          in: query
          required: false
          schema:
            anyOf:
              - type: string
              - type: "null"
            title: Claimed By Session
        - name: parent_session_id
          in: query
          required: false
          schema:
            anyOf:
              - type: string
              - type: "null"
            title: Parent Session Id
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TaskResponse"
        "403":
          description: Tenant scope access denied
        "404":
          description: Resource not found or tenant mismatch
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
        "503":
          description: Server is draining
  /tasks/claim-batch:
    post:
      summary: Claim Batch
      description: Atomically claim multiple tasks by ID for an agent.
      operationId: claim_batch_tasks_claim_batch_post
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/BatchClaimRequest"
        required: true
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/BatchClaimResponse"
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
        "503":
          description: Server is draining
  /tasks/{task_id}/claim:
    post:
      summary: Claim Task
      description: |-
        Claim a specific task by ID.

        Pass ``expected_version`` as a query param for optimistic locking
        (CAS). If the task's version doesn't match, returns 409 Conflict.

        Pass ``claimed_by_session`` to record which parent orchestrator
        session owns this claim.
      operationId: claim_task_tasks__task_id__claim_post
      parameters:
        - name: task_id
          in: path
          required: true
          schema:
            type: string
            title: Task Id
        - name: expected_version
          in: query
          required: false
          schema:
            anyOf:
              - type: integer
              - type: "null"
            title: Expected Version
        - name: claimed_by_session
          in: query
          required: false
          schema:
            anyOf:
              - type: string
              - type: "null"
            title: Claimed By Session
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TaskResponse"
        "404":
          description: Task not found
        "409":
          description: Version conflict or invalid state
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
        "503":
          description: Server is draining
  /tasks/{task_id}/complete:
    post:
      summary: Complete Task
      description: |-
        Mark a task as done (or refused) from a worker terminal payload.

        Structured payloads (``body.payload`` or a JSON object embedded in
        ``result_summary``) are validated against the worker completion
        contract (#2244): an invalid payload is a typed ``contract_violation``
        failure carrying the schema error path, and a validated refusal lands
        the task in the terminal REFUSED state instead of DONE. Legacy prose
        summaries are accepted unchanged.

        If ``result_summary`` is empty the task is auto-transitioned to
        ``FAILED`` with ``reason='completion missing summary'`` and
        a 422 is returned with the failed task payload so the client knows the
        slot was released.
      operationId: complete_task_tasks__task_id__complete_post
      parameters:
        - name: task_id
          in: path
          required: true
          schema:
            type: string
            title: Task Id
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/TaskCompleteRequest"
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TaskResponse"
        "404":
          description: Task not found
        "409":
          description: Invalid state transition
        "422":
          description: Empty result_summary or contract violation - task auto-failed
  /tasks/{task_id}/wait-for-subtasks:
    post:
      summary: Wait For Subtasks
      description: Mark a parent task as waiting until its generated subtasks complete.
      operationId: wait_for_subtasks_tasks__task_id__wait_for_subtasks_post
      parameters:
        - name: task_id
          in: path
          required: true
          schema:
            type: string
            title: Task Id
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/TaskWaitForSubtasksRequest"
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TaskResponse"
        "404":
          description: Task not found
        "409":
          description: Invalid state transition
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /tasks/{task_id}/fail:
    post:
      summary: Fail Task
      description: Mark a task as failed.
      operationId: fail_task_tasks__task_id__fail_post
      parameters:
        - name: task_id
          in: path
          required: true
          schema:
            type: string
            title: Task Id
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/TaskFailRequest"
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TaskResponse"
        "404":
          description: Task not found
        "409":
          description: Invalid state transition
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /tasks/{task_id}/release:
    post:
      summary: Release Task
      description: |-
        Release a claimed task back to the open pool without failing it.

        A cluster worker that claims a task but cannot start its agent (e.g. the
        workspace is not a usable git checkout, or the adapter spawn fails) must
        return the task to the pool so another node can pick it up, rather than
        stranding it in ``claimed`` with no live agent (#3018). Distinct from
        ``/fail`` (terminal FAILED) and ``/reopen`` (DONE -> OPEN): the task
        transitions CLAIMED/IN_PROGRESS -> OPEN and is immediately claimable again.
      operationId: release_task_tasks__task_id__release_post
      parameters:
        - name: task_id
          in: path
          required: true
          schema:
            type: string
            title: Task Id
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/TaskReleaseRequest"
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TaskResponse"
        "404":
          description: Task not found
        "409":
          description: Invalid state transition
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /tasks/{task_id}/reopen:
    post:
      summary: Reopen Task
      description: |-
        Reopen a done task that failed janitor verification (same task id).

        Transitions DONE -> OPEN and increments
        ``metadata['janitor_reopen_count']``. The orchestrator enforces the
        reopen budget; this endpoint only performs the state transition.
      operationId: reopen_task_tasks__task_id__reopen_post
      parameters:
        - name: task_id
          in: path
          required: true
          schema:
            type: string
            title: Task Id
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/TaskReopenRequest"
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TaskResponse"
        "404":
          description: Task not found
        "409":
          description: Invalid state transition
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /tasks/{task_id}/close:
    post:
      summary: Close Task
      description: Mark a verified task as closed (terminal success state).
      operationId: close_task_tasks__task_id__close_post
      parameters:
        - name: task_id
          in: path
          required: true
          schema:
            type: string
            title: Task Id
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TaskResponse"
        "404":
          description: Task not found
        "409":
          description: Invalid state transition
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /tasks/{task_id}/cancel:
    post:
      summary: Cancel Task
      description: |-
        Cancel a task and cascade to all of its descendant subtasks.

        Walks the subtask tree (``parent_task_id`` references) via
        ``TaskStore.cancel_cascade`` so that children are not left running
        after the parent is aborted.  Returns the root task.
      operationId: cancel_task_tasks__task_id__cancel_post
      parameters:
        - name: task_id
          in: path
          required: true
          schema:
            type: string
            title: Task Id
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/TaskCancelRequest"
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TaskResponse"
        "404":
          description: Task not found
        "409":
          description: Invalid state transition
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /tasks/{task_id}/block:
    post:
      summary: Block Task
      description: Mark a task as blocked -- requires human intervention to unblock.
      operationId: block_task_tasks__task_id__block_post
      parameters:
        - name: task_id
          in: path
          required: true
          schema:
            type: string
            title: Task Id
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/TaskBlockRequest"
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TaskResponse"
        "404":
          description: Task not found
        "409":
          description: Invalid state transition
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /tasks/{task_id}/progress:
    post:
      summary: Progress Task
      description: |-
        Append an intermediate progress update to a task.

        Also stores a progress snapshot for stall detection when snapshot
        fields (files_changed, tests_passing, errors) are provided.
      operationId: progress_task_tasks__task_id__progress_post
      parameters:
        - name: task_id
          in: path
          required: true
          schema:
            type: string
            title: Task Id
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/TaskProgressRequest"
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TaskResponse"
        "404":
          description: Task not found
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
    get:
      summary: Get Task Progress
      description: |-
        Return the chain-computed progress vector for a task.

        The ledger read is resolved from the task's own authoritative run id, never
        from a client-supplied parameter, so the vector cannot be steered by pairing
        this task's journal with an arbitrary run's ledger.
      operationId: get_task_progress_tasks__task_id__progress_get
      parameters:
        - name: task_id
          in: path
          required: true
          schema:
            type: string
            title: Task Id
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TaskProgressResponse"
        "404":
          description: Task not found
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /tasks/{task_id}/partial-merge:
    post:
      summary: Partial Merge Task
      description: |-
        Incrementally merge specific committed files from the agent's branch into main.

        Allows a long-running agent to push a completed subset of its work (e.g.
        the first 5 of 10 test files) while still writing the rest.  Reduces
        wall-clock time by making partial results available downstream earlier.

        Only files that are already **committed** in the agent's worktree branch
        (``agent/<session_id>``) are merged.  Uncommitted files are returned in
        ``uncommitted_files`` so the caller knows to commit them in the worktree
        first.  Files that were already merged by a prior call are skipped and
        returned in ``skipped_already_merged``.

        Requires the task to be ``in_progress`` with a ``claimed_by_session`` set.
      operationId: partial_merge_task_tasks__task_id__partial_merge_post
      parameters:
        - name: task_id
          in: path
          required: true
          schema:
            type: string
            title: Task Id
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PartialMergeRequest"
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PartialMergeResponse"
        "404":
          description: Task not found
        "409":
          description: Task not in progress or has no active session
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
    get:
      summary: Get Partial Merge State
      description: |-
        Return the cumulative incremental-merge state for a task's active session.

        Useful for monitoring how much of an in-progress task's output has already
        been merged into the main branch.
      operationId: get_partial_merge_state_tasks__task_id__partial_merge_get
      parameters:
        - name: task_id
          in: path
          required: true
          schema:
            type: string
            title: Task Id
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PartialMergeResponse"
        "404":
          description: Task not found
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /tasks/{task_id}/snapshots:
    get:
      summary: Get Task Snapshots
      description: Return stored progress snapshots for a task (oldest-first, up to 10).
      operationId: get_task_snapshots_tasks__task_id__snapshots_get
      parameters:
        - name: task_id
          in: path
          required: true
          schema:
            type: string
            title: Task Id
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/SnapshotEntry"
                title: Response Get Task Snapshots Tasks  Task Id  Snapshots Get
        "404":
          description: Task not found
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /tasks/counts:
    get:
      summary: Task Counts
      description: |-
        Return task counts per status without serialising task bodies.

        This is the lightweight alternative to GET /tasks for orchestrator
        tick summaries and dashboard polling.
      operationId: task_counts_tasks_counts_get
      parameters:
        - name: tenant
          in: query
          required: false
          schema:
            anyOf:
              - type: string
              - type: "null"
            title: Tenant
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TaskCountsResponse"
        "403":
          description: Tenant scope access denied
        "404":
          description: Resource not found or tenant mismatch
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /tasks/archive:
    get:
      summary: Get Archive
      description: Return the last N archived (done/failed) task records.
      operationId: get_archive_tasks_archive_get
      parameters:
        - name: limit
          in: query
          required: false
          schema:
            type: integer
            default: 50
            title: Limit
        - name: tenant
          in: query
          required: false
          schema:
            anyOf:
              - type: string
              - type: "null"
            title: Tenant
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/ArchiveRecord"
                title: Response Get Archive Tasks Archive Get
        "403":
          description: Tenant scope access denied
        "404":
          description: Resource not found or tenant mismatch
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /tasks/graph:
    get:
      summary: Get Task Graph
      description: |-
        Return the task dependency graph as JSON (nodes + edges + critical path).

        Builds a DAG from all current tasks and returns:
        - ``nodes``: list of {id, role, status, estimated_minutes, title}
        - ``edges``: list of {from, to, type, semantic_type}
        - ``critical_path``: ordered list of task IDs on the longest chain
        - ``critical_path_minutes``: total estimated minutes on the critical path
        - ``parallel_width``: max tasks that can run concurrently
        - ``bottlenecks``: task IDs that block the most downstream work
      operationId: get_task_graph_tasks_graph_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
        "403":
          description: Tenant scope access denied
        "404":
          description: Resource not found or tenant mismatch
  /tasks/{task_id}:
    get:
      summary: Get Task
      description: Get a single task by ID.
      operationId: get_task_tasks__task_id__get
      parameters:
        - name: task_id
          in: path
          required: true
          schema:
            type: string
            title: Task Id
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TaskResponse"
        "404":
          description: Task not found
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
    patch:
      summary: Patch Task
      description: |-
        Update mutable task fields (role, priority, model) - manager corrections.

        Used by the manager agent or dashboard to correct mis-assigned tasks,
        adjust priority, or change model without interrupting the orchestrator.
      operationId: patch_task_tasks__task_id__patch
      parameters:
        - name: task_id
          in: path
          required: true
          schema:
            type: string
            title: Task Id
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/TaskPatchRequest"
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TaskResponse"
        "404":
          description: Task not found
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /tasks/{task_id}/graph-neighbors:
    get:
      summary: Get Task Graph Neighbors
      description: |-
        Return immediate dependency neighbours for a single task.

        Powers the dashboard Deps tab: upstream tasks the requested one waits
        on (its ``depends_on`` list) and downstream tasks that declare it as a
        dependency.  Depth is intentionally fixed at 1 - the panel renders two
        flat lists, not a transitive graph.
      operationId: get_task_graph_neighbors_tasks__task_id__graph_neighbors_get
      parameters:
        - name: task_id
          in: path
          required: true
          schema:
            type: string
            title: Task Id
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
                title: Response Get Task Graph Neighbors Tasks  Task Id  Graph Neighbors Get
        "404":
          description: Task not found
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /tasks/{task_id}/gates:
    get:
      summary: Get Task Gates
      description: Return the persisted quality-gate report for a task.
      operationId: get_task_gates_tasks__task_id__gates_get
      parameters:
        - name: task_id
          in: path
          required: true
          schema:
            type: string
            title: Task Id
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
        "404":
          description: Task or gate report not found
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
        "500":
          description: Gate report unreadable
  /tasks/{task_id}/prioritize:
    post:
      summary: Prioritize Task
      description: Bump a task to priority 0 so the orchestrator picks it up next.
      operationId: prioritize_task_tasks__task_id__prioritize_post
      parameters:
        - name: task_id
          in: path
          required: true
          schema:
            type: string
            title: Task Id
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TaskResponse"
        "404":
          description: Task not found
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /tasks/{task_id}/force-claim:
    post:
      summary: Force Claim Task
      description: |-
        Force a task back to open with priority 0 for immediate pickup.

        Resets claimed/in_progress tasks back to open so the orchestrator's
        next tick will spawn a fresh agent for them.  Terminal tasks
        (done/failed/cancelled) are rejected with 409.
      operationId: force_claim_task_tasks__task_id__force_claim_post
      parameters:
        - name: task_id
          in: path
          required: true
          schema:
            type: string
            title: Task Id
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TaskResponse"
        "404":
          description: Task not found
        "409":
          description: Cannot force-claim terminal task
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /agents/{agent_id}/heartbeat:
    post:
      summary: Agent Heartbeat
      description: Register an agent heartbeat.
      operationId: agent_heartbeat_agents__agent_id__heartbeat_post
      parameters:
        - name: agent_id
          in: path
          required: true
          schema:
            type: string
            title: Agent Id
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/HeartbeatRequest"
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HeartbeatResponse"
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /bulletin:
    post:
      summary: Post Bulletin
      description: |-
        Append a message to the bulletin board.

        Returns 201 when the message is stored and any registered signal action
        ran. When a signal action hook fails (for example a ``blocker`` whose
        clearance gate did not materialize), the message is still on the
        append-only board and queued in the board's retry outbox, but the action is
        not complete: the response is 202 rather than 201 so the caller can tell
        "stored and acted on" from "stored, action pending retry" (#2648).
      operationId: post_bulletin_bulletin_post
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/BulletinPostRequest"
      responses:
        "201":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/BulletinMessageResponse"
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
    get:
      summary: Get Bulletin
      description: Get bulletin messages since a given timestamp.
      operationId: get_bulletin_bulletin_get
      parameters:
        - name: since
          in: query
          required: false
          schema:
            type: number
            default: 0
            title: Since
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/BulletinMessageResponse"
                title: Response Get Bulletin Bulletin Get
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /channel/query:
    post:
      summary: Post Channel Query
      description: Post a coordination query targeted at an agent or role.
      operationId: post_channel_query_channel_query_post
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ChannelQueryRequest"
        required: true
      responses:
        "201":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ChannelQueryResponse"
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /channel/{query_id}/respond:
    post:
      summary: Post Channel Response
      description: Respond to a channel query.
      operationId: post_channel_response_channel__query_id__respond_post
      parameters:
        - name: query_id
          in: path
          required: true
          schema:
            type: string
            title: Query Id
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ChannelResponseRequest"
      responses:
        "201":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ChannelResponseResponse"
        "404":
          description: Query not found
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /channel/queries:
    get:
      summary: Get Channel Queries
      description: Get pending queries, optionally filtered by agent_id or role.
      operationId: get_channel_queries_channel_queries_get
      parameters:
        - name: agent_id
          in: query
          required: false
          schema:
            anyOf:
              - type: string
              - type: "null"
            title: Agent Id
        - name: role
          in: query
          required: false
          schema:
            anyOf:
              - type: string
              - type: "null"
            title: Role
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/ChannelQueryResponse"
                title: Response Get Channel Queries Channel Queries Get
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /channel/{query_id}/responses:
    get:
      summary: Get Channel Responses
      description: Get all responses for a channel query.
      operationId: get_channel_responses_channel__query_id__responses_get
      parameters:
        - name: query_id
          in: path
          required: true
          schema:
            type: string
            title: Query Id
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/ChannelResponseResponse"
                title: Response Get Channel Responses Channel  Query Id  Responses Get
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /tasks/claim-receipt:
    post:
      summary: Claim Receipt
      description: |-
        Claim the next eligible backlog row and return a signed claim receipt.

        The dependency gate is enforced by :class:`ClaimFilter`: a row is offered
        only when its ``depends_on`` are all in ``completed_ids``. The granted
        claim is mirrored into the audit chain via the existing
        ``record_task_claim_receipt`` (no new event type), and the returned
        receipt embeds that event's chain head so the claim verifies offline. A
        filter matching no eligible row returns a signed refusal receipt.
      operationId: claim_receipt_tasks_claim_receipt_post
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ClaimReceiptRequest"
        required: true
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
                title: Response Claim Receipt Tasks Claim Receipt Post
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
        "503":
          description: Server is draining -- no new claims accepted
  /tasks/{task_id}/messages:
    post:
      summary: Post Task Message
      description: |-
        Append one typed message to the recipient task's mailbox.

        The message is DLP-redacted, HMAC-chained onto the mailbox journal,
        Ed25519-signed, and mirrored into the audit chain before the response
        is returned - the response IS the signed journal entry.
      operationId: post_task_message_tasks__task_id__messages_post
      parameters:
        - name: task_id
          in: path
          required: true
          schema:
            type: string
            title: Task Id
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/TaskMessagePost"
      responses:
        "201":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TaskMessageResponse"
        "404":
          description: Task not found
        "422":
          description: Unknown message kind or body over the byte cap
        "429":
          description: Recipient task mailbox is full
    get:
      summary: Get Task Messages
      description: |-
        Deliver pending messages for a task, in chain append order.

        ``since_seq`` is a deterministic cursor: pass the highest ``seq``
        already processed to receive only newer messages. Replaying the same
        journal always reproduces the same delivery order.
      operationId: get_task_messages_tasks__task_id__messages_get
      parameters:
        - name: task_id
          in: path
          required: true
          schema:
            type: string
            title: Task Id
        - name: since_seq
          in: query
          required: false
          schema:
            type: integer
            default: -1
            title: Since Seq
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/TaskMessageResponse"
                title: Response Get Task Messages Tasks  Task Id  Messages Get
        "404":
          description: Task not found
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /tasks/{task_id}/artifacts:
    post:
      summary: Post Task Artifact
      description: Post one journal-anchored artifact against a task the caller holds.
      operationId: post_task_artifact_tasks__task_id__artifacts_post
      parameters:
        - name: task_id
          in: path
          required: true
          schema:
            type: string
            title: Task Id
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/TaskArtifactPost"
      responses:
        "201":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TaskArtifactContentResponse"
        "403":
          description: Caller does not hold the task's claim
        "404":
          description: Task not found
        "413":
          description: Artifact payload exceeds the per-blob cap
        "422":
          description: Invalid artifact payload
    get:
      summary: List Task Artifacts
      description: List every posted artifact version with its verification state.
      operationId: list_task_artifacts_tasks__task_id__artifacts_get
      parameters:
        - name: task_id
          in: path
          required: true
          schema:
            type: string
            title: Task Id
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/TaskArtifactContentResponse"
                title: Response List Task Artifacts Tasks  Task Id  Artifacts Get
        "404":
          description: Task not found
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /tasks/{task_id}/steer:
    post:
      summary: Post Task Steer
      description: |-
        Record a steering receipt for a running worker and apply its effect.

        The receipt is bound into the audit chain before the effect executes; the
        ``steer.*`` mailbox message and any process signal reference the receipt
        hash returned here. An effect can never precede its receipt.
      operationId: post_task_steer_tasks__task_id__steer_post
      parameters:
        - name: task_id
          in: path
          required: true
          schema:
            type: string
            title: Task Id
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/TaskSteerPost"
      responses:
        "201":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TaskSteerResponse"
        "403":
          description: Scope is not authorised to steer
        "404":
          description: Task not found
        "409":
          description: Confirmed payload differs from the executed command
        "422":
          description: Malformed steering command
        "503":
          description: Task mailbox is not configured
  /cluster/nodes:
    post:
      summary: Register Node
      description: Register a node, or update the entry of one that is re-registering.
      operationId: register_node_cluster_nodes_post
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/NodeRegisterRequest"
      responses:
        "201":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/NodeResponse"
        "401":
          description: Cluster authentication failed
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
    get:
      summary: List Nodes
      description: List all cluster nodes, optionally filtered by status.
      operationId: list_nodes_cluster_nodes_get
      parameters:
        - name: status
          in: query
          required: false
          schema:
            anyOf:
              - type: string
              - type: "null"
            title: Status
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/NodeResponse"
                title: Response List Nodes Cluster Nodes Get
        "400":
          description: Invalid node status
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /cluster/nodes/{node_id}/heartbeat:
    post:
      summary: Node Heartbeat
      description: Record a heartbeat from a cluster node.
      operationId: node_heartbeat_cluster_nodes__node_id__heartbeat_post
      parameters:
        - name: node_id
          in: path
          required: true
          schema:
            type: string
            title: Node Id
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/NodeHeartbeatRequest"
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/NodeResponse"
        "401":
          description: Cluster authentication failed
        "404":
          description: Node not registered
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /cluster/nodes/{node_id}:
    delete:
      summary: Unregister Node
      description: Remove a node from the cluster.
      operationId: unregister_node_cluster_nodes__node_id__delete
      parameters:
        - name: node_id
          in: path
          required: true
          schema:
            type: string
            title: Node Id
      responses:
        "204":
          description: Successful Response
        "401":
          description: Cluster authentication failed
        "404":
          description: Node not found
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /cluster/nodes/{node_id}/cordon:
    post:
      summary: Cordon Node
      description: Cordon a node -- exclude from scheduling.
      operationId: cordon_node_cluster_nodes__node_id__cordon_post
      parameters:
        - name: node_id
          in: path
          required: true
          schema:
            type: string
            title: Node Id
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                type: object
                additionalProperties:
                  type: string
                title: Response Cordon Node Cluster Nodes  Node Id  Cordon Post
        "401":
          description: Cluster authentication failed
        "404":
          description: Node not found
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /cluster/nodes/{node_id}/uncordon:
    post:
      summary: Uncordon Node
      description: Uncordon a node -- resume accepting tasks.
      operationId: uncordon_node_cluster_nodes__node_id__uncordon_post
      parameters:
        - name: node_id
          in: path
          required: true
          schema:
            type: string
            title: Node Id
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                type: object
                additionalProperties:
                  type: string
                title: Response Uncordon Node Cluster Nodes  Node Id  Uncordon Post
        "401":
          description: Cluster authentication failed
        "404":
          description: Node not found
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /cluster/nodes/{node_id}/drain:
    post:
      summary: Drain Node
      description: Start draining a node -- cordon + signal agents to finish.
      operationId: drain_node_cluster_nodes__node_id__drain_post
      parameters:
        - name: node_id
          in: path
          required: true
          schema:
            type: string
            title: Node Id
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                type: object
                additionalProperties:
                  type: string
                title: Response Drain Node Cluster Nodes  Node Id  Drain Post
        "401":
          description: Cluster authentication failed
        "404":
          description: Node not found
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /cluster/status:
    get:
      summary: Cluster Status
      description: Get cluster status summary.
      operationId: cluster_status_cluster_status_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ClusterStatusResponse"
  /cluster/claims/gossip:
    post:
      summary: Gossip Claims
      description: |-
        Fold peer claim receipts into this node's signed journal (#2558).

        The leaderless counterpart to ``POST /cluster/steal``: no node decides who
        gets what here. Each receipt is folded only after its Ed25519 signature and
        its chain link both verify, so an unverifiable receipt is never written.

        A receipt that does not extend the local head is *not* merged. It produces
        a signed ``fork`` receipt carrying the divergence entry index, which the
        response surfaces through ``forked``. Silent merge would be the one failure
        mode a leaderless design cannot recover from: two partitions would each
        hold a coherent-looking journal describing incompatible work.

        Authorisation reuses the node-heartbeat scope: gossip is a peer-to-peer
        fleet-membership operation, not an administrative one.
      operationId: gossip_claims_cluster_claims_gossip_post
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ClaimGossipRequest"
        required: true
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ClaimGossipResponse"
        "401":
          description: Cluster authentication failed
        "409":
          description: Node is not running the MESH topology
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /cluster/steal:
    post:
      summary: Steal Tasks
      description: |-
        Evaluate task stealing policy and reassign claimed tasks between nodes.

        Workers report their queue depths; the server runs the steal policy and
        returns a list of task reassignments.  Stolen tasks are reset to ``open``
        so the receiver node can claim them.

        Authorisation requires the node-admin scope, like the other node-registry
        mutations (cordon, uncordon, drain, unregister) this sits beside in the
        operational-primitives table.  It is deliberately NOT the heartbeat scope
        that ``POST /cluster/claims/gossip`` uses: gossip proves each receipt with
        its own Ed25519 signature and chain link inside the handler, so its bearer
        scope only has to establish fleet membership, whereas here the caller's
        reported queue depths drive ``force_claim`` directly with no further proof
        to check.
      operationId: steal_tasks_cluster_steal_post
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/TaskStealRequest"
        required: true
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TaskStealResponse"
        "401":
          description: Cluster authentication failed
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /a2a/agent-card:
    get:
      summary: Agent Card
      description: |-
        Publish the Bernstein orchestrator Agent Card (legacy A2A path).

        The richer service manifest at ``/.well-known/agent.json`` is served by
        ``routes.well_known``; this endpoint is preserved for callers that
        historically pulled the orchestrator's own A2A card.
      operationId: agent_card_a2a_agent_card_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/A2AAgentCardResponse"
  /a2a/agents:
    get:
      summary: List A2A Agents
      description: Return Bernstein's A2A agent card via the task API namespace.
      operationId: list_a2a_agents_a2a_agents_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/A2AAgentCardResponse"
  /a2a/message:
    post:
      summary: A2A Message
      description: Receive an inbound A2A message and inject it into the target task context.
      operationId: a2a_message_a2a_message_post
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/A2AMessageRequest"
        required: true
      responses:
        "201":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/A2AMessageResponse"
        "404":
          description: Task not found
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /a2a/tasks/send:
    post:
      summary: A2A Send Task
      description: |-
        Receive a task from an external A2A agent.

        Creates both an A2A task record and a corresponding Bernstein task,
        linking them together for lifecycle synchronisation.
      operationId: a2a_send_task_a2a_tasks_send_post
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/A2ATaskSendRequest"
        required: true
      responses:
        "201":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/A2ATaskResponse"
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /a2a/tasks/{a2a_task_id}:
    get:
      summary: A2A Get Task
      description: Get an A2A task by ID, syncing status from the Bernstein task.
      operationId: a2a_get_task_a2a_tasks__a2a_task_id__get
      parameters:
        - name: a2a_task_id
          in: path
          required: true
          schema:
            type: string
            title: A2A Task Id
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/A2ATaskResponse"
        "404":
          description: A2A task not found
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /a2a/tasks/{a2a_task_id}/artifacts:
    post:
      summary: A2A Add Artifact
      description: Attach an artifact to an A2A task.
      operationId: a2a_add_artifact_a2a_tasks__a2a_task_id__artifacts_post
      parameters:
        - name: a2a_task_id
          in: path
          required: true
          schema:
            type: string
            title: A2A Task Id
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/A2AArtifactRequest"
      responses:
        "201":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/A2AArtifactResponse"
        "404":
          description: A2A task not found
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /a2a/v0/tasks:
    post:
      summary: A2A V0 Accept Task
      description: |-
        Accept a federated task delegated from a peer orchestrator.

        Wire format::

            {
              "sender": { ...AgentCard... },
              "task":   { "id": "...", "message": "...", "role": "..." }
            }

        Returns 202 with the local federated-task id and the remote task id
        that was offered. Validation errors return HTTP 409 so that the
        caller's retry policy treats them as terminal (the peer is reachable
        and authoritative, no point retrying with the same body).
      operationId: a2a_v0_accept_task_a2a_v0_tasks_post
      responses:
        "202":
          description: Successful Response
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
                title: Response A2A V0 Accept Task A2A V0 Tasks Post
        "400":
          description: Invalid sender Agent Card or task body
        "409":
          description: Task rejected (validation, capacity, etc.)
  /status:
    get:
      summary: Status Dashboard
      description: Dashboard summary of task counts.
      operationId: status_dashboard_status_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /status/duration-predictions:
    get:
      summary: Duration Predictions
      description: |-
        Return ML-predicted duration estimates for all open/claimed tasks.

        Uses the local GradientBoosting duration predictor.  Falls back to the
        static cold-start table when fewer than 50 completions are available.

        Response shape::

            {
              "predictor": {
                "trained": true,
                "training_samples": 142,
                "cold_start": false
              },
              "tasks": [
                {
                  "task_id": "abc123",
                  "title": "Refactor auth module",
                  "role": "backend",
                  "p50_seconds": 720.0,
                  "p90_seconds": 1440.0,
                  "confidence": 0.62,
                  "is_cold_start": false,
                  "eta_p50": "12m 0s",
                  "eta_p90": "24m 0s"
                }
              ]
            }
      operationId: duration_predictions_status_duration_predictions_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /routing/bandit:
    get:
      summary: Bandit Routing Stats
      description: |-
        Return contextual bandit routing statistics.

        Reads persisted state from ``.sdd/routing/``.  Returns an empty dict
        when bandit routing has not been activated (``--routing bandit`` not passed).
      operationId: bandit_routing_stats_routing_bandit_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /dashboard/data:
    get:
      summary: Dashboard Data
      description: |-
        Return all mission control dashboard data as JSON.

        Includes stats, tasks with timeline data, agent details with costs,
        file ownership map, cost history, and alerts.
      operationId: dashboard_data_dashboard_data_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /events:
    get:
      summary: Sse Events
      description: |-
        Server-Sent Events stream for real-time dashboard updates.

        Includes disconnect detection via heartbeat pings and connection
        timeout handling to prevent leaked subscriber queues.
      operationId: sse_events_events_get
      responses:
        "200":
          description: Server-Sent Events stream. The response body does not terminate.
          content:
            text/event-stream:
              schema:
                type: string
  /badge.json:
    get:
      summary: Get Badge
      description: |-
        Return dynamic badge data for GitHub shields.io integration.

        Shows tasks completed, total cost, and quality score.
        Usage: https://img.shields.io/endpoint?url=<server>/badge.json
      operationId: get_badge_badge_json_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /memory/audit:
    get:
      summary: Memory Audit
      description: |-
        Audit the lesson memory provenance chain (OWASP ASI06 2026).

        Returns chain integrity status and a per-entry provenance trail.
        Detects tampering, insertion, deletion, and reordering attacks.
      operationId: memory_audit_memory_audit_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /broadcast:
    post:
      summary: Broadcast Command
      description: |-
        Send a message to all running agents via fastest available channel.

        Uses stdin pipe where available (sub-second delivery), falls back
        to file-based COMMAND signal for agents without pipe support.

        Expects JSON body: ``{"message": "some instruction"}``.
      operationId: broadcast_command_broadcast_post
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/BroadcastRequest"
        required: true
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /health:
    get:
      summary: Health Check
      description: Liveness check with component-level status.
      operationId: health_check_health_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HealthResponse"
  /health/ready:
    get:
      summary: Ready Check
      description: Readiness check for load balancers.
      operationId: ready_check_health_ready_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /ready:
    get:
      summary: Ready Alias
      description: Alias for /health/ready.
      operationId: ready_alias_ready_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /health/live:
    get:
      summary: Live Check
      description: Liveness check for process monitoring.
      operationId: live_check_health_live_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /alive:
    get:
      summary: Live Alias
      description: Alias for /health/live.
      operationId: live_alias_alive_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /config:
    post:
      summary: Update Config
      description: |-
        Update mutable config fields at runtime.

        Accepts JSON body with ``{"max_agents": N}``.  Writes the change to
        ``bernstein.yaml`` so the orchestrator's hot-reload picks it up on
        the next tick (~30s).  Returns the new effective value.

        Agent identity JWTs (per-agent, task-scoped) are rejected with 403 -
        mutating process-wide config is an operator action.  SSO admin users
        and legacy operator tokens may proceed.  Bearer-level permission
        enforcement is handled by :class:`SSOAuthMiddleware` via the
        ``admin:manage`` mapping; this check adds defense-in-depth against any
        agent JWT that slips through the middleware's prefix match.
      operationId: update_config_config_post
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /shutdown:
    post:
      summary: Shutdown Server
      description: |-
        Initiate graceful server shutdown.

        Accepts an optional JSON body ``{"reason": "..."}``.  Schedules a
        SIGTERM to the current process shortly after the response is sent so
        that the Uvicorn server exits cleanly.
      operationId: shutdown_server_shutdown_post
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /cache-stats:
    get:
      summary: Cache Stats
      description: |-
        Return prompt caching statistics from the manifest.

        Reads `.sdd/caching/manifest.jsonl` and returns aggregated counts,
        estimated token savings, and estimated USD savings based on the
        Anthropic cached-input discount (90% off standard input price).

        Returns 200 with empty statistics if no cache manifest exists yet.
      operationId: cache_stats_cache_stats_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /metrics:
    get:
      summary: Metrics Endpoint
      description: |-
        Prometheus metrics scrape endpoint.

        Updates all gauges from the current task store state, then
        returns the full metric exposition in Prometheus text format.
      operationId: metrics_endpoint_metrics_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /workspace:
    get:
      tags:
        - workspace
      summary: Workspace Status
      description: Return repository status for the configured workspace.
      operationId: workspace_status_workspace_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WorkspaceResponse"
        "400":
          description: Invalid seed file
  /workspace/merge-order:
    post:
      tags:
        - workspace
      summary: Workspace Merge Order
      description: Return the repo merge order derived from current cross-repo task dependencies.
      operationId: workspace_merge_order_workspace_merge_order_post
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MergeOrderResponse"
        "400":
          description: Invalid seed file
        "404":
          description: No workspace configured
  /alerts:
    get:
      summary: Get Alerts
      description: |-
        Return current dashboard alerts as JSON.

        Builds alerts from the live task/agent state - failed tasks, blocked
        tasks, stale agents, and budget thresholds.  Intended for dashboard
        polling or external monitoring.

        Returns a JSON object with keys:
        - ``alerts``: list of alert dicts (``level``, ``message``, ``detail``)
        - ``count``: total number of alerts
        - ``ts``: server timestamp (Unix seconds)
      operationId: get_alerts_alerts_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /webhook:
    post:
      summary: Generic Webhook
      description: |-
        Create a task directly from a generic inbound webhook payload.

        The endpoint is intentionally small and separate from the trigger-manager
        flow: callers POST a task-shaped payload and Bernstein creates one task.
        ``BERNSTEIN_WEBHOOK_SECRET`` must be configured (fail-closed; )
        and each request must carry a fresh ``X-Bernstein-Timestamp`` header
        plus a matching ``X-Bernstein-Webhook-Signature-256`` HMAC over
        ``f"{timestamp}.".encode() + body``. The plaintext
        ``X-Bernstein-Webhook-Secret`` fallback has been removed; callers
        relying on it must upgrade to the HMAC + timestamp flow.

        Automation bridge (#2512): an admitted trigger returns a signed,
        chain-anchored trigger receipt in ``receipt`` so the calling platform holds
        a proof of what it asked for rather than a bare task reference. A trigger
        that fails authentication, or that replays a trigger id already admitted,
        is refused with its own signed refusal receipt (HTTP 401 and 409
        respectively) -- the negative path leaves a record, never a silent drop.
      operationId: generic_webhook_webhook_post
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/WebhookTaskCreate"
        required: true
      responses:
        "201":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WebhookTaskResponse"
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /webhooks/github:
    post:
      summary: Github Webhook
      description: |-
        Receive a GitHub App webhook, verify signature, and create tasks.

        Handles the following event types:
        - ``issues`` (opened / labeled)
        - ``pull_request_review_comment`` / ``issue_comment``
        - ``push``
        - ``workflow_run`` (completed + failure) - creates a ci-fix task, capped at
          ``MAX_CI_RETRIES`` active attempts per branch.

        Reads ``GITHUB_WEBHOOK_SECRET`` from environment for HMAC verification.
        Fail-closed: when the secret is not configured the
        endpoint is disabled and returns 503; unsigned GitHub webhooks are
        never accepted.
        Replay protection: if the caller includes an
        ``X-Bernstein-Timestamp`` header the request is additionally
        checked for freshness - drift greater than five minutes returns
        401.  Real GitHub deliveries omit this header and continue to
        work; the check is there so bernstein-internal relays cannot be
        replayed after capture.
        Returns 200 on success, 401 on bad/missing signature or stale
        timestamp, 400 on parse error, 503 when the endpoint is not
        configured.
      operationId: github_webhook_webhooks_github_post
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /webhooks/gitlab:
    post:
      summary: Gitlab Webhook
      description: |-
        Receive a GitLab CI webhook, verify token, and create ci-fix tasks.

        Handles the following event types:
        - ``pipeline`` (failed) - creates a ci-fix task, capped at
          ``MAX_CI_RETRIES`` active attempts per branch.
        - ``job`` (failed) - creates a ci-fix task for the specific job.

        Reads ``GITLAB_WEBHOOK_TOKEN`` from environment. GitLab sends a simple
        plaintext token in the ``x-gitlab-token`` header.
        Returns 200 on success, 401 on bad/missing token.
      operationId: gitlab_webhook_webhooks_gitlab_post
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /webhooks/telemetry/sentry/:
    post:
      summary: Telemetry Sentry
      description: Receive a Sentry-protocol issue-alert webhook.
      operationId: telemetry_sentry_webhooks_telemetry_sentry__post
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /webhooks/telemetry/gha_failure/:
    post:
      summary: Telemetry Gha Failure
      description: Receive a GitHub Actions ``workflow_run`` failure webhook.
      operationId: telemetry_gha_failure_webhooks_telemetry_gha_failure__post
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /webhooks/telemetry/datadog/:
    post:
      summary: Telemetry Datadog
      description: Receive a Datadog Logs webhook (stubbed in MVP).
      operationId: telemetry_datadog_webhooks_telemetry_datadog__post
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /webhooks/telemetry/loki/:
    post:
      summary: Telemetry Loki
      description: Receive a Loki / Alertmanager webhook (stubbed in MVP).
      operationId: telemetry_loki_webhooks_telemetry_loki__post
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /webhooks/telemetry/custom_jsonl/:
    post:
      summary: Telemetry Custom Jsonl
      description: Receive a custom JSONL tail webhook (stubbed in MVP).
      operationId: telemetry_custom_jsonl_webhooks_telemetry_custom_jsonl__post
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /webhooks/trackers/{adapter}:
    post:
      summary: Tracker Webhook
      description: |-
        Receive a tracker webhook, verify, dedupe, and enqueue.

        Path parameter:
            adapter: Short adapter name registered via
                :func:`bernstein.core.trackers.webhook_receiver.register_handler`.

        The endpoint accepts any JSON object.  All verification and replay
        decisions are made before the body is enqueued.  When verification
        succeeds and the delivery is fresh the parsed
        :class:`~bernstein.core.trackers.webhook_receiver.TrackerEvent` is
        stashed on ``app.state.tracker_event_queue`` if present so the
        orchestrator's normal task ingestion can drain it; if no queue is
        wired we simply log the event.  Either way the tracker receives a
        200 so it does not retry.
      operationId: tracker_webhook_webhooks_trackers__adapter__post
      parameters:
        - name: adapter
          in: path
          required: true
          schema:
            type: string
            title: Adapter
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /webhooks/discord/interactions:
    post:
      summary: Discord Interactions
      description: |-
        Receive and route Discord Application Command interactions.

        Verifies the Ed25519 signature, handles PING handshakes, and dispatches
        slash commands to the appropriate handler. Returns an immediate response
        (Discord requires a reply within 3 seconds).

        Returns:
            200 with a Discord interaction response object on success.
            401 if the signature is invalid.
            400 if the payload cannot be parsed.
      operationId: discord_interactions_webhooks_discord_interactions_post
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /webhooks/slack/commands:
    post:
      summary: Slack Slash Command
      description: |-
        Receive a Slack slash command, verify signature, and ack immediately.

        Slack requires a response within 3 seconds.  This endpoint verifies the
        request signature, parses the URL-encoded form payload, and returns an
        immediate acknowledgement.  Any long-running work (task creation, etc.)
        should be dispatched asynchronously using ``response_url``.

        Reads ``SLACK_SIGNING_SECRET`` from environment for HMAC verification.
        The secret MUST be configured: when it is not, the endpoint is
        disabled and returns ``UNCONFIGURED_STATUS``; only signed Slack
        requests are accepted.
        Returns 200 on success, 401 on bad/missing signature, 400 on parse
        error, ``UNCONFIGURED_STATUS`` when the endpoint is not configured.

        Slash command form fields parsed:
            - ``command``      - the slash command (e.g. ``/bernstein``)
            - ``text``         - text following the command
            - ``user_id``      - Slack user ID
            - ``channel_id``   - Slack channel ID
            - ``response_url`` - URL for delayed responses (up to 30 min)
            - ``trigger_id``   - trigger ID for opening modals
      operationId: slack_slash_command_webhooks_slack_commands_post
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /webhooks/slack/events:
    post:
      summary: Slack Events
      description: |-
        Receive Slack Events API callbacks.

        Handles:
        - ``url_verification``: returns the challenge value for endpoint verification.
        - ``event_callback`` with ``message`` type: creates a task when the bot is
          mentioned.  Bot messages and ``message_changed`` subtypes are ignored to
          prevent loops.

        Reads ``SLACK_SIGNING_SECRET`` from environment for HMAC verification.
        The secret MUST be configured: when it is not, the endpoint is
        disabled and returns ``UNCONFIGURED_STATUS``; only signed Slack
        requests are accepted.  Note that the ``url_verification`` handshake
        is signed by Slack too, so registering the endpoint works normally.
        Payload shape is validated before it is read: the body and, when
        present, its ``event`` member must both be JSON objects.
        Returns 200 on success, 401 on bad/missing signature, 400 on parse
        error or malformed payload shape, ``UNCONFIGURED_STATUS`` when the
        endpoint is not configured.
      operationId: slack_events_webhooks_slack_events_post
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /events/cost:
    get:
      summary: Cost Events
      description: |-
        SSE endpoint for real-time cost updates.

        Listens to the global SSE bus for ``bulletin`` events that match
        the ``live_cost_update`` status pattern and forwards them to clients.
        Also provides periodic heartbeats.
      operationId: cost_events_events_cost_get
      responses:
        "200":
          description: Server-Sent Events stream. The response body does not terminate.
          content:
            text/event-stream:
              schema:
                type: string
  /costs:
    get:
      summary: Get Costs
      description: |-
        Aggregate cost data across all runs.

        Scans every persisted cost file in ``.sdd/runtime/costs/``, aggregates
        per-agent and per-model totals, and computes cost attainment as
        ``(total_spent / total_budget) * 100``.  Budget of zero is treated as
        unlimited - attainment is reported as 0.0 in that case.
      operationId: get_costs_costs_get
      parameters:
        - name: tenant
          in: query
          required: false
          schema:
            anyOf:
              - type: string
              - type: "null"
            title: Tenant
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
        "403":
          description: Tenant access denied
        "404":
          description: Tenant not found
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /costs/live:
    get:
      summary: Get Cost Live
      description: |-
        Return live cost breakdown for the most recent run.

        Finds the most recently modified cost file in ``.sdd/runtime/costs/``,
        loads it, and returns budget status plus per-agent and per-model
        cost breakdowns.
      operationId: get_cost_live_costs_live_get
      parameters:
        - name: tenant
          in: query
          required: false
          schema:
            anyOf:
              - type: string
              - type: "null"
            title: Tenant
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
        "403":
          description: Tenant access denied
        "404":
          description: Tenant not found
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /costs/current:
    get:
      summary: Get Cost Current
      description: |-
        Return real-time cost snapshot for the active run + GUI rollups.

        Updated after each agent completion.  Designed for TUI sidebar polling
        and lightweight dashboard widgets.  Returns per-model input/output/cache
        token breakdown alongside spend and budget status.

        Web GUI (Costs.tsx §6.05) consumes the additive ``today_usd``,
        ``week_usd``, ``projected_month_usd``, ``budget_usd``, ``used_pct``,
        ``prior_week_usd``, ``delta_hour_usd``, ``resets_at`` and
        ``last_sync_at`` fields. Existing TUI/CLI callers keep reading
        ``spent_usd`` / ``percentage_used`` etc. unchanged.

        Scope: every figure here is the caller's tenant's, not the run's or the
        deployment's - spend is replayed from the caller's scope only, and the
        cap it is measured against is that tenant's configured cap where one is
        configured.  ``tenant_id`` in the response names the scope, so a client
        aggregating across tenants can tell these apart from run-wide totals.
      operationId: get_cost_current_costs_current_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /costs/alerts:
    get:
      summary: Get Cost Alerts
      description: |-
        Return active budget alerts and 30d/90d cost trends.

        Reads the live cost data for the most recent run, checks whether spend
        has reached the 80% or 95% alert threshold, and returns trend data
        computed from ``.sdd/metrics/cost_history.jsonl``.

        Both halves of this response are scoped to ``tenant_id``: ``alerts`` is
        computed from the caller's tenant's spend against the caller's tenant's
        cap, and ``trend`` / ``history_days`` are narrowed to snapshots recorded
        for that same tenant.  A history snapshot written before per-tenant
        attribution existed carries no tenant and is excluded from every scoped
        trend, so a fresh deployment's 30/90-day averages start empty and fill in
        as new, attributed snapshots accumulate.
      operationId: get_cost_alerts_costs_alerts_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /costs/history:
    get:
      summary: Get Cost History
      description: |-
        Return cost history for chart visualization.

        Two response modes share one endpoint:

        * ``GET /costs/history?hours=24&granularity=hour`` (web GUI sparkline) -
          returns a flat ``[{ts, usd}]`` array bucketed from cost-tracker
          usages over the last *hours* window.
        * ``GET /costs/history`` *or* ``?envelope=1`` (legacy/CLI) - returns the
          original ``{history, trend, burn_rate_*, history_days}`` envelope
          built from ``.sdd/metrics/cost_history.jsonl`` daily snapshots, narrowed
          to the caller's tenant the same way ``/costs/alerts`` is.

        The sparkline branch lets the GUI feed `recharts` directly without
        unwrapping a ``.history`` field.
      operationId: get_cost_history_costs_history_get
      parameters:
        - name: hours
          in: query
          required: false
          schema:
            anyOf:
              - type: integer
              - type: "null"
            title: Hours
        - name: granularity
          in: query
          required: false
          schema:
            type: string
            default: day
            title: Granularity
        - name: envelope
          in: query
          required: false
          schema:
            type: integer
            default: 0
            title: Envelope
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /costs/export:
    get:
      summary: Export Costs
      description: |-
        Export cost data as CSV or JSON for finance analysis.

        Args:
            request: FastAPI request.
            format: Export format ('csv' or 'json').

        Returns:
            File response with cost data in requested format.
      operationId: export_costs_costs_export_get
      parameters:
        - name: format
          in: query
          required: false
          schema:
            type: string
            default: json
            title: Format
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /costs/forecast:
    get:
      summary: Forecast Costs
      description: |-
        Forecast cost for next hour and project monthly spend.

        Extrapolates current spending rate to predict next hour's cost AND
        rolls the trailing 7-day spend out to a 30-day projection
        (``projected_month_usd``) for the web GUI's "projected month" KPI
        card. The legacy fields (``forecast_next_hour_usd``,
        ``burn_rate_*``, ``confidence``, ``data_points``) remain unchanged
        for the TUI / CLI.
      operationId: forecast_costs_costs_forecast_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /costs/compare:
    get:
      summary: Compare Model Costs
      description: |-
        Return live model cost comparison during execution.

        Shows current costs by model with token usage statistics.
      operationId: compare_model_costs_costs_compare_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /costs/cache-stats:
    get:
      summary: Cache Stats
      description: |-
        Return prompt cache hit rate statistics.

        Shows cache hits/misses and savings by model.
      operationId: cache_stats_costs_cache_stats_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /costs/model-comparison:
    get:
      summary: Model Cost Comparison
      description: |-
        Return model cost comparison report.

        Shows what the current run would have cost with different models.
        Useful for optimizing model routing decisions.
      operationId: model_cost_comparison_costs_model_comparison_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /costs/token-efficiency:
    get:
      summary: Token Efficiency
      description: |-
        Compare token efficiency across models and tasks.

        Ranks models by tokens per useful line of code.
      operationId: token_efficiency_costs_token_efficiency_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /costs/by-tag:
    get:
      summary: Get Costs By Tag
      description: |-
        Aggregate cost data grouped by allocation tag *or* by adapter.

        The endpoint serves three callers:

        * Web GUI (``Costs.tsx`` adapter table) - calls ``GET /costs/by-tag``
          and expects an array of ``{adapter, calls, tokens, cost_usd,
          share_pct, delta_7d_pct}`` rows. With ``shape=auto`` (default) and
          no ``tag_key``, this is what we return.
        * Legacy callers passing ``tag_key=…`` - receive the existing
          ``{by_tag: {key: {value: cost}}}`` envelope.
        * Legacy callers wanting the envelope explicitly - pass
          ``shape=tags`` and get the envelope without supplying a key.

        The ``hours`` parameter controls the GUI window (default 24h).
      operationId: get_costs_by_tag_costs_by_tag_get
      parameters:
        - name: tag_key
          in: query
          required: false
          schema:
            anyOf:
              - type: string
              - type: "null"
            title: Tag Key
        - name: hours
          in: query
          required: false
          schema:
            type: integer
            default: 24
            title: Hours
        - name: shape
          in: query
          required: false
          schema:
            type: string
            default: auto
            title: Shape
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /costs/by-adapter:
    get:
      summary: Get Costs By Adapter
      description: |-
        Per-adapter cost breakdown for the web GUI Costs tab.

        Returns the same array shape as ``GET /costs/by-tag`` (default mode);
        exists as a clearer alias so the frontend doesn't have to know about
        the legacy "by-tag" naming.
      operationId: get_costs_by_adapter_costs_by_adapter_get
      parameters:
        - name: hours
          in: query
          required: false
          schema:
            type: integer
            default: 24
            title: Hours
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /costs/top-tasks:
    get:
      summary: Get Costs Top Tasks
      description: |-
        Top *limit* most-expensive tasks within the trailing *hours* window.

        Web GUI Costs.tsx renders this as the "Top 10 tasks" card. Each item:
        ``{id, title, agent, cost_usd}``. Empty list when no usage data is
        present so the card can show its empty-state cleanly.
      operationId: get_costs_top_tasks_costs_top_tasks_get
      parameters:
        - name: limit
          in: query
          required: false
          schema:
            type: integer
            default: 10
            title: Limit
        - name: hours
          in: query
          required: false
          schema:
            type: integer
            default: 24
            title: Hours
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /costs/token-breakdown:
    get:
      summary: Get Token Breakdown
      description: |-
        Per-agent session token consumption breakdown.

        For each agent session shows where the context budget was spent:
        system prompt (Bernstein overhead), context files, task description,
        tool call results accumulated at runtime, and assistant output.

        Identifies optimization opportunities - e.g. if 60% of tokens are
        context files the agent never used.

        Args:
            request: FastAPI request.
            session_id: If provided, return breakdown for a single session only.

        Returns:
            JSON with ``sessions`` list and aggregate ``summary``.
      operationId: get_token_breakdown_costs_token_breakdown_get
      parameters:
        - name: session_id
          in: query
          required: false
          schema:
            anyOf:
              - type: string
              - type: "null"
            title: Session Id
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /costs/efficiency:
    get:
      summary: Get Cost Efficiency
      description: |-
        Real-time cost-per-line-of-code efficiency metric.

        Shows cost efficiency as the run progresses:
        - **current**: efficiency of the most recently completed task
        - **run_average**: efficiency across all completed tasks in this run
        - **historical_average**: efficiency across all tracked runs

        Helps identify unusually expensive runs.

        Returns:
            JSON with ``current``, ``run_average``, ``historical_average``, and
            ``message`` fields.
      operationId: get_cost_efficiency_costs_efficiency_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /costs/{run_id}:
    get:
      summary: Get Cost Budget
      description: |-
        Return budget status for a specific run, within the caller's scope.

        Loads the persisted cost tracker from ``.sdd/runtime/costs/{run_id}.json``
        and returns its ``BudgetStatus`` as JSON.

        Scope: every figure is the caller's tenant's share of the run, not the
        run's total - the run file holds the spend of every tenant that spent
        against it, and only the caller's is replayed.  ``tenant_id`` in the
        response names the scope the figures belong to.
      operationId: get_cost_budget_costs__run_id__get
      parameters:
        - name: run_id
          in: path
          required: true
          schema:
            type: string
            title: Run Id
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
        "404":
          description: No cost data for run
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /dashboard/auth/status:
    get:
      summary: Dashboard Auth Status
      description: Report whether dashboard auth is required and who is logged in.
      operationId: dashboard_auth_status_dashboard_auth_status_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /dashboard/auth/login:
    post:
      summary: Dashboard Auth Login
      description: |-
        Open a dashboard session from a password or a scoped token.

        The session cookie wraps exactly the principal and scope the credential
        carried; a viewer token can never log into an operator session. Every
        attempt -- success or failure -- is journaled as a signed governance
        decision (``dashboard.login``).
      operationId: dashboard_auth_login_dashboard_auth_login_post
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /dashboard/auth/logout:
    post:
      summary: Dashboard Auth Logout
      description: Close the current dashboard session (idempotent).
      operationId: dashboard_auth_logout_dashboard_auth_logout_post
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /dashboard/file_locks:
    get:
      summary: File Locks Endpoint
      description: |-
        Return active file locks grouped by agent for the dashboard.

        Reads the persisted lock state from ``.sdd/runtime/file_locks.json`` and
        returns it in a dashboard-friendly format with both a flat list and an
        agent-grouped view.

        Returns:
            JSON with ``all_locks`` (flat list sorted by path), ``locks_by_agent``
            (dict keyed by agent_id with files list + task info + elapsed_s),
            ``count`` (total lock count), and ``ts`` (generation timestamp).
      operationId: file_locks_endpoint_dashboard_file_locks_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /dashboard/team:
    get:
      summary: Team Adoption Dashboard
      description: |-
        Aggregate team usage metrics for engineering managers.

        Returns total runs, tasks completed, cost saved vs. budget,
        code merge stats, and quality gate pass rate.
      operationId: team_adoption_dashboard_dashboard_team_get
      responses:
        "200":
          description: Team adoption metrics
          content:
            application/json:
              schema: {}
  /graph/impact:
    get:
      tags:
        - graph
      summary: Graph Impact
      description: Return downstream files impacted by changing the given file.
      operationId: graph_impact_graph_impact_get
      parameters:
        - name: file
          in: query
          required: true
          schema:
            type: string
            minLength: 1
            title: File
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ImpactResponse"
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /observability/agents:
    get:
      summary: Observability Agents
      description: Return runtime heartbeat, stall-profile, and log-summary data per agent.
      operationId: observability_agents_observability_agents_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
                title: Response Observability Agents Observability Agents Get
  /observability/effectiveness:
    get:
      summary: Observability Effectiveness
      description: Return recent effectiveness data, role trends, and best configs.
      operationId: observability_effectiveness_observability_effectiveness_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
                title: Response Observability Effectiveness Observability Effectiveness Get
  /observability/recommendations:
    get:
      summary: Observability Recommendations
      description: Return the current recommendation set and delivery hit counts.
      operationId: observability_recommendations_observability_recommendations_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
                title: Response Observability Recommendations Observability Recommendations Get
  /observability/budget:
    get:
      summary: Observability Budget
      description: Return completion-budget status per lineage.
      operationId: observability_budget_observability_budget_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
                title: Response Observability Budget Observability Budget Get
  /observability/deps:
    get:
      summary: Observability Deps
      description: |-
        Return dependency-graph validation status for current tasks.

        The response names the ids it walked - the ready set, the critical path,
        and both broken-edge lists - so the walk is narrowed to the caller's
        tenant scope rather than the whole store.
      operationId: observability_deps_observability_deps_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
                title: Response Observability Deps Observability Deps Get
  /recap:
    get:
      summary: Recap
      description: |-
        Return post-run summary with diff stats, quality scores, and cost breakdown.

        Reads completed tasks from the archive and computes:
        - Task completion statistics
        - Git diff statistics (files changed, additions, deletions)
        - Quality score distribution
        - Cost breakdown by model and role
      operationId: recap_recap_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
                title: Response Recap Recap Get
  /observability/token-histogram:
    get:
      summary: Token Histogram
      description: |-
        Return histogram of token usage by task complexity.

        Shows average tokens consumed for small, medium, large tasks.
        Helps understand token consumption patterns.
      operationId: token_histogram_observability_token_histogram_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
                title: Response Token Histogram Observability Token Histogram Get
  /observability/queue-depth:
    get:
      summary: Get Queue Depth
      description: |-
        Return task queue depth over time.

        Returns last N records of queue depth snapshots.

        Args:
            request: FastAPI request.
            limit: Maximum number of records to return (default 100).

        Returns:
            List of queue depth snapshots with timestamps.
      operationId: get_queue_depth_observability_queue_depth_get
      parameters:
        - name: limit
          in: query
          required: false
          schema:
            type: integer
            default: 100
            title: Limit
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
                title: Response Get Queue Depth Observability Queue Depth Get
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /observability/timeline:
    get:
      summary: Get Timeline
      description: |-
        Return task timing data for timeline visualization.

        Returns start and end times for all tasks tracked in metrics.
      operationId: get_timeline_observability_timeline_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
                title: Response Get Timeline Observability Timeline Get
  /changelog:
    get:
      summary: Get Changelog
      description: |-
        Generate changelog from completed tasks.

        Groups completed tasks by type (Features, Fixes, etc.) and
        formats as markdown changelog.

        Args:
            request: FastAPI request.
            days: Number of days to include (default 30).

        Returns:
            Dict with 'markdown' key containing changelog text.
      operationId: get_changelog_changelog_get
      parameters:
        - name: days
          in: query
          required: false
          schema:
            type: integer
            default: 30
            title: Days
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
                title: Response Get Changelog Changelog Get
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /observability/incidents:
    get:
      summary: List Incidents
      description: |-
        List all known incidents.

        Returns:
            Dict with 'incidents' list.
      operationId: list_incidents_observability_incidents_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
                title: Response List Incidents Observability Incidents Get
  /observability/incident-timeline/{incident_id}:
    get:
      summary: Get Incident Timeline
      description: |-
        Build a correlated incident timeline from logs, metrics, and traces.

        Args:
            request: FastAPI request.
            incident_id: The incident ID to build a timeline for.
            window_before: Seconds before incident to include (default 600).
            window_after: Seconds after incident to include (default 300).

        Returns:
            Dict with incident metadata and sorted timeline events.
      operationId: get_incident_timeline_observability_incident_timeline__incident_id__get
      parameters:
        - name: incident_id
          in: path
          required: true
          schema:
            type: string
            title: Incident Id
        - name: window_before
          in: query
          required: false
          schema:
            type: integer
            default: 600
            title: Window Before
        - name: window_after
          in: query
          required: false
          schema:
            type: integer
            default: 300
            title: Window After
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
                title: Response Get Incident Timeline Observability Incident Timeline  Incident Id  Get
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /observability/token-breakdown:
    get:
      summary: Token Breakdown
      description: |-
        Return per-session token consumption breakdown.

        For each agent session with a ``.tokens`` sidecar file, breaks down
        token usage into estimated categories:

        - ``system_prompt_estimated``: overhead from Bernstein role templates
        - ``task_description_estimated``: tokens for the task title + description
        - ``context_estimated``: remaining input tokens (context files, tool results,
          prior conversation history, etc.)
        - ``output_tokens``: actual assistant output tokens

        Also reports ``optimization_opportunities`` - a list of human-readable
        insights when a category accounts for an unusually large share of tokens
        (e.g. "context files are 60% of input").

        Token sidecar files live at ``.sdd/runtime/{session_id}.tokens``.
        Breakdown percentages use a 4-chars/token heuristic for size estimates.

        Returns:
            Dict with ``sessions`` list and aggregate ``summary``.
      operationId: token_breakdown_observability_token_breakdown_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
                title: Response Token Breakdown Observability Token Breakdown Get
  /quality:
    get:
      summary: Get Quality Metrics
      description: |-
        Return aggregated internal quality metrics (last 7 days).

        Reads from ``.sdd/metrics/`` JSONL files to compute:

        - ``per_model``: per-model success rate, avg tokens, and completion
          time distribution (p50/p90/p99).
        - ``overall``: aggregate across all models.
        - ``gate_stats``: per-gate pass/blocked/flagged counts (last 30 days).
        - ``guardrail_pass_rate``: fraction of gate checks that passed.
        - ``review_rejection_rate``: fraction of tasks that failed overall.

        Returns an empty structure when no metric data exists yet.
      operationId: get_quality_metrics_quality_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /quality/budget-forecast:
    get:
      summary: Get Budget Forecast
      description: Return projected spend for the active planned backlog.
      operationId: get_budget_forecast_quality_budget_forecast_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /quality/trend:
    get:
      summary: Get Quality Trend
      description: |-
        Return time-series quality metrics for trend visualization.

        Buckets quality data by day (default) or week and returns per-bucket
        success rates, gate pass rates, and average quality scores. Covers the
        last 90 days by default so dashboards can show weeks-to-months trends.

        Query parameters:
        - ``days``: lookback window in days (default 90, max 365).
        - ``granularity``: ``"day"`` (default) or ``"week"``.

        Returns a ``series`` list ordered by date, each entry containing:
        - ``date``: ISO date string (bucket start).
        - ``ts``: Unix timestamp of the bucket start.
        - ``tasks_total``, ``tasks_success``: raw task counts.
        - ``success_rate``: fraction of tasks that succeeded (omitted if no tasks).
        - ``gate_pass_rates``: dict of gate name → pass rate for that bucket.
        - ``avg_quality_score``: mean quality score 0-100 (omitted if no scores).
      operationId: get_quality_trend_quality_trend_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /quality/models:
    get:
      summary: Get Quality By Model
      description: |-
        Return per-model quality breakdown (last 30 days).

        Extended view of model performance for routing configuration and cost
        analysis. Covers a longer window than the default ``/quality`` summary.
      operationId: get_quality_by_model_quality_models_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /quality/file-health:
    get:
      summary: List File Health
      description: |-
        Return per-file code health scores, worst files first.

        Query parameters:
        - ``limit``: max results (default 50, max 500).
        - ``min_score``: only return files at or below this score.
        - ``grade``: filter by grade (A/B/C/D/F).

        Returns a JSON object with ``files`` list and summary statistics.
      operationId: list_file_health_quality_file_health_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /quality/file-health/flagged:
    get:
      summary: List Flagged Files
      description: |-
        Return files currently flagged for human review due to health degradation.

        A file is flagged when:
        - A task dropped its health score by ≥10 points, OR
        - Its total health score is below 60 (grade D or F).

        Returns ``files`` list with detailed health scores and degradation context.
      operationId: list_flagged_files_quality_file_health_flagged_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /quality/file-health/{file_path}:
    get:
      summary: Get File Health
      description: |-
        Return the current health score for a single file.

        Args:
            file_path: File path relative to repository root (URL-encoded).

        Returns 404 if the file has never been tracked.
      operationId: get_file_health_quality_file_health__file_path__get
      parameters:
        - name: file_path
          in: path
          required: true
          schema:
            type: string
            title: File Path
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
        "404":
          description: File not tracked yet
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /fleet/projects:
    get:
      summary: Fleet Projects
      description: |-
        Return aggregated per-project snapshots for the fleet overview.

        Response shape mirrors :func:`bernstein.core.fleet.web.api_projects`:

        .. code-block:: json

            {
              "projects": [ProjectSnapshot, ...],
              "errors": [],
              "stub": true|false,
              "hint": "Run `bernstein fleet --web` for the real aggregator."
            }

        ``stub: true`` means the operator UI is talking to a single-project
        server that has no fleet aggregator wired in; the ``projects`` list
        is empty in that case so the SPA can render the empty-state.
      operationId: fleet_projects_fleet_projects_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
                title: Response Fleet Projects Fleet Projects Get
  /fleet/search:
    get:
      summary: Fleet Search
      description: |-
        Cross-project search stub for the topbar search bar.

        Accepts a free-text query plus the ``agent:/status:/across:`` operator
        syntax used by the frontend search component; the stub does not yet
        execute the search and instead returns the parsed filters so the SPA
        can demonstrate the round-trip while the backend implementation is
        being built.

        Returns:
            ``{"query": str, "filters": {...}, "matches": [], "stub": bool}``.
      operationId: fleet_search_fleet_search_get
      parameters:
        - name: q
          in: query
          required: false
          schema:
            type: string
            description: Cross-project search query
            default: ""
            title: Q
          description: Cross-project search query
        - name: limit
          in: query
          required: false
          schema:
            type: integer
            maximum: 500
            minimum: 1
            default: 50
            title: Limit
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
                title: Response Fleet Search Fleet Search Get
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /drain:
    get:
      summary: Drain Status
      description: Check drain status.
      operationId: drain_status_drain_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
    post:
      summary: Drain Start
      description: Begin draining -- stop accepting new task claims.
      operationId: drain_start_drain_post
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /drain/cancel:
    post:
      summary: Drain Cancel
      description: Cancel drain -- resume accepting claims.
      operationId: drain_cancel_drain_cancel_post
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /identities:
    get:
      tags:
        - identities
      summary: List Identities
      description: |-
        List agent identities with optional status/role filters.

        ``status`` is validated against the :class:`AgentIdentityStatus`
        enum by FastAPI, so an unknown value yields a ``422`` rather than
        reaching the handler and raising an unhandled ``ValueError``.
      operationId: list_identities_identities_get
      parameters:
        - name: status
          in: query
          required: false
          schema:
            anyOf:
              - $ref: "#/components/schemas/AgentIdentityStatus"
              - type: "null"
            title: Status
        - name: role
          in: query
          required: false
          schema:
            anyOf:
              - type: string
              - type: "null"
            title: Role
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /identities/{identity_id}:
    get:
      tags:
        - identities
      summary: Get Identity
      description: Get details for a single agent identity.
      operationId: get_identity_identities__identity_id__get
      parameters:
        - name: identity_id
          in: path
          required: true
          schema:
            type: string
            title: Identity Id
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
        "404":
          description: Identity not found
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /identities/{identity_id}/revoke:
    post:
      tags:
        - identities
      summary: Revoke Identity
      description: Revoke an agent identity.
      operationId: revoke_identity_identities__identity_id__revoke_post
      parameters:
        - name: identity_id
          in: path
          required: true
          schema:
            type: string
            title: Identity Id
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
        "404":
          description: Identity not found
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /identities/{identity_id}/audit:
    get:
      tags:
        - identities
      summary: Identity Audit
      description: Return the audit trail for an agent identity.
      operationId: identity_audit_identities__identity_id__audit_get
      parameters:
        - name: identity_id
          in: path
          required: true
          schema:
            type: string
            title: Identity Id
        - name: limit
          in: query
          required: false
          schema:
            type: integer
            default: 100
            title: Limit
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /.well-known/acp.json:
    get:
      summary: Acp Discovery
      description: ACP discovery document - editors poll this to find ACP-compatible agents.
      operationId: acp_discovery__well_known_acp_json_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ACPDiscoveryResponse"
  /acp/v0/agents:
    get:
      summary: List Acp Agents
      description: List all ACP-advertised agents.
      operationId: list_acp_agents_acp_v0_agents_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                items:
                  $ref: "#/components/schemas/ACPAgentListEntry"
                type: array
                title: Response List Acp Agents Acp V0 Agents Get
  /acp/v0/agents/{agent_id}:
    get:
      summary: Get Acp Agent
      description: Get detailed metadata for a specific ACP agent.
      operationId: get_acp_agent_acp_v0_agents__agent_id__get
      parameters:
        - name: agent_id
          in: path
          required: true
          schema:
            type: string
            title: Agent Id
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ACPAgentResponse"
        "404":
          description: ACP agent not found
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /acp/v0/runs:
    post:
      summary: Create Acp Run
      description: |-
        Create an ACP run - creates a Bernstein task and links it.

        Editors call this when the user submits a goal via the ACP sidebar.
      operationId: create_acp_run_acp_v0_runs_post
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ACPRunCreateRequest"
        required: true
      responses:
        "201":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ACPRunResponse"
        "400":
          description: Unknown ACP agent
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /acp/v0/runs/{run_id}:
    get:
      summary: Get Acp Run
      description: Get ACP run status, syncing from the underlying Bernstein task.
      operationId: get_acp_run_acp_v0_runs__run_id__get
      parameters:
        - name: run_id
          in: path
          required: true
          schema:
            type: string
            title: Run Id
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ACPRunResponse"
        "404":
          description: ACP run not found
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
    delete:
      summary: Cancel Acp Run
      description: Cancel an ACP run and its underlying Bernstein task.
      operationId: cancel_acp_run_acp_v0_runs__run_id__delete
      parameters:
        - name: run_id
          in: path
          required: true
          schema:
            type: string
            title: Run Id
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ACPRunResponse"
        "404":
          description: ACP run not found
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /approvals:
    get:
      tags:
        - approvals
      summary: List Approvals
      description: List all pending approval requests across task-review and pre-spawn gates.
      operationId: list_approvals_approvals_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ListApprovalsResponse"
  /approvals/{task_id}/approve:
    post:
      tags:
        - approvals
      summary: Approve Task
      description: |-
        Approve a pending approval request.

        Writes a .approved decision file so the orchestrator poll loop unblocks.
        The pending file is then removed.

        Args:
            task_id: Task ID to approve.
            body: Optional reason metadata.

        Returns:
            Success message.
      operationId: approve_task_approvals__task_id__approve_post
      parameters:
        - name: task_id
          in: path
          required: true
          schema:
            type: string
            title: Task Id
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ApprovalDecisionRequest"
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                type: object
                additionalProperties:
                  type: string
                title: Response Approve Task Approvals  Task Id  Approve Post
        "400":
          description: Invalid task_id format
        "404":
          description: No pending approval for task
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /approvals/{task_id}/reject:
    post:
      tags:
        - approvals
      summary: Reject Task
      description: |-
        Reject a pending approval request.

        Writes a .rejected decision file so the orchestrator poll loop unblocks.
        The pending file is then removed.

        Args:
            task_id: Task ID to reject.
            body: Optional reason metadata.

        Returns:
            Success message.
      operationId: reject_task_approvals__task_id__reject_post
      parameters:
        - name: task_id
          in: path
          required: true
          schema:
            type: string
            title: Task Id
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ApprovalDecisionRequest"
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                type: object
                additionalProperties:
                  type: string
                title: Response Reject Task Approvals  Task Id  Reject Post
        "400":
          description: Invalid task_id format
        "404":
          description: No pending approval for task
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /approvals/queue:
    get:
      tags:
        - approvals
      summary: List Queued Approvals
      description: |-
        List pending tool-call approvals (op-002).

        Args:
            session_id: Optional filter; when given only approvals for that
                session are returned.
      operationId: list_queued_approvals_approvals_queue_get
      parameters:
        - name: session_id
          in: query
          required: false
          schema:
            anyOf:
              - type: string
              - type: "null"
            title: Session Id
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/QueuedApprovalsResponse"
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /approvals/{approval_id}/resolve:
    post:
      tags:
        - approvals
      summary: Resolve Queued Approval
      description: |-
        Resolve a queued approval with ``allow``, ``reject``, or ``always``.

        The request body must echo the ``nonce`` the gate issued when the
        approval was queued. Mismatches return ``409 NONCE_MISMATCH``; a
        nonce replayed against an already-resolved or evicted approval
        returns ``410 NONCE_EXPIRED``.
      operationId: resolve_queued_approval_approvals__approval_id__resolve_post
      parameters:
        - name: approval_id
          in: path
          required: true
          schema:
            type: string
            title: Approval Id
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ResolveRequest"
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                type: object
                additionalProperties:
                  type: string
                title: Response Resolve Queued Approval Approvals  Approval Id  Resolve Post
        "400":
          description: Invalid approval id or decision
        "404":
          description: No pending approval with that id
        "409":
          description: NONCE_MISMATCH
        "410":
          description: NONCE_EXPIRED
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /approvals/live-fragment:
    get:
      tags:
        - approvals
      summary: Approvals Live Fragment
      description: |-
        Return an HTML fragment the live-session page embeds.

        Each pending approval becomes a row with three buttons that POST the
        resolution back to ``/approvals/{id}/resolve``. The fragment is
        intentionally minimal so it can be inlined into the existing live
        dashboard without pulling a new framework.
      operationId: approvals_live_fragment_approvals_live_fragment_get
      parameters:
        - name: session_id
          in: query
          required: false
          schema:
            anyOf:
              - type: string
              - type: "null"
            title: Session Id
      responses:
        "200":
          description: Successful Response
          content:
            text/html:
              schema:
                type: string
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /plans:
    get:
      tags:
        - plans
      summary: List Plans
      description: |-
        List all plans, optionally filtered by status.

        Query params:
            status: Filter by plan status (pending, approved, rejected, expired).
      operationId: list_plans_plans_get
      parameters:
        - name: status
          in: query
          required: false
          schema:
            anyOf:
              - type: string
              - type: "null"
            title: Status
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                type: array
                items:
                  type: object
                  additionalProperties: true
                title: Response List Plans Plans Get
        "400":
          description: Invalid status filter
        "404":
          description: Plan mode is not enabled on this server
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /plans/{plan_id}:
    get:
      tags:
        - plans
      summary: Get Plan
      description: Get a single plan by ID.
      operationId: get_plan_plans__plan_id__get
      parameters:
        - name: plan_id
          in: path
          required: true
          schema:
            type: string
            title: Plan Id
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
                title: Response Get Plan Plans  Plan Id  Get
        "404":
          description: Plan not found, or plan mode is not enabled on this server
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /plans/{plan_id}/approve:
    post:
      tags:
        - plans
      summary: Approve Plan
      description: |-
        Approve a plan: promotes all its PLANNED tasks to OPEN.

        This is the key operation: once approved, the orchestrator will
        pick up the tasks and start spawning agents.
      operationId: approve_plan_plans__plan_id__approve_post
      parameters:
        - name: plan_id
          in: path
          required: true
          schema:
            type: string
            title: Plan Id
      requestBody:
        content:
          application/json:
            schema:
              anyOf:
                - $ref: "#/components/schemas/PlanDecisionRequest"
                - type: "null"
              title: Body
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
                title: Response Approve Plan Plans  Plan Id  Approve Post
        "404":
          description: Plan not found, or plan mode is not enabled on this server
        "409":
          description: Plan already decided, or the plan changed after it was rendered for review
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /plans/{plan_id}/reject:
    post:
      tags:
        - plans
      summary: Reject Plan
      description: |-
        Reject a plan: cancels all its PLANNED tasks.

        Rejected tasks are moved to CANCELLED status so they never execute.
      operationId: reject_plan_plans__plan_id__reject_post
      parameters:
        - name: plan_id
          in: path
          required: true
          schema:
            type: string
            title: Plan Id
      requestBody:
        content:
          application/json:
            schema:
              anyOf:
                - $ref: "#/components/schemas/PlanDecisionRequest"
                - type: "null"
              title: Body
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
                title: Response Reject Plan Plans  Plan Id  Reject Post
        "404":
          description: Plan not found, or plan mode is not enabled on this server
        "409":
          description: Plan already decided, or the plan changed after it was rendered for review
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /gateway/metrics:
    get:
      summary: Gateway Metrics
      description: |-
        Return per-tool MCP call metrics from the active gateway session.

        Returns an empty ``metrics`` dict when no gateway is running.
        Clients can use ``active`` to distinguish the two cases.
      operationId: gateway_metrics_gateway_metrics_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /slo:
    get:
      summary: Get Slo Status
      description: Return current SLO dashboard data.
      operationId: get_slo_status_slo_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /slo/budget:
    get:
      summary: Get Error Budget
      description: Return error budget details in focused format.
      operationId: get_error_budget_slo_budget_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /slo/burndown:
    get:
      summary: Get Slo Burndown
      description: |-
        Return SLO burn-down rate visualization data .

        Provides:
        - Current SLO compliance and error budget fraction
        - Burn rate relative to the allowed failure rate (1.0 = on-target)
        - Linear projection of days until the SLO is breached
        - Sparkline data points for rendering a burn-down chart
        - Human-readable breach projection summary

        Example response::

            {
              "slo_name": "task_success",
              "slo_target": 0.9,
              "slo_current": 0.942,
              "burn_rate": 0.3,
              "burn_rate_per_day": 0.05,
              "budget_fraction": 0.72,
              "budget_consumed_pct": 28.0,
              "days_to_breach": 6.1,
              "breach_projection": "SLO will breach in 6.1 days at current rate",
              "status": "green",
              "sparkline": [...]
            }
      operationId: get_slo_burndown_slo_burndown_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /slo/reset:
    post:
      summary: Reset Slo State
      description: Reset SLO tracker to initial state (no persisted data cleared).
      operationId: reset_slo_state_slo_reset_post
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /sla:
    get:
      summary: List Contracts
      description: Return every registered SLA contract.
      operationId: list_contracts_sla_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /sla/receipts:
    get:
      summary: List Receipts
      description: Return the operator projection of every persisted violation receipt.
      operationId: list_receipts_sla_receipts_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /sla/receipts/{receipt_id}/verify:
    get:
      summary: Verify Receipt Endpoint
      description: Verify a persisted violation receipt offline and return the verdict.
      operationId: verify_receipt_endpoint_sla_receipts__receipt_id__verify_get
      parameters:
        - name: receipt_id
          in: path
          required: true
          schema:
            type: string
            title: Receipt Id
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /sla/{contract_id}:
    get:
      summary: Show Contract
      description: Return one SLA contract's full record.
      operationId: show_contract_sla__contract_id__get
      parameters:
        - name: contract_id
          in: path
          required: true
          schema:
            type: string
            title: Contract Id
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /sla/{contract_id}/report:
    get:
      summary: Contract Report
      description: Return the deterministic error-budget report for a contract.
      operationId: contract_report_sla__contract_id__report_get
      parameters:
        - name: contract_id
          in: path
          required: true
          schema:
            type: string
            title: Contract Id
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /metrics/custom:
    get:
      summary: Get Custom Metrics
      description: |-
        Evaluate all configured custom metrics and return current values.

        Returns an object with a ``metrics`` list. Each entry contains:
        - ``name``: metric name
        - ``value``: computed float value
        - ``unit``: display unit (e.g. ``"lines/$"``)
        - ``description``: optional human-readable description
        - ``error``: present only when evaluation failed

        Returns 200 with an empty list if no custom metrics are configured.
      operationId: get_custom_metrics_metrics_custom_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /metrics/custom/schema:
    get:
      summary: Get Custom Metrics Schema
      description: |-
        Return the configured custom metric definitions (formulas and units).

        Returns the schema without evaluating - useful for documentation and
        formula validation checks.
      operationId: get_custom_metrics_schema_metrics_custom_schema_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /sbom/generate:
    post:
      tags:
        - sbom
      summary: Generate SBOM and optionally run vulnerability scan
      description: |-
        Generate a CycloneDX or SPDX SBOM from installed packages.

        After generation, optionally run ``osv-scanner`` or ``grype`` for
        vulnerability scanning.  When ``block_on_critical=true`` and critical
        findings are detected, responds with HTTP 422 so CI/CD pipelines can
        gate merges on vulnerability status.

        SBOM artifacts are written to ``.sdd/artifacts/sbom/``.
      operationId: generate_sbom_sbom_generate_post
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SBOMGenerateRequest"
        required: true
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SBOMGenerateResponse"
        "400":
          description: Unknown SBOM format
        "422":
          description: Critical vulnerabilities found (gate blocked)
        "503":
          description: Server workdir not configured
  /sbom/artifacts:
    get:
      tags:
        - sbom
      summary: List generated SBOM artifact files
      description: List previously generated SBOM artifact files from ``.sdd/artifacts/sbom/``.
      operationId: list_sbom_artifacts_sbom_artifacts_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SBOMListResponse"
        "503":
          description: Server workdir not configured
  /hooks/{session_id}:
    post:
      summary: Receive Hook
      description: |-
        Receive a hook event from Claude Code.

        Claude Code sends structured JSON with at minimum a ``hook_event_name``
        field.  The event is parsed, persisted to a JSONL sidecar, and triggers
        side effects (heartbeat touch, completion markers, etc.).

        The request body is verified against
        ``X-Bernstein-Hook-Signature-256`` (HMAC-SHA256 over the raw body,
        keyed with ``BERNSTEIN_HOOK_SECRET``) *before* any parsing or
        filesystem work - this is the authentication boundary for the
        endpoint. The ``session_id`` is then validated against
        a strict allowlist to prevent path traversal.

        Args:
            session_id: Agent session identifier from the URL path.
            request: The incoming FastAPI request.

        Returns:
            JSON response with status and action taken, 401 if signature
            verification fails, or 400 if ``session_id`` is unsafe / body
            is not valid JSON.
      operationId: receive_hook_hooks__session_id__post
      parameters:
        - name: session_id
          in: path
          required: true
          schema:
            type: string
            title: Session Id
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /export/tasks:
    get:
      summary: Export Tasks
      description: |-
        Export tasks as CSV or JSON.

        Query params:
            format: ``csv`` or ``json`` (default ``json``).
            limit: Optional max number of tasks to return. Pushed into
                ``TaskStore.list_tasks`` so large stores no longer materialise
                the whole table (issue #1728 finding 3).
            offset: Optional number of tasks to skip before returning rows.

        The export is a whole-store read, so it narrows to the caller's tenant
        scope the same way the paginated task list does. The scope is pushed into
        ``list_tasks`` rather than applied to the returned rows so that it is
        ``limit``/``offset`` that page through the caller's own tasks: filtering
        after the slice would page through every tenant's and return short pages.
      operationId: export_tasks_export_tasks_get
      parameters:
        - name: format
          in: query
          required: false
          schema:
            type: string
            default: json
            title: Format
        - name: limit
          in: query
          required: false
          schema:
            anyOf:
              - type: integer
              - type: "null"
            title: Limit
        - name: offset
          in: query
          required: false
          schema:
            anyOf:
              - type: integer
              - type: "null"
            title: Offset
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /export/agents:
    get:
      summary: Export Agents
      description: |-
        Export agent snapshots as CSV or JSON.

        Query params:
            format: ``csv`` or ``json`` (default ``json``).
      operationId: export_agents_export_agents_get
      parameters:
        - name: format
          in: query
          required: false
          schema:
            type: string
            default: json
            title: Format
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /grafana/dashboard:
    get:
      summary: Grafana Dashboard Endpoint
      description: |-
        Generate and return the Grafana dashboard JSON.

        Query params:
            datasource: Prometheus datasource name (default ``Prometheus``).
      operationId: grafana_dashboard_endpoint_grafana_dashboard_get
      parameters:
        - name: datasource
          in: query
          required: false
          schema:
            type: string
            default: Prometheus
            title: Datasource
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /dashboard/tasks/{task_id}:
    get:
      summary: Task Detail
      description: |-
        Return detailed task view including log tail and progress.

        Args:
            task_id: Task identifier.
      operationId: task_detail_dashboard_tasks__task_id__get
      parameters:
        - name: task_id
          in: path
          required: true
          schema:
            type: string
            title: Task Id
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TaskDetailResponse"
        "404":
          description: Task not found
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /dashboard/tasks/{task_id}/logs/stream:
    get:
      summary: Task Log Stream
      description: |-
        Stream agent logs for a task via Server-Sent Events.

        The stream sends new log content as ``log`` events and closes
        after the task completes or ``_MAX_IDLE_TICKS`` seconds of no new data.
      operationId: task_log_stream_dashboard_tasks__task_id__logs_stream_get
      parameters:
        - name: task_id
          in: path
          required: true
          schema:
            type: string
            title: Task Id
      responses:
        "200":
          description: Server-Sent Events stream. The response body does not terminate.
          content:
            text/event-stream:
              schema:
                type: string
        "404":
          description: Task not found
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /dashboard/tasks/{task_id}/diff:
    get:
      summary: Task Diff
      description: |-
        Return the diff for a task's working branch against the base ref.

        Strategy:
            1. Resolve the working branch from the task's ``assigned_agent`` --
               ``agent/<session-id>``. If no agent is assigned (or the branch
               does not exist yet), fall back to ``git diff HEAD`` so the user
               still sees uncommitted scratch work.
            2. Run ``git diff <base>...<branch>`` (three-dot, symmetric
               difference relative to the merge base) and parse the output into
               a structured per-file representation.
            3. Cap the unified diff at ``_DIFF_MAX_BYTES`` to keep payloads sane.

        The sync ``_run_git`` helper is reused (it is also called from other
        sync helpers in this module). To keep the event loop responsive under
        load (issue #1723) every blocking ``_run_git`` invocation is offloaded
        to the default executor via ``asyncio.to_thread``. The helper itself
        stays sync so non-route callers keep working.
      operationId: task_diff_dashboard_tasks__task_id__diff_get
      parameters:
        - name: task_id
          in: path
          required: true
          schema:
            type: string
            title: Task Id
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TaskDiffResponse"
        "404":
          description: Task not found
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /dashboard/tasks/{task_id}/trace:
    get:
      summary: Task Trace
      description: |-
        Return the timeline of trace events for *task_id*.

        The endpoint is read-only and idempotent. A missing task returns 404; a
        valid task with no trace returns 200 + an empty events list (the FE
        renders an empty-state card in that case).
      operationId: task_trace_dashboard_tasks__task_id__trace_get
      parameters:
        - name: task_id
          in: path
          required: true
          schema:
            type: string
            title: Task Id
        - name: limit
          in: query
          required: false
          schema:
            type: integer
            maximum: 2000
            minimum: 1
            default: 500
            title: Limit
        - name: cursor
          in: query
          required: false
          schema:
            type: integer
            minimum: 0
            default: 0
            title: Cursor
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TraceTimelineResponse"
        "404":
          description: Task not found
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /health/deps:
    get:
      summary: Health Deps
      description: |-
        Return health status with dependency checks.

        Checks: server, store, adapters, sse_bus.
        Overall status is ``healthy`` if all dependencies are ok,
        ``degraded`` if any are degraded, ``unhealthy`` if any are down.
      operationId: health_deps_health_deps_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HealthDepsResponse"
  /tasks/batch-ops:
    post:
      tags:
        - batch-operations
      summary: Batch Operations
      description: |-
        Execute a batch operation on multiple tasks.

        Supported actions:
        - **cancel**: Cancel all specified tasks.
        - **retry**: Reset failed tasks back to open.
        - **reprioritize**: Update priority on all specified tasks (requires ``priority``).
        - **tag**: Add tags to all specified tasks (requires ``tags``).

        Returns a result with lists of succeeded and failed task IDs.
      operationId: batch_operations_tasks_batch_ops_post
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/BatchRequest"
        required: true
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/BatchResult"
        "422":
          description: Invalid batch request
  /audit:
    get:
      tags:
        - audit
      summary: Query Audit Log
      description: |-
        Query the audit log with filtering and pagination.

        Returns:
            Dict with items, total, page, page_size. Items are normalised
            through :func:`_normalise_audit_row` so the web GUI table can
            render every row without optional-chain dance.
      operationId: query_audit_log_audit_get
      parameters:
        - name: event_type
          in: query
          required: false
          schema:
            anyOf:
              - type: string
              - type: "null"
            title: Event Type
        - name: search
          in: query
          required: false
          schema:
            anyOf:
              - type: string
              - type: "null"
            title: Search
        - name: page
          in: query
          required: false
          schema:
            type: integer
            default: 1
            title: Page
        - name: page_size
          in: query
          required: false
          schema:
            type: integer
            default: 50
            title: Page Size
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
                title: Response Query Audit Log Audit Get
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /audit/verify:
    get:
      tags:
        - audit
      summary: Audit Verify
      description: |-
        Lightweight HMAC chain integrity probe for the web GUI banner.

        Walks ``.sdd/audit/*.jsonl`` events and returns a fully-populated
        payload (no nulls in core scalar fields) so the GUI's
        ``ChainStatusBanner`` has something to render even when the audit
        directory hasn't been initialised yet. Full Sigstore / Merkle
        reconciliation lives in the lineage-v1 verifier CLI.
      operationId: audit_verify_audit_verify_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
                title: Response Audit Verify Audit Verify Get
    post:
      tags:
        - audit
      summary: Audit Reverify
      description: |-
        Re-walk the audit chain.

        Behaviourally identical to ``GET /audit/verify`` for the lightweight
        probe - the operator-visible "Re-verify" button in the GUI just wants
        a fresh walk and an up-to-date payload. Accepts ``{from_chunk}`` so
        future implementations can scope the walk; today the field is read
        and echoed but not used to slice the chain.
      operationId: audit_reverify_audit_verify_post
      requestBody:
        content:
          application/json:
            schema:
              anyOf:
                - $ref: "#/components/schemas/VerifyChainRequest"
                - type: "null"
              title: Body
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
                title: Response Audit Reverify Audit Verify Post
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /audit/export:
    post:
      tags:
        - audit
      summary: Audit Export
      description: |-
        Stream the filtered audit log as CSV or JSONL.

        Same filter semantics as ``GET /audit`` (``event_type``, ``search``,
        ``from``, ``to``); returns the entire matching set in one body, no
        pagination - operators expect to download the whole filtered slice.
        Used by the web GUI Export menu (CSV / JSONL buttons).
      operationId: audit_export_audit_export_post
      parameters:
        - name: format
          in: query
          required: false
          schema:
            type: string
            default: csv
            title: Format
        - name: event_type
          in: query
          required: false
          schema:
            anyOf:
              - type: string
              - type: "null"
            title: Event Type
        - name: search
          in: query
          required: false
          schema:
            anyOf:
              - type: string
              - type: "null"
            title: Search
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /graphql:
    post:
      tags:
        - graphql
      summary: Graphql Endpoint
      description: |-
        Execute a GraphQL query.

        Accepts a standard GraphQL request body and resolves the query
        against the in-memory task store.

        Args:
            req: GraphQL request body with query, optional variables and operationName.
            request: FastAPI request (provides access to app state).

        Returns:
            GraphQL response with ``data`` or ``errors``.
      operationId: graphql_endpoint_graphql_post
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/GraphQLRequest"
        required: true
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
                title: Response Graphql Endpoint Graphql Post
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /graduation/status:
    get:
      tags:
        - graduation
      summary: Graduation Status
      description: |-
        Return graduation stage and metrics for all tracked sessions.

        Returns:
            JSON with ``sessions`` list and ``total`` count.
      operationId: graduation_status_graduation_status_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /graduation/config/policies:
    get:
      tags:
        - graduation
      summary: Get Policies
      description: |-
        Return the current graduation stage policies.

        Returns:
            JSON mapping stage names to policy thresholds.
      operationId: get_policies_graduation_config_policies_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /graduation/{session_id}:
    get:
      tags:
        - graduation
      summary: Session Graduation
      description: |-
        Return graduation state for a specific session.

        Args:
            session_id: The session identifier to look up.

        Returns:
            JSON with stage, metrics, promotion log, and graduation readiness.

        Raises:
            HTTPException: 404 when no record exists for *session_id*.
      operationId: session_graduation_graduation__session_id__get
      parameters:
        - name: session_id
          in: path
          required: true
          schema:
            type: string
            title: Session Id
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
        "404":
          description: No graduation record for session
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /graduation/{session_id}/promote:
    post:
      tags:
        - graduation
      summary: Promote Session
      description: |-
        Manually promote a session to the next graduation stage.

        Args:
            session_id: Session to promote.
            body: Promotion reason and who initiated it.

        Returns:
            JSON with ``from_stage``, ``to_stage``, and ``promoted: true``.

        Raises:
            HTTPException: 404 when no record exists.
            HTTPException: 409 when already at the terminal (autonomous) stage.
      operationId: promote_session_graduation__session_id__promote_post
      parameters:
        - name: session_id
          in: path
          required: true
          schema:
            type: string
            title: Session Id
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PromoteRequest"
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
        "404":
          description: No graduation record for session
        "409":
          description: Already at terminal stage
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /graduation/{session_id}/record-event:
    post:
      tags:
        - graduation
      summary: Record Task Event
      description: |-
        Record a task completion or failure for graduation metric tracking.

        The orchestrator or CLI calls this after each task completes/fails so
        the graduation framework can accumulate per-stage metrics and determine
        when the session qualifies for the next stage.

        Args:
            session_id: The session that executed the task.
            body: Task event details.

        Returns:
            JSON with updated stage, metrics, and graduation readiness.

        Raises:
            HTTPException: 422 when *initial_stage* is not a valid stage name.
      operationId: record_task_event_graduation__session_id__record_event_post
      parameters:
        - name: session_id
          in: path
          required: true
          schema:
            type: string
            title: Session Id
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/RecordEventRequest"
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
        "422":
          description: Invalid graduation stage
  /handoff/{token}:
    get:
      summary: Claim Handoff Token
      description: |-
        Claim a handoff token and return the session identity + tail.

        Args:
            token: Opaque urlsafe token presented by the dashboard.
            request: FastAPI request (used to resolve the workdir).

        Returns:
            JSON envelope with ``session_id``, ``task_id``,
            ``source_surface``, ``claimed_at``, ``note`` and ``tail`` (a
            list of recent stream entries).

        Raises:
            HTTPException: ``404`` for unknown tokens, ``410`` for expired
            or already-claimed tokens.
      operationId: claim_handoff_token_handoff__token__get
      parameters:
        - name: token
          in: path
          required: true
          schema:
            type: string
            title: Token
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /team:
    get:
      summary: Team Summary
      description: |-
        Return a summary of the current team state.

        Includes total members, active/finished counts, role distribution,
        and full per-member metadata.
      operationId: team_summary_team_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /team/active:
    get:
      summary: Team Active
      description: Return only active team members.
      operationId: team_active_team_active_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /team/{agent_id}:
    get:
      summary: Team Member
      description: |-
        Return metadata for a single team member.

        Returns 404 if the agent is not in the team roster.
      operationId: team_member_team__agent_id__get
      parameters:
        - name: agent_id
          in: path
          required: true
          schema:
            type: string
            title: Agent Id
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /metrics/provider-latency:
    get:
      summary: Provider Latency Current
      description: |-
        Return current p50/p95/p99 latency percentiles for all tracked providers.

        Each entry in the response includes a ``baseline_p99_ms`` derived from the
        past 7 days of data. When ``p99_ms`` exceeds ``baseline_p99_ms x 2``, the
        entry carries ``"degraded": true``.
      operationId: provider_latency_current_metrics_provider_latency_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /metrics/provider-latency/history:
    get:
      summary: Provider Latency History
      description: |-
        Return raw latency samples for time-series charting.

        Each sample has: ``timestamp``, ``provider``, ``model``, ``latency_ms``.
        Samples are ordered chronologically. Use ``hours`` to control the lookback
        window (default 24h, max 7 days).
      operationId: provider_latency_history_metrics_provider_latency_history_get
      parameters:
        - name: provider
          in: query
          required: false
          schema:
            anyOf:
              - type: string
              - type: "null"
            description: Filter by provider name
            title: Provider
          description: Filter by provider name
        - name: model
          in: query
          required: false
          schema:
            anyOf:
              - type: string
              - type: "null"
            description: Filter by model identifier
            title: Model
          description: Filter by model identifier
        - name: hours
          in: query
          required: false
          schema:
            type: integer
            maximum: 168
            minimum: 1
            description: Hours of history to return (1-168)
            default: 24
            title: Hours
          description: Hours of history to return (1-168)
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /metrics/predictions:
    get:
      summary: Get Predictions
      description: |-
        Evaluate all predictive forecasts and return active alerts.

        Checks three forecast dimensions:

        - **Budget exhaustion**: At current spend velocity, when will the
          budget cap be reached?
        - **Completion rate decline**: Is the task completion rate trending
          downward, indicating the run will take longer than expected?
        - **Run duration overrun**: Based on current throughput, will the run
          exceed the configured time window?

        Use ``budget_cap`` to enable the budget forecast. The run duration
        forecast requires at least one completed task.

        Both numeric parameters are echoed back in the response body, so both
        refuse non-finite values with a 422 instead of admitting them: a range
        bound alone does not exclude them (``inf >= 0.0`` is true, and every
        comparison against ``NaN`` is false), and the JSON renderer cannot
        serialise either one.

        The budget forecast is scoped to ``tenant_id``: the spend series it is
        built from is narrowed to cost points recorded for the caller's tenant,
        the same way the rest of the cost surface is (see
        ``load_cost_history``).  Cost points written before per-tenant
        attribution existed are treated as the default tenant's spend, so a
        legacy single-tenant install keeps its existing numbers.

        Returns a list of ``alerts`` ordered by severity (critical first).
        Each alert has: ``kind``, ``severity``, ``message``,
        ``minutes_until_impact``, ``confidence``.
      operationId: get_predictions_metrics_predictions_get
      parameters:
        - name: budget_cap
          in: query
          required: false
          schema:
            type: number
            minimum: 0
            description: Budget ceiling in USD (0 = skip budget forecast)
            default: 0
            title: Budget Cap
          description: Budget ceiling in USD (0 = skip budget forecast)
        - name: window_hours
          in: query
          required: false
          schema:
            type: number
            maximum: 72
            minimum: 0.1
            description: Configured run window in hours (default 4)
            default: 4
            title: Window Hours
          description: Configured run window in hours (default 4)
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /sessions/{session_id}/peek:
    get:
      summary: Peek Session
      description: |-
        Return the recent stream-tail entries for ``session_id``.

        Args:
            session_id: Bernstein session whose tail to read.
            request: FastAPI request - used to resolve the workdir and the
                ``tail`` query argument.

        Returns:
            JSON envelope with ``session_id`` plus a ``tail`` list of
            ``{ts, surface, text}`` entries in chronological order. An
            empty list signals "buffer not initialised yet" rather than an
            error so the polling page renders a blank pane while it waits.
      operationId: peek_session_sessions__session_id__peek_get
      parameters:
        - name: session_id
          in: path
          required: true
          schema:
            type: string
            title: Session Id
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /sessions/{session_id}/send:
    post:
      summary: Send To Session
      description: |-
        Pipe one line of operator input into ``session_id``'s stdin.

        The send-bar tile on the dashboard POSTs ``{"text": "..."}`` here; we
        forward through :func:`bernstein.core.agents.agent_ipc.send_message`,
        which writes the line into the agent's registered stdin pipe.

        Args:
            session_id: Slug-shaped session id; must pass the same validator
                as the peek endpoint.
            request: FastAPI request (unused beyond routing-level checks but
                present so the bearer-auth middleware sees the same shape as
                our other mutating routes).
            payload: JSON body with a single ``text`` field. Empty / missing
                text is rejected with ``400``; oversize payloads above
                :data:`MAX_SEND_BYTES` are rejected with ``413``.

        Returns:
            JSON envelope with ``session_id`` and ``delivered`` (``True`` if
            the line reached a registered stdin pipe, ``False`` if no pipe
            is registered for this session).  The 200/404 split lets the
            front-end keep the input enabled but warn the operator when the
            agent has no live pipe yet.
      operationId: send_to_session_sessions__session_id__send_post
      parameters:
        - name: session_id
          in: path
          required: true
          schema:
            type: string
            title: Session Id
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties:
                type: string
              title: Payload
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /orchestrator/holds:
    get:
      tags:
        - orchestrator-holds
      summary: Get Holds
      description: List all currently active (non-expired) holds.
      operationId: get_holds_orchestrator_holds_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HoldListResponse"
    post:
      tags:
        - orchestrator-holds
      summary: Create Hold
      description: Acquire a new hold, preventing orchestrator self-stop while active.
      operationId: create_hold_orchestrator_holds_post
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/HoldCreateRequest"
        required: true
      responses:
        "200":
          description: Hold acquired
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HoldResponse"
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /orchestrator/holds/{hold_id}:
    delete:
      tags:
        - orchestrator-holds
      summary: Delete Hold
      description: Release a hold by id.
      operationId: delete_hold_orchestrator_holds__hold_id__delete
      parameters:
        - name: hold_id
          in: path
          required: true
          schema:
            type: string
            title: Hold Id
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                type: object
                additionalProperties:
                  type: boolean
                title: Response Delete Hold Orchestrator Holds  Hold Id  Delete
        "404":
          description: Hold not found (already released or expired)
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /orchestrator/holds/{hold_id}/renew:
    post:
      tags:
        - orchestrator-holds
      summary: Renew Hold Endpoint
      description: Heartbeat-renew a hold, extending its expiry by another grace window.
      operationId: renew_hold_endpoint_orchestrator_holds__hold_id__renew_post
      parameters:
        - name: hold_id
          in: path
          required: true
          schema:
            type: string
            title: Hold Id
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HoldResponse"
        "404":
          description: Hold not found (never existed, released, or already expired)
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /review-board/runs:
    get:
      summary: Review Board Runs
      description: List run ids that have a journal to project, newest first.
      operationId: review_board_runs_review_board_runs_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /review-board/runs/{run_id}:
    get:
      summary: Review Board Projection
      description: |-
        Serve the board projection receipt for ``run_id``.

        The response is a deterministic function of the run's journal file:
        the same journal bytes serve the same ``board`` and
        ``projection_hash`` from any server, so a reviewer can cross-check two
        operators (or the API against a local ``project_run`` fold) byte for
        byte. ``journal_verified=false`` marks a chain that no longer
        recomputes - the board is still rendered but must not be trusted.
      operationId: review_board_projection_review_board_runs__run_id__get
      parameters:
        - name: run_id
          in: path
          required: true
          schema:
            type: string
            title: Run Id
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /review-board/runs/{run_id}/evidence/{task_id}:
    get:
      summary: Review Board Evidence
      description: |-
        Serve the sealed evidence bundle for a board card.

        The bundle is the #2362 proof-of-done artifact: content-addressed
        items, the gate verdict, the producing signature, and the audit-chain
        entry hash. ``bundle_hash`` is recomputed from the canonical binding
        bytes on every read so the drawer always shows the bundle's current
        identity.
      operationId: review_board_evidence_review_board_runs__run_id__evidence__task_id__get
      parameters:
        - name: run_id
          in: path
          required: true
          schema:
            type: string
            title: Run Id
        - name: task_id
          in: path
          required: true
          schema:
            type: string
            title: Task Id
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /review-board/runs/{run_id}/diff/{task_id}:
    get:
      summary: Review Board Diff
      description: |-
        Serve the captured task diff for the card drawer's diff viewer.

        The diff bytes were captured beside the run journal at completion time
        (``task_diff_captured``), so they are exactly what executed and are
        available against a detached run - no live ``git`` at review time. The
        served bytes are re-hashed and cross-checked against the journal-chained
        capture hash: ``verified`` is ``true`` only when the diff a reviewer folds
        open equals the diff that was captured and the chain still recomputes.
      operationId: review_board_diff_review_board_runs__run_id__diff__task_id__get
      parameters:
        - name: run_id
          in: path
          required: true
          schema:
            type: string
            title: Run Id
        - name: task_id
          in: path
          required: true
          schema:
            type: string
            title: Task Id
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /dashboard/review-board/runs/{run_id}/tasks/{task_id}/review:
    post:
      summary: Review Board Action
      description: |-
        Record an operator board decision as a chained, signed receipt.

        The scope gate is enforced upstream by the dashboard-auth middleware
        (operator scope required for this write); the acting principal arrives on
        ``request.state.dashboard_principal``. The decision row is appended via
        ``EventJournal.resume`` so it chains onto the verified journal tail and
        fails closed on a poisoned chain (``409``).
      operationId: review_board_action_dashboard_review_board_runs__run_id__tasks__task_id__review_post
      parameters:
        - name: run_id
          in: path
          required: true
          schema:
            type: string
            title: Run Id
        - name: task_id
          in: path
          required: true
          schema:
            type: string
            title: Task Id
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ReviewActionRequest"
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /dashboard/review-board:
    get:
      summary: Review Board Page
      description: |-
        Serve the review-board page.

        The page is a pure consumer of the projection endpoints above plus the
        existing ``/events`` SSE stream; it holds no state of its own, so
        reloading it (or opening it on a second machine against the same
        journal) renders the identical board.
      operationId: review_board_page_dashboard_review_board_get
      responses:
        "200":
          description: Successful Response
          content:
            text/html:
              schema:
                type: string
  /artifacts:
    get:
      summary: List Artifacts
      description: Return every artifact key the local lineage spines carry.
      operationId: list_artifacts_artifacts_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /artifacts/health:
    get:
      summary: Artifact Health
      description: |-
        Return the canonical health verdict for ``?uri=``.

        Query parameters:

        * ``uri`` (required) - the artifact key.
        * ``at`` - evaluation instant; defaults to the wall clock. Pin it to
          reproduce a verdict byte-for-byte against the CLI.
        * ``cadence_seconds`` - declared refresh cadence; omitted means the cadence
          leg reports ``not_applicable``.

        The body is the exact string the CLI prints for the same state and instant,
        byte for byte. The status is always 200: the verdict is the payload, and a
        red artifact is a successfully computed answer, not a failed request.
      operationId: artifact_health_artifacts_health_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /artifacts/log:
    get:
      summary: Artifact Log Route
      description: |-
        Return productions of ``?uri=``, newest first (the attribution log).

        Recorded attempts -- tasks that declared this artifact and did not deliver it
        -- travel in the same document under ``attempts`` (issue #2559), so a
        consumer cannot see the productions without also seeing what tried and
        failed. Byte-identical to what the CLI prints for the same state.
      operationId: artifact_log_route_artifacts_log_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /missions:
    get:
      summary: Missions List
      description: List mission ids that have a ledger to project, newest first.
      operationId: missions_list_missions_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /missions/{mission_id}:
    get:
      summary: Mission Projection
      description: |-
        Serve the mission projection receipt for ``mission_id``.

        The response is a deterministic function of the mission's ledger file: the
        same ledger bytes serve the same ``status`` and ``mission_status_hash`` from
        any server, so two operators cross-check byte for byte.
        ``ledger_verified=false`` (with ``overall=unverified``) marks a chain that no
        longer recomputes -- the timeline still renders, but the screen must show the
        unverified banner instead of trusting the state.
      operationId: mission_projection_missions__mission_id__get
      parameters:
        - name: mission_id
          in: path
          required: true
          schema:
            type: string
            title: Mission Id
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /missions/{mission_id}/digest:
    get:
      summary: Mission Digest
      description: |-
        Serve the canonical daily progress digest for a fire instant.

        Read-only: the digest is recomputed from the ledger as a pure fold, so the
        endpoint never writes to the chain. The payload carries the ``digest_hash``,
        the ``receipt_id`` (the per-fire delivery idempotency key), and the verbatim
        ``message`` the digest projects to -- the exact bytes a chat delivery posts,
        so a caller can cross-check a posted message against this projection.
      operationId: mission_digest_missions__mission_id__digest_get
      parameters:
        - name: mission_id
          in: path
          required: true
          schema:
            type: string
            title: Mission Id
        - name: fire_time
          in: query
          required: true
          schema:
            type: integer
            description: Integer Unix epoch of the canonical fire instant.
            title: Fire Time
          description: Integer Unix epoch of the canonical fire instant.
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /missions/{mission_id}/evidence/{task_id}:
    get:
      summary: Mission Evidence
      description: |-
        Serve the sealed evidence bundle behind a timeline element's provenance link.

        ``bundle_hash`` is recomputed from the canonical binding bytes on every read,
        so the drawer always shows the bundle's current identity -- and a bundle that
        no longer matches the hash a phase receipt bound projects that phase as
        unverified in the mission projection above.
      operationId: mission_evidence_missions__mission_id__evidence__task_id__get
      parameters:
        - name: mission_id
          in: path
          required: true
          schema:
            type: string
            title: Mission Id
        - name: task_id
          in: path
          required: true
          schema:
            type: string
            title: Task Id
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /api/v1/tasks/search:
    get:
      summary: Search Tasks
      description: |-
        Search tasks with pagination, sorting, and filtering.

        Query params:
            page: Page number (1-based, default 1).
            per_page: Items per page (1-100, default 20).
            sort: Sort field (created_at, priority, title, role, status).
            order: Sort order (asc, desc; default desc).
            status: Filter by task status.
            role: Filter by task role.
            assigned_agent: Filter by assigned agent.
      operationId: search_tasks_api_v1_tasks_search_get
      parameters:
        - name: page
          in: query
          required: false
          schema:
            type: integer
            default: 1
            title: Page
        - name: per_page
          in: query
          required: false
          schema:
            type: integer
            default: 20
            title: Per Page
        - name: sort
          in: query
          required: false
          schema:
            type: string
            default: created_at
            title: Sort
        - name: order
          in: query
          required: false
          schema:
            type: string
            default: desc
            title: Order
        - name: status
          in: query
          required: false
          schema:
            anyOf:
              - type: string
              - type: "null"
            title: Status
        - name: role
          in: query
          required: false
          schema:
            anyOf:
              - type: string
              - type: "null"
            title: Role
        - name: assigned_agent
          in: query
          required: false
          schema:
            anyOf:
              - type: string
              - type: "null"
            title: Assigned Agent
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PaginatedSearchResponse"
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /api/v1/agents:
    get:
      summary: List Agents
      description: |-
        Return a flat list of agent sessions for the web GUI grid.

        When ``TaskStore.agents`` is empty (e.g. only mock adapters spawned and
        they never heartbeat) we fall back to synthesising one entry per
        claimed/in-progress task, marked with ``"synthetic": true``. That keeps
        the GUI grid populated during demos and avoids the dreaded "0 sessions"
        empty state when work is obviously in flight.

        Both branches render the id and title of the task a session is on, so
        both narrow to the caller's tenant scope: the live sessions carry no
        tenant of their own and are placed by the task they name, while the
        synthesised entries are built from a scoped read in the first place.
      operationId: list_agents_api_v1_agents_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                items:
                  additionalProperties: true
                  type: object
                type: array
                title: Response List Agents Api V1 Agents Get
  /api/v1/agents/comparison:
    get:
      tags:
        - agent-comparison
      summary: Get Agent Comparison
      description: |-
        Return per-(adapter, model) performance comparison metrics.

        Aggregates data from all agent sessions in the current run:
        success rate, average completion time, cost per task, and
        quality gate pass rate.

        Returns:
            JSON list of :class:`AgentMetrics` objects sorted by adapter
            then model.
      operationId: get_agent_comparison_api_v1_agents_comparison_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                items:
                  $ref: "#/components/schemas/AgentMetrics"
                type: array
                title: Response Get Agent Comparison Api V1 Agents Comparison Get
  /api/v1/agents/{session_id}/logs:
    get:
      summary: Agent Logs
      description: |-
        Return log file content for a session.

        Args:
            session_id: Agent session ID.
            tail_bytes: If > 0, return only the last N bytes of the log.
      operationId: agent_logs_api_v1_agents__session_id__logs_get
      parameters:
        - name: session_id
          in: path
          required: true
          schema:
            type: string
            title: Session Id
        - name: tail_bytes
          in: query
          required: false
          schema:
            type: integer
            default: 0
            title: Tail Bytes
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AgentLogsResponse"
        "404":
          description: No log file for session
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /api/v1/agents/{session_id}/kill:
    post:
      summary: Agent Kill
      description: |-
        Request that an agent session be killed.

        Writes a ``.kill`` signal file that the orchestrator picks up on
        its next tick.
      operationId: agent_kill_api_v1_agents__session_id__kill_post
      parameters:
        - name: session_id
          in: path
          required: true
          schema:
            type: string
            title: Session Id
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AgentKillResponse"
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /api/v1/agents/{session_id}/stream:
    get:
      summary: Agent Stream
      description: SSE stream of live log output for a session.
      operationId: agent_stream_api_v1_agents__session_id__stream_get
      parameters:
        - name: session_id
          in: path
          required: true
          schema:
            type: string
            title: Session Id
      responses:
        "200":
          description: Server-Sent Events stream. The response body does not terminate.
          content:
            text/event-stream:
              schema:
                type: string
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /api/v1/auth/providers:
    get:
      tags:
        - authentication
      summary: Auth Providers
      description: List available authentication providers.
      operationId: auth_providers_api_v1_auth_providers_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AuthProvidersResponse"
  /api/v1/auth/login:
    get:
      tags:
        - authentication
      summary: Login
      description: Initiate SSO login. Redirects to IdP.
      operationId: login_api_v1_auth_login_get
      parameters:
        - name: provider
          in: query
          required: false
          schema:
            $ref: "#/components/schemas/LoginProvider"
            default: oidc
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
        "400":
          description: Authentication provider not enabled
        "404":
          description: SSO authentication is not configured on this server
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /api/v1/auth/oidc/callback:
    get:
      tags:
        - authentication
      summary: Oidc Callback
      description: OIDC authorization code callback.
      operationId: oidc_callback_api_v1_auth_oidc_callback_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
        "400":
          description: Missing or invalid authorization code or state
        "404":
          description: SSO authentication is not configured on this server
  /api/v1/auth/saml/acs:
    post:
      tags:
        - authentication
      summary: Saml Acs
      description: |-
        SAML Assertion Consumer Service (ACS) endpoint.

        Receives the SAML Response from the IdP via HTTP-POST binding.
      operationId: saml_acs_api_v1_auth_saml_acs_post
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
        "400":
          description: Missing SAMLResponse
        "404":
          description: SSO authentication is not configured on this server
  /api/v1/auth/saml/metadata:
    get:
      tags:
        - authentication
      summary: Saml Metadata
      description: SAML SP metadata endpoint for IdP configuration.
      operationId: saml_metadata_api_v1_auth_saml_metadata_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
        "404":
          description: SSO authentication is not configured on this server
  /api/v1/auth/cli/device:
    post:
      tags:
        - authentication
      summary: Device Code Request
      description: |-
        Initiate device authorization flow for CLI login.

        The CLI calls this to get a device_code and user_code.
        The user enters the user_code in the web dashboard after SSO login
        to authorize the CLI session.
      operationId: device_code_request_api_v1_auth_cli_device_post
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/DeviceCodeRequest"
        required: true
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DeviceCodeResponse"
        "404":
          description: SSO authentication is not configured on this server
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /api/v1/auth/cli/token:
    post:
      tags:
        - authentication
      summary: Device Token Poll
      description: |-
        Poll for device authorization status.

        Returns the access token once the user has authorized the device code.
      operationId: device_token_poll_api_v1_auth_cli_token_post
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/DevicePollRequest"
        required: true
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DevicePollResponse"
        "404":
          description: SSO authentication is not configured on this server
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /api/v1/auth/cli/authorize:
    post:
      tags:
        - authentication
      summary: Device Authorize
      description: |-
        Authorize a device code (called from web dashboard after SSO login).

        Requires an authenticated user session.
      operationId: device_authorize_api_v1_auth_cli_authorize_post
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/DeviceAuthorizeRequest"
        required: true
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
        "400":
          description: Invalid or expired user code
        "401":
          description: Authentication required
        "404":
          description: SSO authentication is not configured on this server
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /api/v1/auth/me:
    get:
      tags:
        - authentication
      summary: Get Profile
      description: Get the current authenticated user's profile.
      operationId: get_profile_api_v1_auth_me_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/UserProfileResponse"
        "401":
          description: Authentication required
  /api/v1/auth/logout:
    post:
      tags:
        - authentication
      summary: Logout
      description: Logout and revoke the current session.
      operationId: logout_api_v1_auth_logout_post
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
        "404":
          description: SSO authentication is not configured on this server
  /api/v1/auth/group-mappings:
    get:
      tags:
        - authentication
      summary: Get Group Mappings
      description: Get current SSO group → role mappings.
      operationId: get_group_mappings_api_v1_auth_group_mappings_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/GroupMappingsResponse"
        "404":
          description: SSO authentication is not configured on this server
    put:
      tags:
        - authentication
      summary: Update Group Mappings
      description: Update SSO group → role mappings (admin only).
      operationId: update_group_mappings_api_v1_auth_group_mappings_put
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/GroupMappingsUpdateRequest"
        required: true
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
        "400":
          description: Invalid role value
        "401":
          description: Authentication required
        "403":
          description: Admin role required
        "404":
          description: SSO authentication is not configured on this server
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /api/v1/auth/users:
    get:
      tags:
        - authentication
      summary: List Users
      description: List all users (admin only).
      operationId: list_users_api_v1_auth_users_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
        "401":
          description: Authentication required
        "403":
          description: Admin role required
        "404":
          description: SSO authentication is not configured on this server
  /api/v1/tasks:
    post:
      summary: Create Task
      description: Create a new task.
      operationId: create_task_api_v1_tasks_post
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/TaskCreate"
      responses:
        "201":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TaskResponse"
        "400":
          description: Blocked by pre-create hook
        "403":
          description: Tenant access denied
        "404":
          description: Tenant not found
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
        "429":
          description: Tenant task quota exceeded
    get:
      summary: List Tasks
      description: |-
        List tasks, optionally filtered by status, cell_id, and/or claim owner.

        When ``limit`` or ``offset`` query params are provided the response is a
        paginated envelope (``{tasks, total, limit, offset}``).  Without them,
        the legacy flat list is returned for backward compatibility, capped at
        ``_LIST_TASKS_HARD_CAP`` items and accompanied by a ``Deprecation``
        header asking callers to pass explicit pagination.

        Args:
            request: FastAPI request.
            status: If provided, only tasks with this status are returned.
            cell_id: If provided, only tasks in this cell are returned.
            tenant: Tenant scope override.
            claimed_by_session: If provided, only tasks claimed by this parent
                orchestrator session are returned.
            limit: Maximum number of tasks to return (max 500).  Triggers
                paginated response when present.
            offset: Number of tasks to skip.  Triggers paginated response
                when present.

        Returns:
            Paginated response **or** plain list of TaskResponse dicts (capped
            at ``_LIST_TASKS_HARD_CAP``).
      operationId: list_tasks_api_v1_tasks_get
      parameters:
        - name: status
          in: query
          required: false
          schema:
            anyOf:
              - type: string
              - type: "null"
            title: Status
        - name: cell_id
          in: query
          required: false
          schema:
            anyOf:
              - type: string
              - type: "null"
            title: Cell Id
        - name: tenant
          in: query
          required: false
          schema:
            anyOf:
              - type: string
              - type: "null"
            title: Tenant
        - name: claimed_by_session
          in: query
          required: false
          schema:
            anyOf:
              - type: string
              - type: "null"
            title: Claimed By Session
        - name: parent_session_id
          in: query
          required: false
          schema:
            anyOf:
              - type: string
              - type: "null"
            title: Parent Session Id
        - name: limit
          in: query
          required: false
          schema:
            anyOf:
              - type: integer
              - type: "null"
            title: Limit
        - name: offset
          in: query
          required: false
          schema:
            anyOf:
              - type: integer
              - type: "null"
            title: Offset
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
        "403":
          description: Tenant scope access denied
        "404":
          description: Resource not found or tenant mismatch
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /api/v1/tasks/batch:
    post:
      summary: Create Tasks Batch
      description: Create multiple tasks atomically with title dedup.
      operationId: create_tasks_batch_api_v1_tasks_batch_post
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/BatchCreateRequest"
        required: true
      responses:
        "201":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/BatchCreateResponse"
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
        "503":
          description: Server is draining
  /api/v1/tasks/self-create:
    post:
      summary: Self Create Subtask
      description: |-
        Create a subtask linked to a parent task.

        Agents call this to decompose work during execution.  The parent
        task is automatically transitioned to ``WAITING_FOR_SUBTASKS`` on
        the first subtask creation (if it is not already in that state).
      operationId: self_create_subtask_api_v1_tasks_self_create_post
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/TaskSelfCreate"
        required: true
      responses:
        "201":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TaskResponse"
        "404":
          description: Parent task not found
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /api/v1/tasks/next/{role}:
    get:
      summary: Next Task
      description: |-
        Claim the next available task for *role*.

        Pass ``claimed_by_session`` as a query param to record which parent
        orchestrator session owns the claim.

        Pass ``parent_session_id`` to restrict claiming to tasks that were
        created under that coordinator session.  Workers belonging to a
        coordinator should always pass their coordinator's session ID here
        to avoid stealing tasks from other namespaces.
      operationId: next_task_api_v1_tasks_next__role__get
      parameters:
        - name: role
          in: path
          required: true
          schema:
            type: string
            title: Role
        - name: claimed_by_session
          in: query
          required: false
          schema:
            anyOf:
              - type: string
              - type: "null"
            title: Claimed By Session
        - name: parent_session_id
          in: query
          required: false
          schema:
            anyOf:
              - type: string
              - type: "null"
            title: Parent Session Id
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TaskResponse"
        "403":
          description: Tenant scope access denied
        "404":
          description: Resource not found or tenant mismatch
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
        "503":
          description: Server is draining
  /api/v1/tasks/claim-batch:
    post:
      summary: Claim Batch
      description: Atomically claim multiple tasks by ID for an agent.
      operationId: claim_batch_api_v1_tasks_claim_batch_post
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/BatchClaimRequest"
        required: true
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/BatchClaimResponse"
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
        "503":
          description: Server is draining
  /api/v1/tasks/{task_id}/claim:
    post:
      summary: Claim Task
      description: |-
        Claim a specific task by ID.

        Pass ``expected_version`` as a query param for optimistic locking
        (CAS). If the task's version doesn't match, returns 409 Conflict.

        Pass ``claimed_by_session`` to record which parent orchestrator
        session owns this claim.
      operationId: claim_task_api_v1_tasks__task_id__claim_post
      parameters:
        - name: task_id
          in: path
          required: true
          schema:
            type: string
            title: Task Id
        - name: expected_version
          in: query
          required: false
          schema:
            anyOf:
              - type: integer
              - type: "null"
            title: Expected Version
        - name: claimed_by_session
          in: query
          required: false
          schema:
            anyOf:
              - type: string
              - type: "null"
            title: Claimed By Session
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TaskResponse"
        "404":
          description: Task not found
        "409":
          description: Version conflict or invalid state
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
        "503":
          description: Server is draining
  /api/v1/tasks/{task_id}/complete:
    post:
      summary: Complete Task
      description: |-
        Mark a task as done (or refused) from a worker terminal payload.

        Structured payloads (``body.payload`` or a JSON object embedded in
        ``result_summary``) are validated against the worker completion
        contract (#2244): an invalid payload is a typed ``contract_violation``
        failure carrying the schema error path, and a validated refusal lands
        the task in the terminal REFUSED state instead of DONE. Legacy prose
        summaries are accepted unchanged.

        If ``result_summary`` is empty the task is auto-transitioned to
        ``FAILED`` with ``reason='completion missing summary'`` and
        a 422 is returned with the failed task payload so the client knows the
        slot was released.
      operationId: complete_task_api_v1_tasks__task_id__complete_post
      parameters:
        - name: task_id
          in: path
          required: true
          schema:
            type: string
            title: Task Id
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/TaskCompleteRequest"
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TaskResponse"
        "404":
          description: Task not found
        "409":
          description: Invalid state transition
        "422":
          description: Empty result_summary or contract violation - task auto-failed
  /api/v1/tasks/{task_id}/wait-for-subtasks:
    post:
      summary: Wait For Subtasks
      description: Mark a parent task as waiting until its generated subtasks complete.
      operationId: wait_for_subtasks_api_v1_tasks__task_id__wait_for_subtasks_post
      parameters:
        - name: task_id
          in: path
          required: true
          schema:
            type: string
            title: Task Id
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/TaskWaitForSubtasksRequest"
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TaskResponse"
        "404":
          description: Task not found
        "409":
          description: Invalid state transition
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /api/v1/tasks/{task_id}/fail:
    post:
      summary: Fail Task
      description: Mark a task as failed.
      operationId: fail_task_api_v1_tasks__task_id__fail_post
      parameters:
        - name: task_id
          in: path
          required: true
          schema:
            type: string
            title: Task Id
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/TaskFailRequest"
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TaskResponse"
        "404":
          description: Task not found
        "409":
          description: Invalid state transition
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /api/v1/tasks/{task_id}/release:
    post:
      summary: Release Task
      description: |-
        Release a claimed task back to the open pool without failing it.

        A cluster worker that claims a task but cannot start its agent (e.g. the
        workspace is not a usable git checkout, or the adapter spawn fails) must
        return the task to the pool so another node can pick it up, rather than
        stranding it in ``claimed`` with no live agent (#3018). Distinct from
        ``/fail`` (terminal FAILED) and ``/reopen`` (DONE -> OPEN): the task
        transitions CLAIMED/IN_PROGRESS -> OPEN and is immediately claimable again.
      operationId: release_task_api_v1_tasks__task_id__release_post
      parameters:
        - name: task_id
          in: path
          required: true
          schema:
            type: string
            title: Task Id
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/TaskReleaseRequest"
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TaskResponse"
        "404":
          description: Task not found
        "409":
          description: Invalid state transition
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /api/v1/tasks/{task_id}/reopen:
    post:
      summary: Reopen Task
      description: |-
        Reopen a done task that failed janitor verification (same task id).

        Transitions DONE -> OPEN and increments
        ``metadata['janitor_reopen_count']``. The orchestrator enforces the
        reopen budget; this endpoint only performs the state transition.
      operationId: reopen_task_api_v1_tasks__task_id__reopen_post
      parameters:
        - name: task_id
          in: path
          required: true
          schema:
            type: string
            title: Task Id
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/TaskReopenRequest"
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TaskResponse"
        "404":
          description: Task not found
        "409":
          description: Invalid state transition
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /api/v1/tasks/{task_id}/close:
    post:
      summary: Close Task
      description: Mark a verified task as closed (terminal success state).
      operationId: close_task_api_v1_tasks__task_id__close_post
      parameters:
        - name: task_id
          in: path
          required: true
          schema:
            type: string
            title: Task Id
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TaskResponse"
        "404":
          description: Task not found
        "409":
          description: Invalid state transition
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /api/v1/tasks/{task_id}/cancel:
    post:
      summary: Cancel Task
      description: |-
        Cancel a task and cascade to all of its descendant subtasks.

        Walks the subtask tree (``parent_task_id`` references) via
        ``TaskStore.cancel_cascade`` so that children are not left running
        after the parent is aborted.  Returns the root task.
      operationId: cancel_task_api_v1_tasks__task_id__cancel_post
      parameters:
        - name: task_id
          in: path
          required: true
          schema:
            type: string
            title: Task Id
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/TaskCancelRequest"
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TaskResponse"
        "404":
          description: Task not found
        "409":
          description: Invalid state transition
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /api/v1/tasks/{task_id}/block:
    post:
      summary: Block Task
      description: Mark a task as blocked -- requires human intervention to unblock.
      operationId: block_task_api_v1_tasks__task_id__block_post
      parameters:
        - name: task_id
          in: path
          required: true
          schema:
            type: string
            title: Task Id
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/TaskBlockRequest"
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TaskResponse"
        "404":
          description: Task not found
        "409":
          description: Invalid state transition
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /api/v1/tasks/{task_id}/progress:
    post:
      summary: Progress Task
      description: |-
        Append an intermediate progress update to a task.

        Also stores a progress snapshot for stall detection when snapshot
        fields (files_changed, tests_passing, errors) are provided.
      operationId: progress_task_api_v1_tasks__task_id__progress_post
      parameters:
        - name: task_id
          in: path
          required: true
          schema:
            type: string
            title: Task Id
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/TaskProgressRequest"
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TaskResponse"
        "404":
          description: Task not found
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
    get:
      summary: Get Task Progress
      description: |-
        Return the chain-computed progress vector for a task.

        The ledger read is resolved from the task's own authoritative run id, never
        from a client-supplied parameter, so the vector cannot be steered by pairing
        this task's journal with an arbitrary run's ledger.
      operationId: get_task_progress_api_v1_tasks__task_id__progress_get
      parameters:
        - name: task_id
          in: path
          required: true
          schema:
            type: string
            title: Task Id
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TaskProgressResponse"
        "404":
          description: Task not found
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /api/v1/tasks/{task_id}/partial-merge:
    post:
      summary: Partial Merge Task
      description: |-
        Incrementally merge specific committed files from the agent's branch into main.

        Allows a long-running agent to push a completed subset of its work (e.g.
        the first 5 of 10 test files) while still writing the rest.  Reduces
        wall-clock time by making partial results available downstream earlier.

        Only files that are already **committed** in the agent's worktree branch
        (``agent/<session_id>``) are merged.  Uncommitted files are returned in
        ``uncommitted_files`` so the caller knows to commit them in the worktree
        first.  Files that were already merged by a prior call are skipped and
        returned in ``skipped_already_merged``.

        Requires the task to be ``in_progress`` with a ``claimed_by_session`` set.
      operationId: partial_merge_task_api_v1_tasks__task_id__partial_merge_post
      parameters:
        - name: task_id
          in: path
          required: true
          schema:
            type: string
            title: Task Id
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PartialMergeRequest"
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PartialMergeResponse"
        "404":
          description: Task not found
        "409":
          description: Task not in progress or has no active session
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
    get:
      summary: Get Partial Merge State
      description: |-
        Return the cumulative incremental-merge state for a task's active session.

        Useful for monitoring how much of an in-progress task's output has already
        been merged into the main branch.
      operationId: get_partial_merge_state_api_v1_tasks__task_id__partial_merge_get
      parameters:
        - name: task_id
          in: path
          required: true
          schema:
            type: string
            title: Task Id
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PartialMergeResponse"
        "404":
          description: Task not found
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /api/v1/tasks/{task_id}/snapshots:
    get:
      summary: Get Task Snapshots
      description: Return stored progress snapshots for a task (oldest-first, up to 10).
      operationId: get_task_snapshots_api_v1_tasks__task_id__snapshots_get
      parameters:
        - name: task_id
          in: path
          required: true
          schema:
            type: string
            title: Task Id
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/SnapshotEntry"
                title: Response Get Task Snapshots Api V1 Tasks  Task Id  Snapshots Get
        "404":
          description: Task not found
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /api/v1/tasks/counts:
    get:
      summary: Task Counts
      description: |-
        Return task counts per status without serialising task bodies.

        This is the lightweight alternative to GET /tasks for orchestrator
        tick summaries and dashboard polling.
      operationId: task_counts_api_v1_tasks_counts_get
      parameters:
        - name: tenant
          in: query
          required: false
          schema:
            anyOf:
              - type: string
              - type: "null"
            title: Tenant
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TaskCountsResponse"
        "403":
          description: Tenant scope access denied
        "404":
          description: Resource not found or tenant mismatch
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /api/v1/tasks/archive:
    get:
      summary: Get Archive
      description: Return the last N archived (done/failed) task records.
      operationId: get_archive_api_v1_tasks_archive_get
      parameters:
        - name: limit
          in: query
          required: false
          schema:
            type: integer
            default: 50
            title: Limit
        - name: tenant
          in: query
          required: false
          schema:
            anyOf:
              - type: string
              - type: "null"
            title: Tenant
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/ArchiveRecord"
                title: Response Get Archive Api V1 Tasks Archive Get
        "403":
          description: Tenant scope access denied
        "404":
          description: Resource not found or tenant mismatch
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /api/v1/tasks/graph:
    get:
      summary: Get Task Graph
      description: |-
        Return the task dependency graph as JSON (nodes + edges + critical path).

        Builds a DAG from all current tasks and returns:
        - ``nodes``: list of {id, role, status, estimated_minutes, title}
        - ``edges``: list of {from, to, type, semantic_type}
        - ``critical_path``: ordered list of task IDs on the longest chain
        - ``critical_path_minutes``: total estimated minutes on the critical path
        - ``parallel_width``: max tasks that can run concurrently
        - ``bottlenecks``: task IDs that block the most downstream work
      operationId: get_task_graph_api_v1_tasks_graph_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
        "403":
          description: Tenant scope access denied
        "404":
          description: Resource not found or tenant mismatch
  /api/v1/tasks/{task_id}:
    get:
      summary: Get Task
      description: Get a single task by ID.
      operationId: get_task_api_v1_tasks__task_id__get
      parameters:
        - name: task_id
          in: path
          required: true
          schema:
            type: string
            title: Task Id
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TaskResponse"
        "404":
          description: Task not found
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
    patch:
      summary: Patch Task
      description: |-
        Update mutable task fields (role, priority, model) - manager corrections.

        Used by the manager agent or dashboard to correct mis-assigned tasks,
        adjust priority, or change model without interrupting the orchestrator.
      operationId: patch_task_api_v1_tasks__task_id__patch
      parameters:
        - name: task_id
          in: path
          required: true
          schema:
            type: string
            title: Task Id
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/TaskPatchRequest"
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TaskResponse"
        "404":
          description: Task not found
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /api/v1/tasks/{task_id}/graph-neighbors:
    get:
      summary: Get Task Graph Neighbors
      description: |-
        Return immediate dependency neighbours for a single task.

        Powers the dashboard Deps tab: upstream tasks the requested one waits
        on (its ``depends_on`` list) and downstream tasks that declare it as a
        dependency.  Depth is intentionally fixed at 1 - the panel renders two
        flat lists, not a transitive graph.
      operationId: get_task_graph_neighbors_api_v1_tasks__task_id__graph_neighbors_get
      parameters:
        - name: task_id
          in: path
          required: true
          schema:
            type: string
            title: Task Id
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
                title: Response Get Task Graph Neighbors Api V1 Tasks  Task Id  Graph Neighbors Get
        "404":
          description: Task not found
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /api/v1/tasks/{task_id}/gates:
    get:
      summary: Get Task Gates
      description: Return the persisted quality-gate report for a task.
      operationId: get_task_gates_api_v1_tasks__task_id__gates_get
      parameters:
        - name: task_id
          in: path
          required: true
          schema:
            type: string
            title: Task Id
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
        "404":
          description: Task or gate report not found
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
        "500":
          description: Gate report unreadable
  /api/v1/tasks/{task_id}/prioritize:
    post:
      summary: Prioritize Task
      description: Bump a task to priority 0 so the orchestrator picks it up next.
      operationId: prioritize_task_api_v1_tasks__task_id__prioritize_post
      parameters:
        - name: task_id
          in: path
          required: true
          schema:
            type: string
            title: Task Id
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TaskResponse"
        "404":
          description: Task not found
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /api/v1/tasks/{task_id}/force-claim:
    post:
      summary: Force Claim Task
      description: |-
        Force a task back to open with priority 0 for immediate pickup.

        Resets claimed/in_progress tasks back to open so the orchestrator's
        next tick will spawn a fresh agent for them.  Terminal tasks
        (done/failed/cancelled) are rejected with 409.
      operationId: force_claim_task_api_v1_tasks__task_id__force_claim_post
      parameters:
        - name: task_id
          in: path
          required: true
          schema:
            type: string
            title: Task Id
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TaskResponse"
        "404":
          description: Task not found
        "409":
          description: Cannot force-claim terminal task
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /api/v1/agents/{agent_id}/heartbeat:
    post:
      summary: Agent Heartbeat
      description: Register an agent heartbeat.
      operationId: agent_heartbeat_api_v1_agents__agent_id__heartbeat_post
      parameters:
        - name: agent_id
          in: path
          required: true
          schema:
            type: string
            title: Agent Id
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/HeartbeatRequest"
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HeartbeatResponse"
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /api/v1/bulletin:
    post:
      summary: Post Bulletin
      description: |-
        Append a message to the bulletin board.

        Returns 201 when the message is stored and any registered signal action
        ran. When a signal action hook fails (for example a ``blocker`` whose
        clearance gate did not materialize), the message is still on the
        append-only board and queued in the board's retry outbox, but the action is
        not complete: the response is 202 rather than 201 so the caller can tell
        "stored and acted on" from "stored, action pending retry" (#2648).
      operationId: post_bulletin_api_v1_bulletin_post
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/BulletinPostRequest"
      responses:
        "201":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/BulletinMessageResponse"
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
    get:
      summary: Get Bulletin
      description: Get bulletin messages since a given timestamp.
      operationId: get_bulletin_api_v1_bulletin_get
      parameters:
        - name: since
          in: query
          required: false
          schema:
            type: number
            default: 0
            title: Since
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/BulletinMessageResponse"
                title: Response Get Bulletin Api V1 Bulletin Get
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /api/v1/channel/query:
    post:
      summary: Post Channel Query
      description: Post a coordination query targeted at an agent or role.
      operationId: post_channel_query_api_v1_channel_query_post
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ChannelQueryRequest"
        required: true
      responses:
        "201":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ChannelQueryResponse"
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /api/v1/channel/{query_id}/respond:
    post:
      summary: Post Channel Response
      description: Respond to a channel query.
      operationId: post_channel_response_api_v1_channel__query_id__respond_post
      parameters:
        - name: query_id
          in: path
          required: true
          schema:
            type: string
            title: Query Id
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ChannelResponseRequest"
      responses:
        "201":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ChannelResponseResponse"
        "404":
          description: Query not found
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /api/v1/channel/queries:
    get:
      summary: Get Channel Queries
      description: Get pending queries, optionally filtered by agent_id or role.
      operationId: get_channel_queries_api_v1_channel_queries_get
      parameters:
        - name: agent_id
          in: query
          required: false
          schema:
            anyOf:
              - type: string
              - type: "null"
            title: Agent Id
        - name: role
          in: query
          required: false
          schema:
            anyOf:
              - type: string
              - type: "null"
            title: Role
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/ChannelQueryResponse"
                title: Response Get Channel Queries Api V1 Channel Queries Get
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /api/v1/channel/{query_id}/responses:
    get:
      summary: Get Channel Responses
      description: Get all responses for a channel query.
      operationId: get_channel_responses_api_v1_channel__query_id__responses_get
      parameters:
        - name: query_id
          in: path
          required: true
          schema:
            type: string
            title: Query Id
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/ChannelResponseResponse"
                title: Response Get Channel Responses Api V1 Channel  Query Id  Responses Get
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /api/v1/tasks/claim-receipt:
    post:
      summary: Claim Receipt
      description: |-
        Claim the next eligible backlog row and return a signed claim receipt.

        The dependency gate is enforced by :class:`ClaimFilter`: a row is offered
        only when its ``depends_on`` are all in ``completed_ids``. The granted
        claim is mirrored into the audit chain via the existing
        ``record_task_claim_receipt`` (no new event type), and the returned
        receipt embeds that event's chain head so the claim verifies offline. A
        filter matching no eligible row returns a signed refusal receipt.
      operationId: claim_receipt_api_v1_tasks_claim_receipt_post
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ClaimReceiptRequest"
        required: true
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
                title: Response Claim Receipt Api V1 Tasks Claim Receipt Post
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
        "503":
          description: Server is draining -- no new claims accepted
  /api/v1/tasks/{task_id}/messages:
    post:
      summary: Post Task Message
      description: |-
        Append one typed message to the recipient task's mailbox.

        The message is DLP-redacted, HMAC-chained onto the mailbox journal,
        Ed25519-signed, and mirrored into the audit chain before the response
        is returned - the response IS the signed journal entry.
      operationId: post_task_message_api_v1_tasks__task_id__messages_post
      parameters:
        - name: task_id
          in: path
          required: true
          schema:
            type: string
            title: Task Id
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/TaskMessagePost"
      responses:
        "201":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TaskMessageResponse"
        "404":
          description: Task not found
        "422":
          description: Unknown message kind or body over the byte cap
        "429":
          description: Recipient task mailbox is full
    get:
      summary: Get Task Messages
      description: |-
        Deliver pending messages for a task, in chain append order.

        ``since_seq`` is a deterministic cursor: pass the highest ``seq``
        already processed to receive only newer messages. Replaying the same
        journal always reproduces the same delivery order.
      operationId: get_task_messages_api_v1_tasks__task_id__messages_get
      parameters:
        - name: task_id
          in: path
          required: true
          schema:
            type: string
            title: Task Id
        - name: since_seq
          in: query
          required: false
          schema:
            type: integer
            default: -1
            title: Since Seq
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/TaskMessageResponse"
                title: Response Get Task Messages Api V1 Tasks  Task Id  Messages Get
        "404":
          description: Task not found
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /api/v1/tasks/{task_id}/artifacts:
    post:
      summary: Post Task Artifact
      description: Post one journal-anchored artifact against a task the caller holds.
      operationId: post_task_artifact_api_v1_tasks__task_id__artifacts_post
      parameters:
        - name: task_id
          in: path
          required: true
          schema:
            type: string
            title: Task Id
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/TaskArtifactPost"
      responses:
        "201":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TaskArtifactContentResponse"
        "403":
          description: Caller does not hold the task's claim
        "404":
          description: Task not found
        "413":
          description: Artifact payload exceeds the per-blob cap
        "422":
          description: Invalid artifact payload
    get:
      summary: List Task Artifacts
      description: List every posted artifact version with its verification state.
      operationId: list_task_artifacts_api_v1_tasks__task_id__artifacts_get
      parameters:
        - name: task_id
          in: path
          required: true
          schema:
            type: string
            title: Task Id
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/TaskArtifactContentResponse"
                title: Response List Task Artifacts Api V1 Tasks  Task Id  Artifacts Get
        "404":
          description: Task not found
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /api/v1/tasks/{task_id}/steer:
    post:
      summary: Post Task Steer
      description: |-
        Record a steering receipt for a running worker and apply its effect.

        The receipt is bound into the audit chain before the effect executes; the
        ``steer.*`` mailbox message and any process signal reference the receipt
        hash returned here. An effect can never precede its receipt.
      operationId: post_task_steer_api_v1_tasks__task_id__steer_post
      parameters:
        - name: task_id
          in: path
          required: true
          schema:
            type: string
            title: Task Id
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/TaskSteerPost"
      responses:
        "201":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TaskSteerResponse"
        "403":
          description: Scope is not authorised to steer
        "404":
          description: Task not found
        "409":
          description: Confirmed payload differs from the executed command
        "422":
          description: Malformed steering command
        "503":
          description: Task mailbox is not configured
  /api/v1/cluster/nodes:
    post:
      summary: Register Node
      description: Register a node, or update the entry of one that is re-registering.
      operationId: register_node_api_v1_cluster_nodes_post
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/NodeRegisterRequest"
      responses:
        "201":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/NodeResponse"
        "401":
          description: Cluster authentication failed
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
    get:
      summary: List Nodes
      description: List all cluster nodes, optionally filtered by status.
      operationId: list_nodes_api_v1_cluster_nodes_get
      parameters:
        - name: status
          in: query
          required: false
          schema:
            anyOf:
              - type: string
              - type: "null"
            title: Status
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/NodeResponse"
                title: Response List Nodes Api V1 Cluster Nodes Get
        "400":
          description: Invalid node status
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /api/v1/cluster/nodes/{node_id}/heartbeat:
    post:
      summary: Node Heartbeat
      description: Record a heartbeat from a cluster node.
      operationId: node_heartbeat_api_v1_cluster_nodes__node_id__heartbeat_post
      parameters:
        - name: node_id
          in: path
          required: true
          schema:
            type: string
            title: Node Id
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/NodeHeartbeatRequest"
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/NodeResponse"
        "401":
          description: Cluster authentication failed
        "404":
          description: Node not registered
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /api/v1/cluster/nodes/{node_id}:
    delete:
      summary: Unregister Node
      description: Remove a node from the cluster.
      operationId: unregister_node_api_v1_cluster_nodes__node_id__delete
      parameters:
        - name: node_id
          in: path
          required: true
          schema:
            type: string
            title: Node Id
      responses:
        "204":
          description: Successful Response
        "401":
          description: Cluster authentication failed
        "404":
          description: Node not found
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /api/v1/cluster/nodes/{node_id}/cordon:
    post:
      summary: Cordon Node
      description: Cordon a node -- exclude from scheduling.
      operationId: cordon_node_api_v1_cluster_nodes__node_id__cordon_post
      parameters:
        - name: node_id
          in: path
          required: true
          schema:
            type: string
            title: Node Id
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                type: object
                additionalProperties:
                  type: string
                title: Response Cordon Node Api V1 Cluster Nodes  Node Id  Cordon Post
        "401":
          description: Cluster authentication failed
        "404":
          description: Node not found
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /api/v1/cluster/nodes/{node_id}/uncordon:
    post:
      summary: Uncordon Node
      description: Uncordon a node -- resume accepting tasks.
      operationId: uncordon_node_api_v1_cluster_nodes__node_id__uncordon_post
      parameters:
        - name: node_id
          in: path
          required: true
          schema:
            type: string
            title: Node Id
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                type: object
                additionalProperties:
                  type: string
                title: Response Uncordon Node Api V1 Cluster Nodes  Node Id  Uncordon Post
        "401":
          description: Cluster authentication failed
        "404":
          description: Node not found
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /api/v1/cluster/nodes/{node_id}/drain:
    post:
      summary: Drain Node
      description: Start draining a node -- cordon + signal agents to finish.
      operationId: drain_node_api_v1_cluster_nodes__node_id__drain_post
      parameters:
        - name: node_id
          in: path
          required: true
          schema:
            type: string
            title: Node Id
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                type: object
                additionalProperties:
                  type: string
                title: Response Drain Node Api V1 Cluster Nodes  Node Id  Drain Post
        "401":
          description: Cluster authentication failed
        "404":
          description: Node not found
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /api/v1/cluster/status:
    get:
      summary: Cluster Status
      description: Get cluster status summary.
      operationId: cluster_status_api_v1_cluster_status_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ClusterStatusResponse"
  /api/v1/cluster/claims/gossip:
    post:
      summary: Gossip Claims
      description: |-
        Fold peer claim receipts into this node's signed journal (#2558).

        The leaderless counterpart to ``POST /cluster/steal``: no node decides who
        gets what here. Each receipt is folded only after its Ed25519 signature and
        its chain link both verify, so an unverifiable receipt is never written.

        A receipt that does not extend the local head is *not* merged. It produces
        a signed ``fork`` receipt carrying the divergence entry index, which the
        response surfaces through ``forked``. Silent merge would be the one failure
        mode a leaderless design cannot recover from: two partitions would each
        hold a coherent-looking journal describing incompatible work.

        Authorisation reuses the node-heartbeat scope: gossip is a peer-to-peer
        fleet-membership operation, not an administrative one.
      operationId: gossip_claims_api_v1_cluster_claims_gossip_post
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ClaimGossipRequest"
        required: true
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ClaimGossipResponse"
        "401":
          description: Cluster authentication failed
        "409":
          description: Node is not running the MESH topology
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /api/v1/cluster/steal:
    post:
      summary: Steal Tasks
      description: |-
        Evaluate task stealing policy and reassign claimed tasks between nodes.

        Workers report their queue depths; the server runs the steal policy and
        returns a list of task reassignments.  Stolen tasks are reset to ``open``
        so the receiver node can claim them.

        Authorisation requires the node-admin scope, like the other node-registry
        mutations (cordon, uncordon, drain, unregister) this sits beside in the
        operational-primitives table.  It is deliberately NOT the heartbeat scope
        that ``POST /cluster/claims/gossip`` uses: gossip proves each receipt with
        its own Ed25519 signature and chain link inside the handler, so its bearer
        scope only has to establish fleet membership, whereas here the caller's
        reported queue depths drive ``force_claim`` directly with no further proof
        to check.
      operationId: steal_tasks_api_v1_cluster_steal_post
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/TaskStealRequest"
        required: true
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TaskStealResponse"
        "401":
          description: Cluster authentication failed
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /api/v1/a2a/agent-card:
    get:
      summary: Agent Card
      description: |-
        Publish the Bernstein orchestrator Agent Card (legacy A2A path).

        The richer service manifest at ``/.well-known/agent.json`` is served by
        ``routes.well_known``; this endpoint is preserved for callers that
        historically pulled the orchestrator's own A2A card.
      operationId: agent_card_api_v1_a2a_agent_card_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/A2AAgentCardResponse"
  /api/v1/a2a/agents:
    get:
      summary: List A2A Agents
      description: Return Bernstein's A2A agent card via the task API namespace.
      operationId: list_a2a_agents_api_v1_a2a_agents_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/A2AAgentCardResponse"
  /api/v1/a2a/message:
    post:
      summary: A2A Message
      description: Receive an inbound A2A message and inject it into the target task context.
      operationId: a2a_message_api_v1_a2a_message_post
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/A2AMessageRequest"
        required: true
      responses:
        "201":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/A2AMessageResponse"
        "404":
          description: Task not found
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /api/v1/a2a/tasks/send:
    post:
      summary: A2A Send Task
      description: |-
        Receive a task from an external A2A agent.

        Creates both an A2A task record and a corresponding Bernstein task,
        linking them together for lifecycle synchronisation.
      operationId: a2a_send_task_api_v1_a2a_tasks_send_post
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/A2ATaskSendRequest"
        required: true
      responses:
        "201":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/A2ATaskResponse"
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /api/v1/a2a/tasks/{a2a_task_id}:
    get:
      summary: A2A Get Task
      description: Get an A2A task by ID, syncing status from the Bernstein task.
      operationId: a2a_get_task_api_v1_a2a_tasks__a2a_task_id__get
      parameters:
        - name: a2a_task_id
          in: path
          required: true
          schema:
            type: string
            title: A2A Task Id
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/A2ATaskResponse"
        "404":
          description: A2A task not found
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /api/v1/a2a/tasks/{a2a_task_id}/artifacts:
    post:
      summary: A2A Add Artifact
      description: Attach an artifact to an A2A task.
      operationId: a2a_add_artifact_api_v1_a2a_tasks__a2a_task_id__artifacts_post
      parameters:
        - name: a2a_task_id
          in: path
          required: true
          schema:
            type: string
            title: A2A Task Id
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/A2AArtifactRequest"
      responses:
        "201":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/A2AArtifactResponse"
        "404":
          description: A2A task not found
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /api/v1/a2a/v0/tasks:
    post:
      summary: A2A V0 Accept Task
      description: |-
        Accept a federated task delegated from a peer orchestrator.

        Wire format::

            {
              "sender": { ...AgentCard... },
              "task":   { "id": "...", "message": "...", "role": "..." }
            }

        Returns 202 with the local federated-task id and the remote task id
        that was offered. Validation errors return HTTP 409 so that the
        caller's retry policy treats them as terminal (the peer is reachable
        and authoritative, no point retrying with the same body).
      operationId: a2a_v0_accept_task_api_v1_a2a_v0_tasks_post
      responses:
        "202":
          description: Successful Response
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
                title: Response A2A V0 Accept Task Api V1 A2A V0 Tasks Post
        "400":
          description: Invalid sender Agent Card or task body
        "409":
          description: Task rejected (validation, capacity, etc.)
  /api/v1/status:
    get:
      summary: Status Dashboard
      description: Dashboard summary of task counts.
      operationId: status_dashboard_api_v1_status_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /api/v1/status/duration-predictions:
    get:
      summary: Duration Predictions
      description: |-
        Return ML-predicted duration estimates for all open/claimed tasks.

        Uses the local GradientBoosting duration predictor.  Falls back to the
        static cold-start table when fewer than 50 completions are available.

        Response shape::

            {
              "predictor": {
                "trained": true,
                "training_samples": 142,
                "cold_start": false
              },
              "tasks": [
                {
                  "task_id": "abc123",
                  "title": "Refactor auth module",
                  "role": "backend",
                  "p50_seconds": 720.0,
                  "p90_seconds": 1440.0,
                  "confidence": 0.62,
                  "is_cold_start": false,
                  "eta_p50": "12m 0s",
                  "eta_p90": "24m 0s"
                }
              ]
            }
      operationId: duration_predictions_api_v1_status_duration_predictions_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /api/v1/routing/bandit:
    get:
      summary: Bandit Routing Stats
      description: |-
        Return contextual bandit routing statistics.

        Reads persisted state from ``.sdd/routing/``.  Returns an empty dict
        when bandit routing has not been activated (``--routing bandit`` not passed).
      operationId: bandit_routing_stats_api_v1_routing_bandit_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /api/v1/dashboard/data:
    get:
      summary: Dashboard Data
      description: |-
        Return all mission control dashboard data as JSON.

        Includes stats, tasks with timeline data, agent details with costs,
        file ownership map, cost history, and alerts.
      operationId: dashboard_data_api_v1_dashboard_data_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /api/v1/events:
    get:
      summary: Sse Events
      description: |-
        Server-Sent Events stream for real-time dashboard updates.

        Includes disconnect detection via heartbeat pings and connection
        timeout handling to prevent leaked subscriber queues.
      operationId: sse_events_api_v1_events_get
      responses:
        "200":
          description: Server-Sent Events stream. The response body does not terminate.
          content:
            text/event-stream:
              schema:
                type: string
  /api/v1/badge.json:
    get:
      summary: Get Badge
      description: |-
        Return dynamic badge data for GitHub shields.io integration.

        Shows tasks completed, total cost, and quality score.
        Usage: https://img.shields.io/endpoint?url=<server>/badge.json
      operationId: get_badge_api_v1_badge_json_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /api/v1/memory/audit:
    get:
      summary: Memory Audit
      description: |-
        Audit the lesson memory provenance chain (OWASP ASI06 2026).

        Returns chain integrity status and a per-entry provenance trail.
        Detects tampering, insertion, deletion, and reordering attacks.
      operationId: memory_audit_api_v1_memory_audit_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /api/v1/broadcast:
    post:
      summary: Broadcast Command
      description: |-
        Send a message to all running agents via fastest available channel.

        Uses stdin pipe where available (sub-second delivery), falls back
        to file-based COMMAND signal for agents without pipe support.

        Expects JSON body: ``{"message": "some instruction"}``.
      operationId: broadcast_command_api_v1_broadcast_post
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/BroadcastRequest"
        required: true
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /api/v1/health:
    get:
      summary: Health Check
      description: Liveness check with component-level status.
      operationId: health_check_api_v1_health_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HealthResponse"
  /api/v1/health/ready:
    get:
      summary: Ready Check
      description: Readiness check for load balancers.
      operationId: ready_check_api_v1_health_ready_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /api/v1/ready:
    get:
      summary: Ready Alias
      description: Alias for /health/ready.
      operationId: ready_alias_api_v1_ready_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /api/v1/health/live:
    get:
      summary: Live Check
      description: Liveness check for process monitoring.
      operationId: live_check_api_v1_health_live_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /api/v1/alive:
    get:
      summary: Live Alias
      description: Alias for /health/live.
      operationId: live_alias_api_v1_alive_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /api/v1/config:
    post:
      summary: Update Config
      description: |-
        Update mutable config fields at runtime.

        Accepts JSON body with ``{"max_agents": N}``.  Writes the change to
        ``bernstein.yaml`` so the orchestrator's hot-reload picks it up on
        the next tick (~30s).  Returns the new effective value.

        Agent identity JWTs (per-agent, task-scoped) are rejected with 403 -
        mutating process-wide config is an operator action.  SSO admin users
        and legacy operator tokens may proceed.  Bearer-level permission
        enforcement is handled by :class:`SSOAuthMiddleware` via the
        ``admin:manage`` mapping; this check adds defense-in-depth against any
        agent JWT that slips through the middleware's prefix match.
      operationId: update_config_api_v1_config_post
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /api/v1/shutdown:
    post:
      summary: Shutdown Server
      description: |-
        Initiate graceful server shutdown.

        Accepts an optional JSON body ``{"reason": "..."}``.  Schedules a
        SIGTERM to the current process shortly after the response is sent so
        that the Uvicorn server exits cleanly.
      operationId: shutdown_server_api_v1_shutdown_post
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /api/v1/cache-stats:
    get:
      summary: Cache Stats
      description: |-
        Return prompt caching statistics from the manifest.

        Reads `.sdd/caching/manifest.jsonl` and returns aggregated counts,
        estimated token savings, and estimated USD savings based on the
        Anthropic cached-input discount (90% off standard input price).

        Returns 200 with empty statistics if no cache manifest exists yet.
      operationId: cache_stats_api_v1_cache_stats_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /api/v1/metrics:
    get:
      summary: Metrics Endpoint
      description: |-
        Prometheus metrics scrape endpoint.

        Updates all gauges from the current task store state, then
        returns the full metric exposition in Prometheus text format.
      operationId: metrics_endpoint_api_v1_metrics_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /api/v1/workspace:
    get:
      tags:
        - workspace
      summary: Workspace Status
      description: Return repository status for the configured workspace.
      operationId: workspace_status_api_v1_workspace_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WorkspaceResponse"
        "400":
          description: Invalid seed file
  /api/v1/workspace/merge-order:
    post:
      tags:
        - workspace
      summary: Workspace Merge Order
      description: Return the repo merge order derived from current cross-repo task dependencies.
      operationId: workspace_merge_order_api_v1_workspace_merge_order_post
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MergeOrderResponse"
        "400":
          description: Invalid seed file
        "404":
          description: No workspace configured
  /api/v1/alerts:
    get:
      summary: Get Alerts
      description: |-
        Return current dashboard alerts as JSON.

        Builds alerts from the live task/agent state - failed tasks, blocked
        tasks, stale agents, and budget thresholds.  Intended for dashboard
        polling or external monitoring.

        Returns a JSON object with keys:
        - ``alerts``: list of alert dicts (``level``, ``message``, ``detail``)
        - ``count``: total number of alerts
        - ``ts``: server timestamp (Unix seconds)
      operationId: get_alerts_api_v1_alerts_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /api/v1/webhook:
    post:
      summary: Generic Webhook
      description: |-
        Create a task directly from a generic inbound webhook payload.

        The endpoint is intentionally small and separate from the trigger-manager
        flow: callers POST a task-shaped payload and Bernstein creates one task.
        ``BERNSTEIN_WEBHOOK_SECRET`` must be configured (fail-closed; )
        and each request must carry a fresh ``X-Bernstein-Timestamp`` header
        plus a matching ``X-Bernstein-Webhook-Signature-256`` HMAC over
        ``f"{timestamp}.".encode() + body``. The plaintext
        ``X-Bernstein-Webhook-Secret`` fallback has been removed; callers
        relying on it must upgrade to the HMAC + timestamp flow.

        Automation bridge (#2512): an admitted trigger returns a signed,
        chain-anchored trigger receipt in ``receipt`` so the calling platform holds
        a proof of what it asked for rather than a bare task reference. A trigger
        that fails authentication, or that replays a trigger id already admitted,
        is refused with its own signed refusal receipt (HTTP 401 and 409
        respectively) -- the negative path leaves a record, never a silent drop.
      operationId: generic_webhook_api_v1_webhook_post
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/WebhookTaskCreate"
        required: true
      responses:
        "201":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WebhookTaskResponse"
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /api/v1/webhooks/github:
    post:
      summary: Github Webhook
      description: |-
        Receive a GitHub App webhook, verify signature, and create tasks.

        Handles the following event types:
        - ``issues`` (opened / labeled)
        - ``pull_request_review_comment`` / ``issue_comment``
        - ``push``
        - ``workflow_run`` (completed + failure) - creates a ci-fix task, capped at
          ``MAX_CI_RETRIES`` active attempts per branch.

        Reads ``GITHUB_WEBHOOK_SECRET`` from environment for HMAC verification.
        Fail-closed: when the secret is not configured the
        endpoint is disabled and returns 503; unsigned GitHub webhooks are
        never accepted.
        Replay protection: if the caller includes an
        ``X-Bernstein-Timestamp`` header the request is additionally
        checked for freshness - drift greater than five minutes returns
        401.  Real GitHub deliveries omit this header and continue to
        work; the check is there so bernstein-internal relays cannot be
        replayed after capture.
        Returns 200 on success, 401 on bad/missing signature or stale
        timestamp, 400 on parse error, 503 when the endpoint is not
        configured.
      operationId: github_webhook_api_v1_webhooks_github_post
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /api/v1/webhooks/gitlab:
    post:
      summary: Gitlab Webhook
      description: |-
        Receive a GitLab CI webhook, verify token, and create ci-fix tasks.

        Handles the following event types:
        - ``pipeline`` (failed) - creates a ci-fix task, capped at
          ``MAX_CI_RETRIES`` active attempts per branch.
        - ``job`` (failed) - creates a ci-fix task for the specific job.

        Reads ``GITLAB_WEBHOOK_TOKEN`` from environment. GitLab sends a simple
        plaintext token in the ``x-gitlab-token`` header.
        Returns 200 on success, 401 on bad/missing token.
      operationId: gitlab_webhook_api_v1_webhooks_gitlab_post
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /api/v1/webhooks/telemetry/sentry/:
    post:
      summary: Telemetry Sentry
      description: Receive a Sentry-protocol issue-alert webhook.
      operationId: telemetry_sentry_api_v1_webhooks_telemetry_sentry__post
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /api/v1/webhooks/telemetry/gha_failure/:
    post:
      summary: Telemetry Gha Failure
      description: Receive a GitHub Actions ``workflow_run`` failure webhook.
      operationId: telemetry_gha_failure_api_v1_webhooks_telemetry_gha_failure__post
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /api/v1/webhooks/telemetry/datadog/:
    post:
      summary: Telemetry Datadog
      description: Receive a Datadog Logs webhook (stubbed in MVP).
      operationId: telemetry_datadog_api_v1_webhooks_telemetry_datadog__post
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /api/v1/webhooks/telemetry/loki/:
    post:
      summary: Telemetry Loki
      description: Receive a Loki / Alertmanager webhook (stubbed in MVP).
      operationId: telemetry_loki_api_v1_webhooks_telemetry_loki__post
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /api/v1/webhooks/telemetry/custom_jsonl/:
    post:
      summary: Telemetry Custom Jsonl
      description: Receive a custom JSONL tail webhook (stubbed in MVP).
      operationId: telemetry_custom_jsonl_api_v1_webhooks_telemetry_custom_jsonl__post
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /api/v1/webhooks/trackers/{adapter}:
    post:
      summary: Tracker Webhook
      description: |-
        Receive a tracker webhook, verify, dedupe, and enqueue.

        Path parameter:
            adapter: Short adapter name registered via
                :func:`bernstein.core.trackers.webhook_receiver.register_handler`.

        The endpoint accepts any JSON object.  All verification and replay
        decisions are made before the body is enqueued.  When verification
        succeeds and the delivery is fresh the parsed
        :class:`~bernstein.core.trackers.webhook_receiver.TrackerEvent` is
        stashed on ``app.state.tracker_event_queue`` if present so the
        orchestrator's normal task ingestion can drain it; if no queue is
        wired we simply log the event.  Either way the tracker receives a
        200 so it does not retry.
      operationId: tracker_webhook_api_v1_webhooks_trackers__adapter__post
      parameters:
        - name: adapter
          in: path
          required: true
          schema:
            type: string
            title: Adapter
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /api/v1/webhooks/discord/interactions:
    post:
      summary: Discord Interactions
      description: |-
        Receive and route Discord Application Command interactions.

        Verifies the Ed25519 signature, handles PING handshakes, and dispatches
        slash commands to the appropriate handler. Returns an immediate response
        (Discord requires a reply within 3 seconds).

        Returns:
            200 with a Discord interaction response object on success.
            401 if the signature is invalid.
            400 if the payload cannot be parsed.
      operationId: discord_interactions_api_v1_webhooks_discord_interactions_post
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /api/v1/webhooks/slack/commands:
    post:
      summary: Slack Slash Command
      description: |-
        Receive a Slack slash command, verify signature, and ack immediately.

        Slack requires a response within 3 seconds.  This endpoint verifies the
        request signature, parses the URL-encoded form payload, and returns an
        immediate acknowledgement.  Any long-running work (task creation, etc.)
        should be dispatched asynchronously using ``response_url``.

        Reads ``SLACK_SIGNING_SECRET`` from environment for HMAC verification.
        The secret MUST be configured: when it is not, the endpoint is
        disabled and returns ``UNCONFIGURED_STATUS``; only signed Slack
        requests are accepted.
        Returns 200 on success, 401 on bad/missing signature, 400 on parse
        error, ``UNCONFIGURED_STATUS`` when the endpoint is not configured.

        Slash command form fields parsed:
            - ``command``      - the slash command (e.g. ``/bernstein``)
            - ``text``         - text following the command
            - ``user_id``      - Slack user ID
            - ``channel_id``   - Slack channel ID
            - ``response_url`` - URL for delayed responses (up to 30 min)
            - ``trigger_id``   - trigger ID for opening modals
      operationId: slack_slash_command_api_v1_webhooks_slack_commands_post
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /api/v1/webhooks/slack/events:
    post:
      summary: Slack Events
      description: |-
        Receive Slack Events API callbacks.

        Handles:
        - ``url_verification``: returns the challenge value for endpoint verification.
        - ``event_callback`` with ``message`` type: creates a task when the bot is
          mentioned.  Bot messages and ``message_changed`` subtypes are ignored to
          prevent loops.

        Reads ``SLACK_SIGNING_SECRET`` from environment for HMAC verification.
        The secret MUST be configured: when it is not, the endpoint is
        disabled and returns ``UNCONFIGURED_STATUS``; only signed Slack
        requests are accepted.  Note that the ``url_verification`` handshake
        is signed by Slack too, so registering the endpoint works normally.
        Payload shape is validated before it is read: the body and, when
        present, its ``event`` member must both be JSON objects.
        Returns 200 on success, 401 on bad/missing signature, 400 on parse
        error or malformed payload shape, ``UNCONFIGURED_STATUS`` when the
        endpoint is not configured.
      operationId: slack_events_api_v1_webhooks_slack_events_post
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /api/v1/events/cost:
    get:
      summary: Cost Events
      description: |-
        SSE endpoint for real-time cost updates.

        Listens to the global SSE bus for ``bulletin`` events that match
        the ``live_cost_update`` status pattern and forwards them to clients.
        Also provides periodic heartbeats.
      operationId: cost_events_api_v1_events_cost_get
      responses:
        "200":
          description: Server-Sent Events stream. The response body does not terminate.
          content:
            text/event-stream:
              schema:
                type: string
  /api/v1/costs:
    get:
      summary: Get Costs
      description: |-
        Aggregate cost data across all runs.

        Scans every persisted cost file in ``.sdd/runtime/costs/``, aggregates
        per-agent and per-model totals, and computes cost attainment as
        ``(total_spent / total_budget) * 100``.  Budget of zero is treated as
        unlimited - attainment is reported as 0.0 in that case.
      operationId: get_costs_api_v1_costs_get
      parameters:
        - name: tenant
          in: query
          required: false
          schema:
            anyOf:
              - type: string
              - type: "null"
            title: Tenant
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
        "403":
          description: Tenant access denied
        "404":
          description: Tenant not found
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /api/v1/costs/live:
    get:
      summary: Get Cost Live
      description: |-
        Return live cost breakdown for the most recent run.

        Finds the most recently modified cost file in ``.sdd/runtime/costs/``,
        loads it, and returns budget status plus per-agent and per-model
        cost breakdowns.
      operationId: get_cost_live_api_v1_costs_live_get
      parameters:
        - name: tenant
          in: query
          required: false
          schema:
            anyOf:
              - type: string
              - type: "null"
            title: Tenant
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
        "403":
          description: Tenant access denied
        "404":
          description: Tenant not found
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /api/v1/costs/current:
    get:
      summary: Get Cost Current
      description: |-
        Return real-time cost snapshot for the active run + GUI rollups.

        Updated after each agent completion.  Designed for TUI sidebar polling
        and lightweight dashboard widgets.  Returns per-model input/output/cache
        token breakdown alongside spend and budget status.

        Web GUI (Costs.tsx §6.05) consumes the additive ``today_usd``,
        ``week_usd``, ``projected_month_usd``, ``budget_usd``, ``used_pct``,
        ``prior_week_usd``, ``delta_hour_usd``, ``resets_at`` and
        ``last_sync_at`` fields. Existing TUI/CLI callers keep reading
        ``spent_usd`` / ``percentage_used`` etc. unchanged.

        Scope: every figure here is the caller's tenant's, not the run's or the
        deployment's - spend is replayed from the caller's scope only, and the
        cap it is measured against is that tenant's configured cap where one is
        configured.  ``tenant_id`` in the response names the scope, so a client
        aggregating across tenants can tell these apart from run-wide totals.
      operationId: get_cost_current_api_v1_costs_current_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /api/v1/costs/alerts:
    get:
      summary: Get Cost Alerts
      description: |-
        Return active budget alerts and 30d/90d cost trends.

        Reads the live cost data for the most recent run, checks whether spend
        has reached the 80% or 95% alert threshold, and returns trend data
        computed from ``.sdd/metrics/cost_history.jsonl``.

        Both halves of this response are scoped to ``tenant_id``: ``alerts`` is
        computed from the caller's tenant's spend against the caller's tenant's
        cap, and ``trend`` / ``history_days`` are narrowed to snapshots recorded
        for that same tenant.  A history snapshot written before per-tenant
        attribution existed carries no tenant and is excluded from every scoped
        trend, so a fresh deployment's 30/90-day averages start empty and fill in
        as new, attributed snapshots accumulate.
      operationId: get_cost_alerts_api_v1_costs_alerts_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /api/v1/costs/history:
    get:
      summary: Get Cost History
      description: |-
        Return cost history for chart visualization.

        Two response modes share one endpoint:

        * ``GET /costs/history?hours=24&granularity=hour`` (web GUI sparkline) -
          returns a flat ``[{ts, usd}]`` array bucketed from cost-tracker
          usages over the last *hours* window.
        * ``GET /costs/history`` *or* ``?envelope=1`` (legacy/CLI) - returns the
          original ``{history, trend, burn_rate_*, history_days}`` envelope
          built from ``.sdd/metrics/cost_history.jsonl`` daily snapshots, narrowed
          to the caller's tenant the same way ``/costs/alerts`` is.

        The sparkline branch lets the GUI feed `recharts` directly without
        unwrapping a ``.history`` field.
      operationId: get_cost_history_api_v1_costs_history_get
      parameters:
        - name: hours
          in: query
          required: false
          schema:
            anyOf:
              - type: integer
              - type: "null"
            title: Hours
        - name: granularity
          in: query
          required: false
          schema:
            type: string
            default: day
            title: Granularity
        - name: envelope
          in: query
          required: false
          schema:
            type: integer
            default: 0
            title: Envelope
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /api/v1/costs/export:
    get:
      summary: Export Costs
      description: |-
        Export cost data as CSV or JSON for finance analysis.

        Args:
            request: FastAPI request.
            format: Export format ('csv' or 'json').

        Returns:
            File response with cost data in requested format.
      operationId: export_costs_api_v1_costs_export_get
      parameters:
        - name: format
          in: query
          required: false
          schema:
            type: string
            default: json
            title: Format
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /api/v1/costs/forecast:
    get:
      summary: Forecast Costs
      description: |-
        Forecast cost for next hour and project monthly spend.

        Extrapolates current spending rate to predict next hour's cost AND
        rolls the trailing 7-day spend out to a 30-day projection
        (``projected_month_usd``) for the web GUI's "projected month" KPI
        card. The legacy fields (``forecast_next_hour_usd``,
        ``burn_rate_*``, ``confidence``, ``data_points``) remain unchanged
        for the TUI / CLI.
      operationId: forecast_costs_api_v1_costs_forecast_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /api/v1/costs/compare:
    get:
      summary: Compare Model Costs
      description: |-
        Return live model cost comparison during execution.

        Shows current costs by model with token usage statistics.
      operationId: compare_model_costs_api_v1_costs_compare_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /api/v1/costs/cache-stats:
    get:
      summary: Cache Stats
      description: |-
        Return prompt cache hit rate statistics.

        Shows cache hits/misses and savings by model.
      operationId: cache_stats_api_v1_costs_cache_stats_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /api/v1/costs/model-comparison:
    get:
      summary: Model Cost Comparison
      description: |-
        Return model cost comparison report.

        Shows what the current run would have cost with different models.
        Useful for optimizing model routing decisions.
      operationId: model_cost_comparison_api_v1_costs_model_comparison_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /api/v1/costs/token-efficiency:
    get:
      summary: Token Efficiency
      description: |-
        Compare token efficiency across models and tasks.

        Ranks models by tokens per useful line of code.
      operationId: token_efficiency_api_v1_costs_token_efficiency_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /api/v1/costs/by-tag:
    get:
      summary: Get Costs By Tag
      description: |-
        Aggregate cost data grouped by allocation tag *or* by adapter.

        The endpoint serves three callers:

        * Web GUI (``Costs.tsx`` adapter table) - calls ``GET /costs/by-tag``
          and expects an array of ``{adapter, calls, tokens, cost_usd,
          share_pct, delta_7d_pct}`` rows. With ``shape=auto`` (default) and
          no ``tag_key``, this is what we return.
        * Legacy callers passing ``tag_key=…`` - receive the existing
          ``{by_tag: {key: {value: cost}}}`` envelope.
        * Legacy callers wanting the envelope explicitly - pass
          ``shape=tags`` and get the envelope without supplying a key.

        The ``hours`` parameter controls the GUI window (default 24h).
      operationId: get_costs_by_tag_api_v1_costs_by_tag_get
      parameters:
        - name: tag_key
          in: query
          required: false
          schema:
            anyOf:
              - type: string
              - type: "null"
            title: Tag Key
        - name: hours
          in: query
          required: false
          schema:
            type: integer
            default: 24
            title: Hours
        - name: shape
          in: query
          required: false
          schema:
            type: string
            default: auto
            title: Shape
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /api/v1/costs/by-adapter:
    get:
      summary: Get Costs By Adapter
      description: |-
        Per-adapter cost breakdown for the web GUI Costs tab.

        Returns the same array shape as ``GET /costs/by-tag`` (default mode);
        exists as a clearer alias so the frontend doesn't have to know about
        the legacy "by-tag" naming.
      operationId: get_costs_by_adapter_api_v1_costs_by_adapter_get
      parameters:
        - name: hours
          in: query
          required: false
          schema:
            type: integer
            default: 24
            title: Hours
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /api/v1/costs/top-tasks:
    get:
      summary: Get Costs Top Tasks
      description: |-
        Top *limit* most-expensive tasks within the trailing *hours* window.

        Web GUI Costs.tsx renders this as the "Top 10 tasks" card. Each item:
        ``{id, title, agent, cost_usd}``. Empty list when no usage data is
        present so the card can show its empty-state cleanly.
      operationId: get_costs_top_tasks_api_v1_costs_top_tasks_get
      parameters:
        - name: limit
          in: query
          required: false
          schema:
            type: integer
            default: 10
            title: Limit
        - name: hours
          in: query
          required: false
          schema:
            type: integer
            default: 24
            title: Hours
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /api/v1/costs/token-breakdown:
    get:
      summary: Get Token Breakdown
      description: |-
        Per-agent session token consumption breakdown.

        For each agent session shows where the context budget was spent:
        system prompt (Bernstein overhead), context files, task description,
        tool call results accumulated at runtime, and assistant output.

        Identifies optimization opportunities - e.g. if 60% of tokens are
        context files the agent never used.

        Args:
            request: FastAPI request.
            session_id: If provided, return breakdown for a single session only.

        Returns:
            JSON with ``sessions`` list and aggregate ``summary``.
      operationId: get_token_breakdown_api_v1_costs_token_breakdown_get
      parameters:
        - name: session_id
          in: query
          required: false
          schema:
            anyOf:
              - type: string
              - type: "null"
            title: Session Id
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /api/v1/costs/efficiency:
    get:
      summary: Get Cost Efficiency
      description: |-
        Real-time cost-per-line-of-code efficiency metric.

        Shows cost efficiency as the run progresses:
        - **current**: efficiency of the most recently completed task
        - **run_average**: efficiency across all completed tasks in this run
        - **historical_average**: efficiency across all tracked runs

        Helps identify unusually expensive runs.

        Returns:
            JSON with ``current``, ``run_average``, ``historical_average``, and
            ``message`` fields.
      operationId: get_cost_efficiency_api_v1_costs_efficiency_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /api/v1/costs/{run_id}:
    get:
      summary: Get Cost Budget
      description: |-
        Return budget status for a specific run, within the caller's scope.

        Loads the persisted cost tracker from ``.sdd/runtime/costs/{run_id}.json``
        and returns its ``BudgetStatus`` as JSON.

        Scope: every figure is the caller's tenant's share of the run, not the
        run's total - the run file holds the spend of every tenant that spent
        against it, and only the caller's is replayed.  ``tenant_id`` in the
        response names the scope the figures belong to.
      operationId: get_cost_budget_api_v1_costs__run_id__get
      parameters:
        - name: run_id
          in: path
          required: true
          schema:
            type: string
            title: Run Id
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
        "404":
          description: No cost data for run
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /api/v1/dashboard/auth/status:
    get:
      summary: Dashboard Auth Status
      description: Report whether dashboard auth is required and who is logged in.
      operationId: dashboard_auth_status_api_v1_dashboard_auth_status_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /api/v1/dashboard/auth/login:
    post:
      summary: Dashboard Auth Login
      description: |-
        Open a dashboard session from a password or a scoped token.

        The session cookie wraps exactly the principal and scope the credential
        carried; a viewer token can never log into an operator session. Every
        attempt -- success or failure -- is journaled as a signed governance
        decision (``dashboard.login``).
      operationId: dashboard_auth_login_api_v1_dashboard_auth_login_post
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /api/v1/dashboard/auth/logout:
    post:
      summary: Dashboard Auth Logout
      description: Close the current dashboard session (idempotent).
      operationId: dashboard_auth_logout_api_v1_dashboard_auth_logout_post
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /api/v1/dashboard/file_locks:
    get:
      summary: File Locks Endpoint
      description: |-
        Return active file locks grouped by agent for the dashboard.

        Reads the persisted lock state from ``.sdd/runtime/file_locks.json`` and
        returns it in a dashboard-friendly format with both a flat list and an
        agent-grouped view.

        Returns:
            JSON with ``all_locks`` (flat list sorted by path), ``locks_by_agent``
            (dict keyed by agent_id with files list + task info + elapsed_s),
            ``count`` (total lock count), and ``ts`` (generation timestamp).
      operationId: file_locks_endpoint_api_v1_dashboard_file_locks_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /api/v1/dashboard/team:
    get:
      summary: Team Adoption Dashboard
      description: |-
        Aggregate team usage metrics for engineering managers.

        Returns total runs, tasks completed, cost saved vs. budget,
        code merge stats, and quality gate pass rate.
      operationId: team_adoption_dashboard_api_v1_dashboard_team_get
      responses:
        "200":
          description: Team adoption metrics
          content:
            application/json:
              schema: {}
  /api/v1/graph/impact:
    get:
      tags:
        - graph
      summary: Graph Impact
      description: Return downstream files impacted by changing the given file.
      operationId: graph_impact_api_v1_graph_impact_get
      parameters:
        - name: file
          in: query
          required: true
          schema:
            type: string
            minLength: 1
            title: File
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ImpactResponse"
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /api/v1/observability/agents:
    get:
      summary: Observability Agents
      description: Return runtime heartbeat, stall-profile, and log-summary data per agent.
      operationId: observability_agents_api_v1_observability_agents_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
                title: Response Observability Agents Api V1 Observability Agents Get
  /api/v1/observability/effectiveness:
    get:
      summary: Observability Effectiveness
      description: Return recent effectiveness data, role trends, and best configs.
      operationId: observability_effectiveness_api_v1_observability_effectiveness_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
                title: Response Observability Effectiveness Api V1 Observability Effectiveness Get
  /api/v1/observability/recommendations:
    get:
      summary: Observability Recommendations
      description: Return the current recommendation set and delivery hit counts.
      operationId: observability_recommendations_api_v1_observability_recommendations_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
                title: Response Observability Recommendations Api V1 Observability Recommendations Get
  /api/v1/observability/budget:
    get:
      summary: Observability Budget
      description: Return completion-budget status per lineage.
      operationId: observability_budget_api_v1_observability_budget_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
                title: Response Observability Budget Api V1 Observability Budget Get
  /api/v1/observability/deps:
    get:
      summary: Observability Deps
      description: |-
        Return dependency-graph validation status for current tasks.

        The response names the ids it walked - the ready set, the critical path,
        and both broken-edge lists - so the walk is narrowed to the caller's
        tenant scope rather than the whole store.
      operationId: observability_deps_api_v1_observability_deps_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
                title: Response Observability Deps Api V1 Observability Deps Get
  /api/v1/recap:
    get:
      summary: Recap
      description: |-
        Return post-run summary with diff stats, quality scores, and cost breakdown.

        Reads completed tasks from the archive and computes:
        - Task completion statistics
        - Git diff statistics (files changed, additions, deletions)
        - Quality score distribution
        - Cost breakdown by model and role
      operationId: recap_api_v1_recap_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
                title: Response Recap Api V1 Recap Get
  /api/v1/observability/token-histogram:
    get:
      summary: Token Histogram
      description: |-
        Return histogram of token usage by task complexity.

        Shows average tokens consumed for small, medium, large tasks.
        Helps understand token consumption patterns.
      operationId: token_histogram_api_v1_observability_token_histogram_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
                title: Response Token Histogram Api V1 Observability Token Histogram Get
  /api/v1/observability/queue-depth:
    get:
      summary: Get Queue Depth
      description: |-
        Return task queue depth over time.

        Returns last N records of queue depth snapshots.

        Args:
            request: FastAPI request.
            limit: Maximum number of records to return (default 100).

        Returns:
            List of queue depth snapshots with timestamps.
      operationId: get_queue_depth_api_v1_observability_queue_depth_get
      parameters:
        - name: limit
          in: query
          required: false
          schema:
            type: integer
            default: 100
            title: Limit
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
                title: Response Get Queue Depth Api V1 Observability Queue Depth Get
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /api/v1/observability/timeline:
    get:
      summary: Get Timeline
      description: |-
        Return task timing data for timeline visualization.

        Returns start and end times for all tasks tracked in metrics.
      operationId: get_timeline_api_v1_observability_timeline_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
                title: Response Get Timeline Api V1 Observability Timeline Get
  /api/v1/changelog:
    get:
      summary: Get Changelog
      description: |-
        Generate changelog from completed tasks.

        Groups completed tasks by type (Features, Fixes, etc.) and
        formats as markdown changelog.

        Args:
            request: FastAPI request.
            days: Number of days to include (default 30).

        Returns:
            Dict with 'markdown' key containing changelog text.
      operationId: get_changelog_api_v1_changelog_get
      parameters:
        - name: days
          in: query
          required: false
          schema:
            type: integer
            default: 30
            title: Days
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
                title: Response Get Changelog Api V1 Changelog Get
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /api/v1/observability/incidents:
    get:
      summary: List Incidents
      description: |-
        List all known incidents.

        Returns:
            Dict with 'incidents' list.
      operationId: list_incidents_api_v1_observability_incidents_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
                title: Response List Incidents Api V1 Observability Incidents Get
  /api/v1/observability/incident-timeline/{incident_id}:
    get:
      summary: Get Incident Timeline
      description: |-
        Build a correlated incident timeline from logs, metrics, and traces.

        Args:
            request: FastAPI request.
            incident_id: The incident ID to build a timeline for.
            window_before: Seconds before incident to include (default 600).
            window_after: Seconds after incident to include (default 300).

        Returns:
            Dict with incident metadata and sorted timeline events.
      operationId: get_incident_timeline_api_v1_observability_incident_timeline__incident_id__get
      parameters:
        - name: incident_id
          in: path
          required: true
          schema:
            type: string
            title: Incident Id
        - name: window_before
          in: query
          required: false
          schema:
            type: integer
            default: 600
            title: Window Before
        - name: window_after
          in: query
          required: false
          schema:
            type: integer
            default: 300
            title: Window After
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
                title: Response Get Incident Timeline Api V1 Observability Incident Timeline  Incident Id  Get
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /api/v1/observability/token-breakdown:
    get:
      summary: Token Breakdown
      description: |-
        Return per-session token consumption breakdown.

        For each agent session with a ``.tokens`` sidecar file, breaks down
        token usage into estimated categories:

        - ``system_prompt_estimated``: overhead from Bernstein role templates
        - ``task_description_estimated``: tokens for the task title + description
        - ``context_estimated``: remaining input tokens (context files, tool results,
          prior conversation history, etc.)
        - ``output_tokens``: actual assistant output tokens

        Also reports ``optimization_opportunities`` - a list of human-readable
        insights when a category accounts for an unusually large share of tokens
        (e.g. "context files are 60% of input").

        Token sidecar files live at ``.sdd/runtime/{session_id}.tokens``.
        Breakdown percentages use a 4-chars/token heuristic for size estimates.

        Returns:
            Dict with ``sessions`` list and aggregate ``summary``.
      operationId: token_breakdown_api_v1_observability_token_breakdown_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
                title: Response Token Breakdown Api V1 Observability Token Breakdown Get
  /api/v1/quality:
    get:
      summary: Get Quality Metrics
      description: |-
        Return aggregated internal quality metrics (last 7 days).

        Reads from ``.sdd/metrics/`` JSONL files to compute:

        - ``per_model``: per-model success rate, avg tokens, and completion
          time distribution (p50/p90/p99).
        - ``overall``: aggregate across all models.
        - ``gate_stats``: per-gate pass/blocked/flagged counts (last 30 days).
        - ``guardrail_pass_rate``: fraction of gate checks that passed.
        - ``review_rejection_rate``: fraction of tasks that failed overall.

        Returns an empty structure when no metric data exists yet.
      operationId: get_quality_metrics_api_v1_quality_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /api/v1/quality/budget-forecast:
    get:
      summary: Get Budget Forecast
      description: Return projected spend for the active planned backlog.
      operationId: get_budget_forecast_api_v1_quality_budget_forecast_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /api/v1/quality/trend:
    get:
      summary: Get Quality Trend
      description: |-
        Return time-series quality metrics for trend visualization.

        Buckets quality data by day (default) or week and returns per-bucket
        success rates, gate pass rates, and average quality scores. Covers the
        last 90 days by default so dashboards can show weeks-to-months trends.

        Query parameters:
        - ``days``: lookback window in days (default 90, max 365).
        - ``granularity``: ``"day"`` (default) or ``"week"``.

        Returns a ``series`` list ordered by date, each entry containing:
        - ``date``: ISO date string (bucket start).
        - ``ts``: Unix timestamp of the bucket start.
        - ``tasks_total``, ``tasks_success``: raw task counts.
        - ``success_rate``: fraction of tasks that succeeded (omitted if no tasks).
        - ``gate_pass_rates``: dict of gate name → pass rate for that bucket.
        - ``avg_quality_score``: mean quality score 0-100 (omitted if no scores).
      operationId: get_quality_trend_api_v1_quality_trend_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /api/v1/quality/models:
    get:
      summary: Get Quality By Model
      description: |-
        Return per-model quality breakdown (last 30 days).

        Extended view of model performance for routing configuration and cost
        analysis. Covers a longer window than the default ``/quality`` summary.
      operationId: get_quality_by_model_api_v1_quality_models_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /api/v1/quality/file-health:
    get:
      summary: List File Health
      description: |-
        Return per-file code health scores, worst files first.

        Query parameters:
        - ``limit``: max results (default 50, max 500).
        - ``min_score``: only return files at or below this score.
        - ``grade``: filter by grade (A/B/C/D/F).

        Returns a JSON object with ``files`` list and summary statistics.
      operationId: list_file_health_api_v1_quality_file_health_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /api/v1/quality/file-health/flagged:
    get:
      summary: List Flagged Files
      description: |-
        Return files currently flagged for human review due to health degradation.

        A file is flagged when:
        - A task dropped its health score by ≥10 points, OR
        - Its total health score is below 60 (grade D or F).

        Returns ``files`` list with detailed health scores and degradation context.
      operationId: list_flagged_files_api_v1_quality_file_health_flagged_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /api/v1/quality/file-health/{file_path}:
    get:
      summary: Get File Health
      description: |-
        Return the current health score for a single file.

        Args:
            file_path: File path relative to repository root (URL-encoded).

        Returns 404 if the file has never been tracked.
      operationId: get_file_health_api_v1_quality_file_health__file_path__get
      parameters:
        - name: file_path
          in: path
          required: true
          schema:
            type: string
            title: File Path
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
        "404":
          description: File not tracked yet
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /api/v1/fleet/projects:
    get:
      summary: Fleet Projects
      description: |-
        Return aggregated per-project snapshots for the fleet overview.

        Response shape mirrors :func:`bernstein.core.fleet.web.api_projects`:

        .. code-block:: json

            {
              "projects": [ProjectSnapshot, ...],
              "errors": [],
              "stub": true|false,
              "hint": "Run `bernstein fleet --web` for the real aggregator."
            }

        ``stub: true`` means the operator UI is talking to a single-project
        server that has no fleet aggregator wired in; the ``projects`` list
        is empty in that case so the SPA can render the empty-state.
      operationId: fleet_projects_api_v1_fleet_projects_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
                title: Response Fleet Projects Api V1 Fleet Projects Get
  /api/v1/fleet/search:
    get:
      summary: Fleet Search
      description: |-
        Cross-project search stub for the topbar search bar.

        Accepts a free-text query plus the ``agent:/status:/across:`` operator
        syntax used by the frontend search component; the stub does not yet
        execute the search and instead returns the parsed filters so the SPA
        can demonstrate the round-trip while the backend implementation is
        being built.

        Returns:
            ``{"query": str, "filters": {...}, "matches": [], "stub": bool}``.
      operationId: fleet_search_api_v1_fleet_search_get
      parameters:
        - name: q
          in: query
          required: false
          schema:
            type: string
            description: Cross-project search query
            default: ""
            title: Q
          description: Cross-project search query
        - name: limit
          in: query
          required: false
          schema:
            type: integer
            maximum: 500
            minimum: 1
            default: 50
            title: Limit
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
                title: Response Fleet Search Api V1 Fleet Search Get
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /api/v1/drain:
    get:
      summary: Drain Status
      description: Check drain status.
      operationId: drain_status_api_v1_drain_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
    post:
      summary: Drain Start
      description: Begin draining -- stop accepting new task claims.
      operationId: drain_start_api_v1_drain_post
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /api/v1/drain/cancel:
    post:
      summary: Drain Cancel
      description: Cancel drain -- resume accepting claims.
      operationId: drain_cancel_api_v1_drain_cancel_post
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /api/v1/identities:
    get:
      tags:
        - identities
      summary: List Identities
      description: |-
        List agent identities with optional status/role filters.

        ``status`` is validated against the :class:`AgentIdentityStatus`
        enum by FastAPI, so an unknown value yields a ``422`` rather than
        reaching the handler and raising an unhandled ``ValueError``.
      operationId: list_identities_api_v1_identities_get
      parameters:
        - name: status
          in: query
          required: false
          schema:
            anyOf:
              - $ref: "#/components/schemas/AgentIdentityStatus"
              - type: "null"
            title: Status
        - name: role
          in: query
          required: false
          schema:
            anyOf:
              - type: string
              - type: "null"
            title: Role
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /api/v1/identities/{identity_id}:
    get:
      tags:
        - identities
      summary: Get Identity
      description: Get details for a single agent identity.
      operationId: get_identity_api_v1_identities__identity_id__get
      parameters:
        - name: identity_id
          in: path
          required: true
          schema:
            type: string
            title: Identity Id
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
        "404":
          description: Identity not found
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /api/v1/identities/{identity_id}/revoke:
    post:
      tags:
        - identities
      summary: Revoke Identity
      description: Revoke an agent identity.
      operationId: revoke_identity_api_v1_identities__identity_id__revoke_post
      parameters:
        - name: identity_id
          in: path
          required: true
          schema:
            type: string
            title: Identity Id
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
        "404":
          description: Identity not found
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /api/v1/identities/{identity_id}/audit:
    get:
      tags:
        - identities
      summary: Identity Audit
      description: Return the audit trail for an agent identity.
      operationId: identity_audit_api_v1_identities__identity_id__audit_get
      parameters:
        - name: identity_id
          in: path
          required: true
          schema:
            type: string
            title: Identity Id
        - name: limit
          in: query
          required: false
          schema:
            type: integer
            default: 100
            title: Limit
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /api/v1/.well-known/acp.json:
    get:
      summary: Acp Discovery
      description: ACP discovery document - editors poll this to find ACP-compatible agents.
      operationId: acp_discovery_api_v1__well_known_acp_json_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ACPDiscoveryResponse"
  /api/v1/acp/v0/agents:
    get:
      summary: List Acp Agents
      description: List all ACP-advertised agents.
      operationId: list_acp_agents_api_v1_acp_v0_agents_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                items:
                  $ref: "#/components/schemas/ACPAgentListEntry"
                type: array
                title: Response List Acp Agents Api V1 Acp V0 Agents Get
  /api/v1/acp/v0/agents/{agent_id}:
    get:
      summary: Get Acp Agent
      description: Get detailed metadata for a specific ACP agent.
      operationId: get_acp_agent_api_v1_acp_v0_agents__agent_id__get
      parameters:
        - name: agent_id
          in: path
          required: true
          schema:
            type: string
            title: Agent Id
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ACPAgentResponse"
        "404":
          description: ACP agent not found
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /api/v1/acp/v0/runs:
    post:
      summary: Create Acp Run
      description: |-
        Create an ACP run - creates a Bernstein task and links it.

        Editors call this when the user submits a goal via the ACP sidebar.
      operationId: create_acp_run_api_v1_acp_v0_runs_post
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ACPRunCreateRequest"
        required: true
      responses:
        "201":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ACPRunResponse"
        "400":
          description: Unknown ACP agent
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /api/v1/acp/v0/runs/{run_id}:
    get:
      summary: Get Acp Run
      description: Get ACP run status, syncing from the underlying Bernstein task.
      operationId: get_acp_run_api_v1_acp_v0_runs__run_id__get
      parameters:
        - name: run_id
          in: path
          required: true
          schema:
            type: string
            title: Run Id
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ACPRunResponse"
        "404":
          description: ACP run not found
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
    delete:
      summary: Cancel Acp Run
      description: Cancel an ACP run and its underlying Bernstein task.
      operationId: cancel_acp_run_api_v1_acp_v0_runs__run_id__delete
      parameters:
        - name: run_id
          in: path
          required: true
          schema:
            type: string
            title: Run Id
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ACPRunResponse"
        "404":
          description: ACP run not found
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /api/v1/approvals:
    get:
      tags:
        - approvals
      summary: List Approvals
      description: List all pending approval requests across task-review and pre-spawn gates.
      operationId: list_approvals_api_v1_approvals_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ListApprovalsResponse"
  /api/v1/approvals/{task_id}/approve:
    post:
      tags:
        - approvals
      summary: Approve Task
      description: |-
        Approve a pending approval request.

        Writes a .approved decision file so the orchestrator poll loop unblocks.
        The pending file is then removed.

        Args:
            task_id: Task ID to approve.
            body: Optional reason metadata.

        Returns:
            Success message.
      operationId: approve_task_api_v1_approvals__task_id__approve_post
      parameters:
        - name: task_id
          in: path
          required: true
          schema:
            type: string
            title: Task Id
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ApprovalDecisionRequest"
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                type: object
                additionalProperties:
                  type: string
                title: Response Approve Task Api V1 Approvals  Task Id  Approve Post
        "400":
          description: Invalid task_id format
        "404":
          description: No pending approval for task
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /api/v1/approvals/{task_id}/reject:
    post:
      tags:
        - approvals
      summary: Reject Task
      description: |-
        Reject a pending approval request.

        Writes a .rejected decision file so the orchestrator poll loop unblocks.
        The pending file is then removed.

        Args:
            task_id: Task ID to reject.
            body: Optional reason metadata.

        Returns:
            Success message.
      operationId: reject_task_api_v1_approvals__task_id__reject_post
      parameters:
        - name: task_id
          in: path
          required: true
          schema:
            type: string
            title: Task Id
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ApprovalDecisionRequest"
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                type: object
                additionalProperties:
                  type: string
                title: Response Reject Task Api V1 Approvals  Task Id  Reject Post
        "400":
          description: Invalid task_id format
        "404":
          description: No pending approval for task
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /api/v1/approvals/queue:
    get:
      tags:
        - approvals
      summary: List Queued Approvals
      description: |-
        List pending tool-call approvals (op-002).

        Args:
            session_id: Optional filter; when given only approvals for that
                session are returned.
      operationId: list_queued_approvals_api_v1_approvals_queue_get
      parameters:
        - name: session_id
          in: query
          required: false
          schema:
            anyOf:
              - type: string
              - type: "null"
            title: Session Id
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/QueuedApprovalsResponse"
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /api/v1/approvals/{approval_id}/resolve:
    post:
      tags:
        - approvals
      summary: Resolve Queued Approval
      description: |-
        Resolve a queued approval with ``allow``, ``reject``, or ``always``.

        The request body must echo the ``nonce`` the gate issued when the
        approval was queued. Mismatches return ``409 NONCE_MISMATCH``; a
        nonce replayed against an already-resolved or evicted approval
        returns ``410 NONCE_EXPIRED``.
      operationId: resolve_queued_approval_api_v1_approvals__approval_id__resolve_post
      parameters:
        - name: approval_id
          in: path
          required: true
          schema:
            type: string
            title: Approval Id
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ResolveRequest"
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                type: object
                additionalProperties:
                  type: string
                title: Response Resolve Queued Approval Api V1 Approvals  Approval Id  Resolve Post
        "400":
          description: Invalid approval id or decision
        "404":
          description: No pending approval with that id
        "409":
          description: NONCE_MISMATCH
        "410":
          description: NONCE_EXPIRED
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /api/v1/approvals/live-fragment:
    get:
      tags:
        - approvals
      summary: Approvals Live Fragment
      description: |-
        Return an HTML fragment the live-session page embeds.

        Each pending approval becomes a row with three buttons that POST the
        resolution back to ``/approvals/{id}/resolve``. The fragment is
        intentionally minimal so it can be inlined into the existing live
        dashboard without pulling a new framework.
      operationId: approvals_live_fragment_api_v1_approvals_live_fragment_get
      parameters:
        - name: session_id
          in: query
          required: false
          schema:
            anyOf:
              - type: string
              - type: "null"
            title: Session Id
      responses:
        "200":
          description: Successful Response
          content:
            text/html:
              schema:
                type: string
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /api/v1/plans:
    get:
      tags:
        - plans
      summary: List Plans
      description: |-
        List all plans, optionally filtered by status.

        Query params:
            status: Filter by plan status (pending, approved, rejected, expired).
      operationId: list_plans_api_v1_plans_get
      parameters:
        - name: status
          in: query
          required: false
          schema:
            anyOf:
              - type: string
              - type: "null"
            title: Status
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                type: array
                items:
                  type: object
                  additionalProperties: true
                title: Response List Plans Api V1 Plans Get
        "400":
          description: Invalid status filter
        "404":
          description: Plan mode is not enabled on this server
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /api/v1/plans/{plan_id}:
    get:
      tags:
        - plans
      summary: Get Plan
      description: Get a single plan by ID.
      operationId: get_plan_api_v1_plans__plan_id__get
      parameters:
        - name: plan_id
          in: path
          required: true
          schema:
            type: string
            title: Plan Id
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
                title: Response Get Plan Api V1 Plans  Plan Id  Get
        "404":
          description: Plan not found, or plan mode is not enabled on this server
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /api/v1/plans/{plan_id}/approve:
    post:
      tags:
        - plans
      summary: Approve Plan
      description: |-
        Approve a plan: promotes all its PLANNED tasks to OPEN.

        This is the key operation: once approved, the orchestrator will
        pick up the tasks and start spawning agents.
      operationId: approve_plan_api_v1_plans__plan_id__approve_post
      parameters:
        - name: plan_id
          in: path
          required: true
          schema:
            type: string
            title: Plan Id
      requestBody:
        content:
          application/json:
            schema:
              anyOf:
                - $ref: "#/components/schemas/PlanDecisionRequest"
                - type: "null"
              title: Body
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
                title: Response Approve Plan Api V1 Plans  Plan Id  Approve Post
        "404":
          description: Plan not found, or plan mode is not enabled on this server
        "409":
          description: Plan already decided, or the plan changed after it was rendered for review
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /api/v1/plans/{plan_id}/reject:
    post:
      tags:
        - plans
      summary: Reject Plan
      description: |-
        Reject a plan: cancels all its PLANNED tasks.

        Rejected tasks are moved to CANCELLED status so they never execute.
      operationId: reject_plan_api_v1_plans__plan_id__reject_post
      parameters:
        - name: plan_id
          in: path
          required: true
          schema:
            type: string
            title: Plan Id
      requestBody:
        content:
          application/json:
            schema:
              anyOf:
                - $ref: "#/components/schemas/PlanDecisionRequest"
                - type: "null"
              title: Body
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
                title: Response Reject Plan Api V1 Plans  Plan Id  Reject Post
        "404":
          description: Plan not found, or plan mode is not enabled on this server
        "409":
          description: Plan already decided, or the plan changed after it was rendered for review
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /api/v1/gateway/metrics:
    get:
      summary: Gateway Metrics
      description: |-
        Return per-tool MCP call metrics from the active gateway session.

        Returns an empty ``metrics`` dict when no gateway is running.
        Clients can use ``active`` to distinguish the two cases.
      operationId: gateway_metrics_api_v1_gateway_metrics_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /api/v1/slo:
    get:
      summary: Get Slo Status
      description: Return current SLO dashboard data.
      operationId: get_slo_status_api_v1_slo_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /api/v1/slo/budget:
    get:
      summary: Get Error Budget
      description: Return error budget details in focused format.
      operationId: get_error_budget_api_v1_slo_budget_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /api/v1/slo/burndown:
    get:
      summary: Get Slo Burndown
      description: |-
        Return SLO burn-down rate visualization data .

        Provides:
        - Current SLO compliance and error budget fraction
        - Burn rate relative to the allowed failure rate (1.0 = on-target)
        - Linear projection of days until the SLO is breached
        - Sparkline data points for rendering a burn-down chart
        - Human-readable breach projection summary

        Example response::

            {
              "slo_name": "task_success",
              "slo_target": 0.9,
              "slo_current": 0.942,
              "burn_rate": 0.3,
              "burn_rate_per_day": 0.05,
              "budget_fraction": 0.72,
              "budget_consumed_pct": 28.0,
              "days_to_breach": 6.1,
              "breach_projection": "SLO will breach in 6.1 days at current rate",
              "status": "green",
              "sparkline": [...]
            }
      operationId: get_slo_burndown_api_v1_slo_burndown_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /api/v1/slo/reset:
    post:
      summary: Reset Slo State
      description: Reset SLO tracker to initial state (no persisted data cleared).
      operationId: reset_slo_state_api_v1_slo_reset_post
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /api/v1/sla:
    get:
      summary: List Contracts
      description: Return every registered SLA contract.
      operationId: list_contracts_api_v1_sla_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /api/v1/sla/receipts:
    get:
      summary: List Receipts
      description: Return the operator projection of every persisted violation receipt.
      operationId: list_receipts_api_v1_sla_receipts_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /api/v1/sla/receipts/{receipt_id}/verify:
    get:
      summary: Verify Receipt Endpoint
      description: Verify a persisted violation receipt offline and return the verdict.
      operationId: verify_receipt_endpoint_api_v1_sla_receipts__receipt_id__verify_get
      parameters:
        - name: receipt_id
          in: path
          required: true
          schema:
            type: string
            title: Receipt Id
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /api/v1/sla/{contract_id}:
    get:
      summary: Show Contract
      description: Return one SLA contract's full record.
      operationId: show_contract_api_v1_sla__contract_id__get
      parameters:
        - name: contract_id
          in: path
          required: true
          schema:
            type: string
            title: Contract Id
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /api/v1/sla/{contract_id}/report:
    get:
      summary: Contract Report
      description: Return the deterministic error-budget report for a contract.
      operationId: contract_report_api_v1_sla__contract_id__report_get
      parameters:
        - name: contract_id
          in: path
          required: true
          schema:
            type: string
            title: Contract Id
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /api/v1/metrics/custom:
    get:
      summary: Get Custom Metrics
      description: |-
        Evaluate all configured custom metrics and return current values.

        Returns an object with a ``metrics`` list. Each entry contains:
        - ``name``: metric name
        - ``value``: computed float value
        - ``unit``: display unit (e.g. ``"lines/$"``)
        - ``description``: optional human-readable description
        - ``error``: present only when evaluation failed

        Returns 200 with an empty list if no custom metrics are configured.
      operationId: get_custom_metrics_api_v1_metrics_custom_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /api/v1/metrics/custom/schema:
    get:
      summary: Get Custom Metrics Schema
      description: |-
        Return the configured custom metric definitions (formulas and units).

        Returns the schema without evaluating - useful for documentation and
        formula validation checks.
      operationId: get_custom_metrics_schema_api_v1_metrics_custom_schema_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /api/v1/sbom/generate:
    post:
      tags:
        - sbom
      summary: Generate SBOM and optionally run vulnerability scan
      description: |-
        Generate a CycloneDX or SPDX SBOM from installed packages.

        After generation, optionally run ``osv-scanner`` or ``grype`` for
        vulnerability scanning.  When ``block_on_critical=true`` and critical
        findings are detected, responds with HTTP 422 so CI/CD pipelines can
        gate merges on vulnerability status.

        SBOM artifacts are written to ``.sdd/artifacts/sbom/``.
      operationId: generate_sbom_api_v1_sbom_generate_post
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SBOMGenerateRequest"
        required: true
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SBOMGenerateResponse"
        "400":
          description: Unknown SBOM format
        "422":
          description: Critical vulnerabilities found (gate blocked)
        "503":
          description: Server workdir not configured
  /api/v1/sbom/artifacts:
    get:
      tags:
        - sbom
      summary: List generated SBOM artifact files
      description: List previously generated SBOM artifact files from ``.sdd/artifacts/sbom/``.
      operationId: list_sbom_artifacts_api_v1_sbom_artifacts_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SBOMListResponse"
        "503":
          description: Server workdir not configured
  /api/v1/hooks/{session_id}:
    post:
      summary: Receive Hook
      description: |-
        Receive a hook event from Claude Code.

        Claude Code sends structured JSON with at minimum a ``hook_event_name``
        field.  The event is parsed, persisted to a JSONL sidecar, and triggers
        side effects (heartbeat touch, completion markers, etc.).

        The request body is verified against
        ``X-Bernstein-Hook-Signature-256`` (HMAC-SHA256 over the raw body,
        keyed with ``BERNSTEIN_HOOK_SECRET``) *before* any parsing or
        filesystem work - this is the authentication boundary for the
        endpoint. The ``session_id`` is then validated against
        a strict allowlist to prevent path traversal.

        Args:
            session_id: Agent session identifier from the URL path.
            request: The incoming FastAPI request.

        Returns:
            JSON response with status and action taken, 401 if signature
            verification fails, or 400 if ``session_id`` is unsafe / body
            is not valid JSON.
      operationId: receive_hook_api_v1_hooks__session_id__post
      parameters:
        - name: session_id
          in: path
          required: true
          schema:
            type: string
            title: Session Id
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /api/v1/export/tasks:
    get:
      summary: Export Tasks
      description: |-
        Export tasks as CSV or JSON.

        Query params:
            format: ``csv`` or ``json`` (default ``json``).
            limit: Optional max number of tasks to return. Pushed into
                ``TaskStore.list_tasks`` so large stores no longer materialise
                the whole table (issue #1728 finding 3).
            offset: Optional number of tasks to skip before returning rows.

        The export is a whole-store read, so it narrows to the caller's tenant
        scope the same way the paginated task list does. The scope is pushed into
        ``list_tasks`` rather than applied to the returned rows so that it is
        ``limit``/``offset`` that page through the caller's own tasks: filtering
        after the slice would page through every tenant's and return short pages.
      operationId: export_tasks_api_v1_export_tasks_get
      parameters:
        - name: format
          in: query
          required: false
          schema:
            type: string
            default: json
            title: Format
        - name: limit
          in: query
          required: false
          schema:
            anyOf:
              - type: integer
              - type: "null"
            title: Limit
        - name: offset
          in: query
          required: false
          schema:
            anyOf:
              - type: integer
              - type: "null"
            title: Offset
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /api/v1/export/agents:
    get:
      summary: Export Agents
      description: |-
        Export agent snapshots as CSV or JSON.

        Query params:
            format: ``csv`` or ``json`` (default ``json``).
      operationId: export_agents_api_v1_export_agents_get
      parameters:
        - name: format
          in: query
          required: false
          schema:
            type: string
            default: json
            title: Format
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /api/v1/grafana/dashboard:
    get:
      summary: Grafana Dashboard Endpoint
      description: |-
        Generate and return the Grafana dashboard JSON.

        Query params:
            datasource: Prometheus datasource name (default ``Prometheus``).
      operationId: grafana_dashboard_endpoint_api_v1_grafana_dashboard_get
      parameters:
        - name: datasource
          in: query
          required: false
          schema:
            type: string
            default: Prometheus
            title: Datasource
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /api/v1/dashboard/tasks/{task_id}:
    get:
      summary: Task Detail
      description: |-
        Return detailed task view including log tail and progress.

        Args:
            task_id: Task identifier.
      operationId: task_detail_api_v1_dashboard_tasks__task_id__get
      parameters:
        - name: task_id
          in: path
          required: true
          schema:
            type: string
            title: Task Id
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TaskDetailResponse"
        "404":
          description: Task not found
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /api/v1/dashboard/tasks/{task_id}/logs/stream:
    get:
      summary: Task Log Stream
      description: |-
        Stream agent logs for a task via Server-Sent Events.

        The stream sends new log content as ``log`` events and closes
        after the task completes or ``_MAX_IDLE_TICKS`` seconds of no new data.
      operationId: task_log_stream_api_v1_dashboard_tasks__task_id__logs_stream_get
      parameters:
        - name: task_id
          in: path
          required: true
          schema:
            type: string
            title: Task Id
      responses:
        "200":
          description: Server-Sent Events stream. The response body does not terminate.
          content:
            text/event-stream:
              schema:
                type: string
        "404":
          description: Task not found
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /api/v1/dashboard/tasks/{task_id}/diff:
    get:
      summary: Task Diff
      description: |-
        Return the diff for a task's working branch against the base ref.

        Strategy:
            1. Resolve the working branch from the task's ``assigned_agent`` --
               ``agent/<session-id>``. If no agent is assigned (or the branch
               does not exist yet), fall back to ``git diff HEAD`` so the user
               still sees uncommitted scratch work.
            2. Run ``git diff <base>...<branch>`` (three-dot, symmetric
               difference relative to the merge base) and parse the output into
               a structured per-file representation.
            3. Cap the unified diff at ``_DIFF_MAX_BYTES`` to keep payloads sane.

        The sync ``_run_git`` helper is reused (it is also called from other
        sync helpers in this module). To keep the event loop responsive under
        load (issue #1723) every blocking ``_run_git`` invocation is offloaded
        to the default executor via ``asyncio.to_thread``. The helper itself
        stays sync so non-route callers keep working.
      operationId: task_diff_api_v1_dashboard_tasks__task_id__diff_get
      parameters:
        - name: task_id
          in: path
          required: true
          schema:
            type: string
            title: Task Id
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TaskDiffResponse"
        "404":
          description: Task not found
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /api/v1/dashboard/tasks/{task_id}/trace:
    get:
      summary: Task Trace
      description: |-
        Return the timeline of trace events for *task_id*.

        The endpoint is read-only and idempotent. A missing task returns 404; a
        valid task with no trace returns 200 + an empty events list (the FE
        renders an empty-state card in that case).
      operationId: task_trace_api_v1_dashboard_tasks__task_id__trace_get
      parameters:
        - name: task_id
          in: path
          required: true
          schema:
            type: string
            title: Task Id
        - name: limit
          in: query
          required: false
          schema:
            type: integer
            maximum: 2000
            minimum: 1
            default: 500
            title: Limit
        - name: cursor
          in: query
          required: false
          schema:
            type: integer
            minimum: 0
            default: 0
            title: Cursor
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TraceTimelineResponse"
        "404":
          description: Task not found
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /api/v1/health/deps:
    get:
      summary: Health Deps
      description: |-
        Return health status with dependency checks.

        Checks: server, store, adapters, sse_bus.
        Overall status is ``healthy`` if all dependencies are ok,
        ``degraded`` if any are degraded, ``unhealthy`` if any are down.
      operationId: health_deps_api_v1_health_deps_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HealthDepsResponse"
  /api/v1/tasks/batch-ops:
    post:
      tags:
        - batch-operations
      summary: Batch Operations
      description: |-
        Execute a batch operation on multiple tasks.

        Supported actions:
        - **cancel**: Cancel all specified tasks.
        - **retry**: Reset failed tasks back to open.
        - **reprioritize**: Update priority on all specified tasks (requires ``priority``).
        - **tag**: Add tags to all specified tasks (requires ``tags``).

        Returns a result with lists of succeeded and failed task IDs.
      operationId: batch_operations_api_v1_tasks_batch_ops_post
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/BatchRequest"
        required: true
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/BatchResult"
        "422":
          description: Invalid batch request
  /api/v1/audit:
    get:
      tags:
        - audit
      summary: Query Audit Log
      description: |-
        Query the audit log with filtering and pagination.

        Returns:
            Dict with items, total, page, page_size. Items are normalised
            through :func:`_normalise_audit_row` so the web GUI table can
            render every row without optional-chain dance.
      operationId: query_audit_log_api_v1_audit_get
      parameters:
        - name: event_type
          in: query
          required: false
          schema:
            anyOf:
              - type: string
              - type: "null"
            title: Event Type
        - name: search
          in: query
          required: false
          schema:
            anyOf:
              - type: string
              - type: "null"
            title: Search
        - name: page
          in: query
          required: false
          schema:
            type: integer
            default: 1
            title: Page
        - name: page_size
          in: query
          required: false
          schema:
            type: integer
            default: 50
            title: Page Size
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
                title: Response Query Audit Log Api V1 Audit Get
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /api/v1/audit/verify:
    get:
      tags:
        - audit
      summary: Audit Verify
      description: |-
        Lightweight HMAC chain integrity probe for the web GUI banner.

        Walks ``.sdd/audit/*.jsonl`` events and returns a fully-populated
        payload (no nulls in core scalar fields) so the GUI's
        ``ChainStatusBanner`` has something to render even when the audit
        directory hasn't been initialised yet. Full Sigstore / Merkle
        reconciliation lives in the lineage-v1 verifier CLI.
      operationId: audit_verify_api_v1_audit_verify_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
                title: Response Audit Verify Api V1 Audit Verify Get
    post:
      tags:
        - audit
      summary: Audit Reverify
      description: |-
        Re-walk the audit chain.

        Behaviourally identical to ``GET /audit/verify`` for the lightweight
        probe - the operator-visible "Re-verify" button in the GUI just wants
        a fresh walk and an up-to-date payload. Accepts ``{from_chunk}`` so
        future implementations can scope the walk; today the field is read
        and echoed but not used to slice the chain.
      operationId: audit_reverify_api_v1_audit_verify_post
      requestBody:
        content:
          application/json:
            schema:
              anyOf:
                - $ref: "#/components/schemas/VerifyChainRequest"
                - type: "null"
              title: Body
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
                title: Response Audit Reverify Api V1 Audit Verify Post
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /api/v1/audit/export:
    post:
      tags:
        - audit
      summary: Audit Export
      description: |-
        Stream the filtered audit log as CSV or JSONL.

        Same filter semantics as ``GET /audit`` (``event_type``, ``search``,
        ``from``, ``to``); returns the entire matching set in one body, no
        pagination - operators expect to download the whole filtered slice.
        Used by the web GUI Export menu (CSV / JSONL buttons).
      operationId: audit_export_api_v1_audit_export_post
      parameters:
        - name: format
          in: query
          required: false
          schema:
            type: string
            default: csv
            title: Format
        - name: event_type
          in: query
          required: false
          schema:
            anyOf:
              - type: string
              - type: "null"
            title: Event Type
        - name: search
          in: query
          required: false
          schema:
            anyOf:
              - type: string
              - type: "null"
            title: Search
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /api/v1/graphql:
    post:
      tags:
        - graphql
      summary: Graphql Endpoint
      description: |-
        Execute a GraphQL query.

        Accepts a standard GraphQL request body and resolves the query
        against the in-memory task store.

        Args:
            req: GraphQL request body with query, optional variables and operationName.
            request: FastAPI request (provides access to app state).

        Returns:
            GraphQL response with ``data`` or ``errors``.
      operationId: graphql_endpoint_api_v1_graphql_post
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/GraphQLRequest"
        required: true
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
                title: Response Graphql Endpoint Api V1 Graphql Post
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /api/v1/graduation/status:
    get:
      tags:
        - graduation
      summary: Graduation Status
      description: |-
        Return graduation stage and metrics for all tracked sessions.

        Returns:
            JSON with ``sessions`` list and ``total`` count.
      operationId: graduation_status_api_v1_graduation_status_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /api/v1/graduation/config/policies:
    get:
      tags:
        - graduation
      summary: Get Policies
      description: |-
        Return the current graduation stage policies.

        Returns:
            JSON mapping stage names to policy thresholds.
      operationId: get_policies_api_v1_graduation_config_policies_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /api/v1/graduation/{session_id}:
    get:
      tags:
        - graduation
      summary: Session Graduation
      description: |-
        Return graduation state for a specific session.

        Args:
            session_id: The session identifier to look up.

        Returns:
            JSON with stage, metrics, promotion log, and graduation readiness.

        Raises:
            HTTPException: 404 when no record exists for *session_id*.
      operationId: session_graduation_api_v1_graduation__session_id__get
      parameters:
        - name: session_id
          in: path
          required: true
          schema:
            type: string
            title: Session Id
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
        "404":
          description: No graduation record for session
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /api/v1/graduation/{session_id}/promote:
    post:
      tags:
        - graduation
      summary: Promote Session
      description: |-
        Manually promote a session to the next graduation stage.

        Args:
            session_id: Session to promote.
            body: Promotion reason and who initiated it.

        Returns:
            JSON with ``from_stage``, ``to_stage``, and ``promoted: true``.

        Raises:
            HTTPException: 404 when no record exists.
            HTTPException: 409 when already at the terminal (autonomous) stage.
      operationId: promote_session_api_v1_graduation__session_id__promote_post
      parameters:
        - name: session_id
          in: path
          required: true
          schema:
            type: string
            title: Session Id
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PromoteRequest"
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
        "404":
          description: No graduation record for session
        "409":
          description: Already at terminal stage
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /api/v1/graduation/{session_id}/record-event:
    post:
      tags:
        - graduation
      summary: Record Task Event
      description: |-
        Record a task completion or failure for graduation metric tracking.

        The orchestrator or CLI calls this after each task completes/fails so
        the graduation framework can accumulate per-stage metrics and determine
        when the session qualifies for the next stage.

        Args:
            session_id: The session that executed the task.
            body: Task event details.

        Returns:
            JSON with updated stage, metrics, and graduation readiness.

        Raises:
            HTTPException: 422 when *initial_stage* is not a valid stage name.
      operationId: record_task_event_api_v1_graduation__session_id__record_event_post
      parameters:
        - name: session_id
          in: path
          required: true
          schema:
            type: string
            title: Session Id
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/RecordEventRequest"
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
        "422":
          description: Invalid graduation stage
  /api/v1/handoff/{token}:
    get:
      summary: Claim Handoff Token
      description: |-
        Claim a handoff token and return the session identity + tail.

        Args:
            token: Opaque urlsafe token presented by the dashboard.
            request: FastAPI request (used to resolve the workdir).

        Returns:
            JSON envelope with ``session_id``, ``task_id``,
            ``source_surface``, ``claimed_at``, ``note`` and ``tail`` (a
            list of recent stream entries).

        Raises:
            HTTPException: ``404`` for unknown tokens, ``410`` for expired
            or already-claimed tokens.
      operationId: claim_handoff_token_api_v1_handoff__token__get
      parameters:
        - name: token
          in: path
          required: true
          schema:
            type: string
            title: Token
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /api/v1/team:
    get:
      summary: Team Summary
      description: |-
        Return a summary of the current team state.

        Includes total members, active/finished counts, role distribution,
        and full per-member metadata.
      operationId: team_summary_api_v1_team_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /api/v1/team/active:
    get:
      summary: Team Active
      description: Return only active team members.
      operationId: team_active_api_v1_team_active_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /api/v1/team/{agent_id}:
    get:
      summary: Team Member
      description: |-
        Return metadata for a single team member.

        Returns 404 if the agent is not in the team roster.
      operationId: team_member_api_v1_team__agent_id__get
      parameters:
        - name: agent_id
          in: path
          required: true
          schema:
            type: string
            title: Agent Id
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /api/v1/metrics/provider-latency:
    get:
      summary: Provider Latency Current
      description: |-
        Return current p50/p95/p99 latency percentiles for all tracked providers.

        Each entry in the response includes a ``baseline_p99_ms`` derived from the
        past 7 days of data. When ``p99_ms`` exceeds ``baseline_p99_ms x 2``, the
        entry carries ``"degraded": true``.
      operationId: provider_latency_current_api_v1_metrics_provider_latency_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /api/v1/metrics/provider-latency/history:
    get:
      summary: Provider Latency History
      description: |-
        Return raw latency samples for time-series charting.

        Each sample has: ``timestamp``, ``provider``, ``model``, ``latency_ms``.
        Samples are ordered chronologically. Use ``hours`` to control the lookback
        window (default 24h, max 7 days).
      operationId: provider_latency_history_api_v1_metrics_provider_latency_history_get
      parameters:
        - name: provider
          in: query
          required: false
          schema:
            anyOf:
              - type: string
              - type: "null"
            description: Filter by provider name
            title: Provider
          description: Filter by provider name
        - name: model
          in: query
          required: false
          schema:
            anyOf:
              - type: string
              - type: "null"
            description: Filter by model identifier
            title: Model
          description: Filter by model identifier
        - name: hours
          in: query
          required: false
          schema:
            type: integer
            maximum: 168
            minimum: 1
            description: Hours of history to return (1-168)
            default: 24
            title: Hours
          description: Hours of history to return (1-168)
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /api/v1/metrics/predictions:
    get:
      summary: Get Predictions
      description: |-
        Evaluate all predictive forecasts and return active alerts.

        Checks three forecast dimensions:

        - **Budget exhaustion**: At current spend velocity, when will the
          budget cap be reached?
        - **Completion rate decline**: Is the task completion rate trending
          downward, indicating the run will take longer than expected?
        - **Run duration overrun**: Based on current throughput, will the run
          exceed the configured time window?

        Use ``budget_cap`` to enable the budget forecast. The run duration
        forecast requires at least one completed task.

        Both numeric parameters are echoed back in the response body, so both
        refuse non-finite values with a 422 instead of admitting them: a range
        bound alone does not exclude them (``inf >= 0.0`` is true, and every
        comparison against ``NaN`` is false), and the JSON renderer cannot
        serialise either one.

        The budget forecast is scoped to ``tenant_id``: the spend series it is
        built from is narrowed to cost points recorded for the caller's tenant,
        the same way the rest of the cost surface is (see
        ``load_cost_history``).  Cost points written before per-tenant
        attribution existed are treated as the default tenant's spend, so a
        legacy single-tenant install keeps its existing numbers.

        Returns a list of ``alerts`` ordered by severity (critical first).
        Each alert has: ``kind``, ``severity``, ``message``,
        ``minutes_until_impact``, ``confidence``.
      operationId: get_predictions_api_v1_metrics_predictions_get
      parameters:
        - name: budget_cap
          in: query
          required: false
          schema:
            type: number
            minimum: 0
            description: Budget ceiling in USD (0 = skip budget forecast)
            default: 0
            title: Budget Cap
          description: Budget ceiling in USD (0 = skip budget forecast)
        - name: window_hours
          in: query
          required: false
          schema:
            type: number
            maximum: 72
            minimum: 0.1
            description: Configured run window in hours (default 4)
            default: 4
            title: Window Hours
          description: Configured run window in hours (default 4)
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /api/v1/sessions/{session_id}/peek:
    get:
      summary: Peek Session
      description: |-
        Return the recent stream-tail entries for ``session_id``.

        Args:
            session_id: Bernstein session whose tail to read.
            request: FastAPI request - used to resolve the workdir and the
                ``tail`` query argument.

        Returns:
            JSON envelope with ``session_id`` plus a ``tail`` list of
            ``{ts, surface, text}`` entries in chronological order. An
            empty list signals "buffer not initialised yet" rather than an
            error so the polling page renders a blank pane while it waits.
      operationId: peek_session_api_v1_sessions__session_id__peek_get
      parameters:
        - name: session_id
          in: path
          required: true
          schema:
            type: string
            title: Session Id
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /api/v1/sessions/{session_id}/send:
    post:
      summary: Send To Session
      description: |-
        Pipe one line of operator input into ``session_id``'s stdin.

        The send-bar tile on the dashboard POSTs ``{"text": "..."}`` here; we
        forward through :func:`bernstein.core.agents.agent_ipc.send_message`,
        which writes the line into the agent's registered stdin pipe.

        Args:
            session_id: Slug-shaped session id; must pass the same validator
                as the peek endpoint.
            request: FastAPI request (unused beyond routing-level checks but
                present so the bearer-auth middleware sees the same shape as
                our other mutating routes).
            payload: JSON body with a single ``text`` field. Empty / missing
                text is rejected with ``400``; oversize payloads above
                :data:`MAX_SEND_BYTES` are rejected with ``413``.

        Returns:
            JSON envelope with ``session_id`` and ``delivered`` (``True`` if
            the line reached a registered stdin pipe, ``False`` if no pipe
            is registered for this session).  The 200/404 split lets the
            front-end keep the input enabled but warn the operator when the
            agent has no live pipe yet.
      operationId: send_to_session_api_v1_sessions__session_id__send_post
      parameters:
        - name: session_id
          in: path
          required: true
          schema:
            type: string
            title: Session Id
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties:
                type: string
              title: Payload
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /api/v1/orchestrator/holds:
    get:
      tags:
        - orchestrator-holds
      summary: Get Holds
      description: List all currently active (non-expired) holds.
      operationId: get_holds_api_v1_orchestrator_holds_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HoldListResponse"
    post:
      tags:
        - orchestrator-holds
      summary: Create Hold
      description: Acquire a new hold, preventing orchestrator self-stop while active.
      operationId: create_hold_api_v1_orchestrator_holds_post
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/HoldCreateRequest"
        required: true
      responses:
        "200":
          description: Hold acquired
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HoldResponse"
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /api/v1/orchestrator/holds/{hold_id}:
    delete:
      tags:
        - orchestrator-holds
      summary: Delete Hold
      description: Release a hold by id.
      operationId: delete_hold_api_v1_orchestrator_holds__hold_id__delete
      parameters:
        - name: hold_id
          in: path
          required: true
          schema:
            type: string
            title: Hold Id
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                type: object
                additionalProperties:
                  type: boolean
                title: Response Delete Hold Api V1 Orchestrator Holds  Hold Id  Delete
        "404":
          description: Hold not found (already released or expired)
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /api/v1/orchestrator/holds/{hold_id}/renew:
    post:
      tags:
        - orchestrator-holds
      summary: Renew Hold Endpoint
      description: Heartbeat-renew a hold, extending its expiry by another grace window.
      operationId: renew_hold_endpoint_api_v1_orchestrator_holds__hold_id__renew_post
      parameters:
        - name: hold_id
          in: path
          required: true
          schema:
            type: string
            title: Hold Id
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HoldResponse"
        "404":
          description: Hold not found (never existed, released, or already expired)
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /api/v1/review-board/runs:
    get:
      summary: Review Board Runs
      description: List run ids that have a journal to project, newest first.
      operationId: review_board_runs_api_v1_review_board_runs_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /api/v1/review-board/runs/{run_id}:
    get:
      summary: Review Board Projection
      description: |-
        Serve the board projection receipt for ``run_id``.

        The response is a deterministic function of the run's journal file:
        the same journal bytes serve the same ``board`` and
        ``projection_hash`` from any server, so a reviewer can cross-check two
        operators (or the API against a local ``project_run`` fold) byte for
        byte. ``journal_verified=false`` marks a chain that no longer
        recomputes - the board is still rendered but must not be trusted.
      operationId: review_board_projection_api_v1_review_board_runs__run_id__get
      parameters:
        - name: run_id
          in: path
          required: true
          schema:
            type: string
            title: Run Id
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /api/v1/review-board/runs/{run_id}/evidence/{task_id}:
    get:
      summary: Review Board Evidence
      description: |-
        Serve the sealed evidence bundle for a board card.

        The bundle is the #2362 proof-of-done artifact: content-addressed
        items, the gate verdict, the producing signature, and the audit-chain
        entry hash. ``bundle_hash`` is recomputed from the canonical binding
        bytes on every read so the drawer always shows the bundle's current
        identity.
      operationId: review_board_evidence_api_v1_review_board_runs__run_id__evidence__task_id__get
      parameters:
        - name: run_id
          in: path
          required: true
          schema:
            type: string
            title: Run Id
        - name: task_id
          in: path
          required: true
          schema:
            type: string
            title: Task Id
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /api/v1/review-board/runs/{run_id}/diff/{task_id}:
    get:
      summary: Review Board Diff
      description: |-
        Serve the captured task diff for the card drawer's diff viewer.

        The diff bytes were captured beside the run journal at completion time
        (``task_diff_captured``), so they are exactly what executed and are
        available against a detached run - no live ``git`` at review time. The
        served bytes are re-hashed and cross-checked against the journal-chained
        capture hash: ``verified`` is ``true`` only when the diff a reviewer folds
        open equals the diff that was captured and the chain still recomputes.
      operationId: review_board_diff_api_v1_review_board_runs__run_id__diff__task_id__get
      parameters:
        - name: run_id
          in: path
          required: true
          schema:
            type: string
            title: Run Id
        - name: task_id
          in: path
          required: true
          schema:
            type: string
            title: Task Id
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /api/v1/dashboard/review-board/runs/{run_id}/tasks/{task_id}/review:
    post:
      summary: Review Board Action
      description: |-
        Record an operator board decision as a chained, signed receipt.

        The scope gate is enforced upstream by the dashboard-auth middleware
        (operator scope required for this write); the acting principal arrives on
        ``request.state.dashboard_principal``. The decision row is appended via
        ``EventJournal.resume`` so it chains onto the verified journal tail and
        fails closed on a poisoned chain (``409``).
      operationId: review_board_action_api_v1_dashboard_review_board_runs__run_id__tasks__task_id__review_post
      parameters:
        - name: run_id
          in: path
          required: true
          schema:
            type: string
            title: Run Id
        - name: task_id
          in: path
          required: true
          schema:
            type: string
            title: Task Id
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ReviewActionRequest"
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /api/v1/dashboard/review-board:
    get:
      summary: Review Board Page
      description: |-
        Serve the review-board page.

        The page is a pure consumer of the projection endpoints above plus the
        existing ``/events`` SSE stream; it holds no state of its own, so
        reloading it (or opening it on a second machine against the same
        journal) renders the identical board.
      operationId: review_board_page_api_v1_dashboard_review_board_get
      responses:
        "200":
          description: Successful Response
          content:
            text/html:
              schema:
                type: string
  /api/v1/artifacts:
    get:
      summary: List Artifacts
      description: Return every artifact key the local lineage spines carry.
      operationId: list_artifacts_api_v1_artifacts_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /api/v1/artifacts/health:
    get:
      summary: Artifact Health
      description: |-
        Return the canonical health verdict for ``?uri=``.

        Query parameters:

        * ``uri`` (required) - the artifact key.
        * ``at`` - evaluation instant; defaults to the wall clock. Pin it to
          reproduce a verdict byte-for-byte against the CLI.
        * ``cadence_seconds`` - declared refresh cadence; omitted means the cadence
          leg reports ``not_applicable``.

        The body is the exact string the CLI prints for the same state and instant,
        byte for byte. The status is always 200: the verdict is the payload, and a
        red artifact is a successfully computed answer, not a failed request.
      operationId: artifact_health_api_v1_artifacts_health_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /api/v1/artifacts/log:
    get:
      summary: Artifact Log Route
      description: |-
        Return productions of ``?uri=``, newest first (the attribution log).

        Recorded attempts -- tasks that declared this artifact and did not deliver it
        -- travel in the same document under ``attempts`` (issue #2559), so a
        consumer cannot see the productions without also seeing what tried and
        failed. Byte-identical to what the CLI prints for the same state.
      operationId: artifact_log_route_api_v1_artifacts_log_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /api/v1/missions:
    get:
      summary: Missions List
      description: List mission ids that have a ledger to project, newest first.
      operationId: missions_list_api_v1_missions_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /api/v1/missions/{mission_id}:
    get:
      summary: Mission Projection
      description: |-
        Serve the mission projection receipt for ``mission_id``.

        The response is a deterministic function of the mission's ledger file: the
        same ledger bytes serve the same ``status`` and ``mission_status_hash`` from
        any server, so two operators cross-check byte for byte.
        ``ledger_verified=false`` (with ``overall=unverified``) marks a chain that no
        longer recomputes -- the timeline still renders, but the screen must show the
        unverified banner instead of trusting the state.
      operationId: mission_projection_api_v1_missions__mission_id__get
      parameters:
        - name: mission_id
          in: path
          required: true
          schema:
            type: string
            title: Mission Id
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /api/v1/missions/{mission_id}/digest:
    get:
      summary: Mission Digest
      description: |-
        Serve the canonical daily progress digest for a fire instant.

        Read-only: the digest is recomputed from the ledger as a pure fold, so the
        endpoint never writes to the chain. The payload carries the ``digest_hash``,
        the ``receipt_id`` (the per-fire delivery idempotency key), and the verbatim
        ``message`` the digest projects to -- the exact bytes a chat delivery posts,
        so a caller can cross-check a posted message against this projection.
      operationId: mission_digest_api_v1_missions__mission_id__digest_get
      parameters:
        - name: mission_id
          in: path
          required: true
          schema:
            type: string
            title: Mission Id
        - name: fire_time
          in: query
          required: true
          schema:
            type: integer
            description: Integer Unix epoch of the canonical fire instant.
            title: Fire Time
          description: Integer Unix epoch of the canonical fire instant.
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /api/v1/missions/{mission_id}/evidence/{task_id}:
    get:
      summary: Mission Evidence
      description: |-
        Serve the sealed evidence bundle behind a timeline element's provenance link.

        ``bundle_hash`` is recomputed from the canonical binding bytes on every read,
        so the drawer always shows the bundle's current identity -- and a bundle that
        no longer matches the hash a phase receipt bound projects that phase as
        unverified in the mission projection above.
      operationId: mission_evidence_api_v1_missions__mission_id__evidence__task_id__get
      parameters:
        - name: mission_id
          in: path
          required: true
          schema:
            type: string
            title: Mission Id
        - name: task_id
          in: path
          required: true
          schema:
            type: string
            title: Task Id
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /gui-meta:
    get:
      tags:
        - gui
      summary: Gui Meta
      operationId: gui_meta_gui_meta_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /api/v1/gui-meta:
    get:
      tags:
        - gui
      summary: Gui Meta
      operationId: gui_meta_api_v1_gui_meta_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema: {}
  /api/csp-report:
    description: Content-Security-Policy violation sink for the landing site. This is the one path in
      this document that does not live on the operator's task server, so it carries its own
      `servers` entry below. Browsers post here on their own - the route is named by the
      `report-uri` and `report-to` directives of every response's Content-Security-Policy - and
      nothing else is expected to call it.
    servers:
      - url: https://bernstein.run
        description: The landing site. Not the task server.
    post:
      summary: Receive a Content-Security-Policy violation report
      description: "Accepts both wire formats: a single kebab-case report under a `csp-report` key
        (`report-uri`, which is what Firefox and Safari send) and an array of Reporting API
        envelopes (`report-to`, which Chrome sends and batches). Envelopes whose `type` is not
        `csp-violation` are ignored. Unauthenticated by construction, since browsers send these
        without credentials; the body is treated as hostile, summarised into the server log, and
        never echoed back."
      operationId: reportCspViolation
      requestBody:
        required: true
        content:
          application/csp-report:
            schema:
              type: object
              properties:
                csp-report:
                  type: object
                  description: One violation, kebab-case fields.
          application/reports+json:
            schema:
              type: array
              items:
                type: object
                properties:
                  type:
                    type: string
                    description: Report kind. Only `csp-violation` is read.
                  body:
                    type: object
                    description: One violation, camelCase fields.
      responses:
        "204":
          description: "Always, whatever happened. A report that was rate-limited, oversized, wrongly typed or
            unparseable gets the same empty reply as one that was logged: a browser cannot act on
            the status, and a uniform answer tells a prober nothing."
    get:
      summary: Not supported - reports arrive by POST
      operationId: cspReportMethodNotAllowed
      responses:
        "405":
          description: Always. The response names POST in its Allow header.
components:
  schemas:
    A2AAgentCardResponse:
      properties:
        name:
          type: string
          title: Name
        description:
          type: string
          title: Description
        capabilities:
          items:
            type: string
          type: array
          title: Capabilities
        protocol_version:
          type: string
          title: Protocol Version
        endpoint:
          type: string
          title: Endpoint
        provider:
          type: string
          title: Provider
      type: object
      required:
        - name
        - description
        - capabilities
        - protocol_version
        - endpoint
        - provider
      title: A2AAgentCardResponse
      description: |-
        Agent Card response for the ``/a2a/agent-card`` discovery endpoint.

        The A2A v1.0 card served at ``/.well-known/agent.json`` is built and
        signed in :mod:`bernstein.core.routes.well_known` and does not use this
        model.
    A2AArtifactRequest:
      properties:
        name:
          type: string
          title: Name
        data:
          type: string
          title: Data
          default: ""
        content_type:
          type: string
          title: Content Type
          default: text/plain
      type: object
      required:
        - name
      title: A2AArtifactRequest
      description: Body for POST /a2a/tasks/{id}/artifacts - attach an artifact.
    A2AArtifactResponse:
      properties:
        name:
          type: string
          title: Name
        content_type:
          type: string
          title: Content Type
        data:
          type: string
          title: Data
        created_at:
          type: number
          title: Created At
      type: object
      required:
        - name
        - content_type
        - data
        - created_at
      title: A2AArtifactResponse
      description: Single artifact in responses.
    A2AMessageRequest:
      properties:
        sender:
          type: string
          title: Sender
        recipient:
          type: string
          title: Recipient
        content:
          type: string
          title: Content
        task_id:
          type: string
          title: Task Id
      type: object
      required:
        - sender
        - recipient
        - content
        - task_id
      title: A2AMessageRequest
      description: Body for POST /a2a/message.
    A2AMessageResponse:
      properties:
        id:
          type: string
          title: Id
        sender:
          type: string
          title: Sender
        recipient:
          type: string
          title: Recipient
        content:
          type: string
          title: Content
        task_id:
          type: string
          title: Task Id
        direction:
          type: string
          title: Direction
        delivered:
          type: boolean
          title: Delivered
        external_endpoint:
          anyOf:
            - type: string
            - type: "null"
          title: External Endpoint
        created_at:
          type: number
          title: Created At
      type: object
      required:
        - id
        - sender
        - recipient
        - content
        - task_id
        - direction
        - delivered
        - external_endpoint
        - created_at
      title: A2AMessageResponse
      description: Serialized A2A message returned by Bernstein endpoints.
    A2ATaskResponse:
      properties:
        id:
          type: string
          title: Id
        bernstein_task_id:
          anyOf:
            - type: string
            - type: "null"
          title: Bernstein Task Id
        sender:
          type: string
          title: Sender
        message:
          type: string
          title: Message
        status:
          type: string
          title: Status
        artifacts:
          items:
            $ref: "#/components/schemas/A2AArtifactResponse"
          type: array
          title: Artifacts
        created_at:
          type: number
          title: Created At
        updated_at:
          type: number
          title: Updated At
        receipt:
          anyOf:
            - additionalProperties: true
              type: object
            - type: "null"
          title: Receipt
      type: object
      required:
        - id
        - bernstein_task_id
        - sender
        - message
        - status
        - artifacts
        - created_at
        - updated_at
      title: A2ATaskResponse
      description: |-
        Serialised A2A task in responses.

        ``receipt`` carries the lineage receipt for an inbound task (#2609): the
        execution evidence a caller verifies offline with ``bernstein a2a verify
        --receipt``. It is ``None`` on read paths, and on write paths when the
        node could not provision receipt key material - an absent receipt means
        "unattested", which a caller should treat as unverified rather than
        trusted.
    A2ATaskSendRequest:
      properties:
        sender:
          type: string
          title: Sender
        message:
          type: string
          title: Message
        role:
          type: string
          title: Role
          default: backend
      type: object
      required:
        - sender
        - message
      title: A2ATaskSendRequest
      description: Body for POST /a2a/tasks/send - receive a task from an external A2A agent.
    ACPAgentCapabilityResponse:
      properties:
        name:
          type: string
          title: Name
        description:
          type: string
          title: Description
        input_schema:
          additionalProperties: true
          type: object
          title: Input Schema
      type: object
      required:
        - name
        - description
      title: ACPAgentCapabilityResponse
      description: Single ACP capability entry.
    ACPAgentListEntry:
      properties:
        name:
          type: string
          title: Name
        description:
          type: string
          title: Description
        endpoint:
          type: string
          title: Endpoint
      type: object
      required:
        - name
        - description
        - endpoint
      title: ACPAgentListEntry
      description: Entry in the agents list.
    ACPAgentResponse:
      properties:
        name:
          type: string
          title: Name
        description:
          type: string
          title: Description
        protocol_version:
          type: string
          title: Protocol Version
        capabilities:
          items:
            $ref: "#/components/schemas/ACPAgentCapabilityResponse"
          type: array
          title: Capabilities
        endpoint:
          type: string
          title: Endpoint
        provider:
          type: string
          title: Provider
      type: object
      required:
        - name
        - description
        - protocol_version
        - capabilities
        - endpoint
        - provider
      title: ACPAgentResponse
      description: ACP agent metadata.
    ACPDiscoveryResponse:
      properties:
        protocol:
          type: string
          title: Protocol
        version:
          type: string
          title: Version
        agents:
          items:
            $ref: "#/components/schemas/ACPAgentListEntry"
          type: array
          title: Agents
      type: object
      required:
        - protocol
        - version
        - agents
      title: ACPDiscoveryResponse
      description: Response for GET /.well-known/acp.json.
    ACPRunCreateRequest:
      properties:
        input:
          type: string
          title: Input
        agent_id:
          type: string
          title: Agent Id
          default: bernstein
        role:
          type: string
          title: Role
          default: backend
      type: object
      required:
        - input
      title: ACPRunCreateRequest
      description: Body for POST /acp/v0/runs.
    ACPRunResponse:
      properties:
        run_id:
          type: string
          title: Run Id
        bernstein_task_id:
          anyOf:
            - type: string
            - type: "null"
          title: Bernstein Task Id
        input:
          type: string
          title: Input
        role:
          type: string
          title: Role
        status:
          type: string
          title: Status
        created_at:
          type: number
          title: Created At
        updated_at:
          type: number
          title: Updated At
      type: object
      required:
        - run_id
        - input
        - role
        - status
        - created_at
        - updated_at
      title: ACPRunResponse
      description: ACP run in responses.
    AgentIdentityStatus:
      type: string
      enum:
        - active
        - suspended
        - revoked
      title: AgentIdentityStatus
      description: Lifecycle status of an agent identity.
    AgentKillResponse:
      properties:
        session_id:
          type: string
          title: Session Id
        kill_requested:
          type: boolean
          title: Kill Requested
      type: object
      required:
        - session_id
        - kill_requested
      title: AgentKillResponse
      description: Response for POST /agents/{session_id}/kill.
    AgentLogsResponse:
      properties:
        session_id:
          type: string
          title: Session Id
        content:
          type: string
          title: Content
        size:
          type: integer
          title: Size
      type: object
      required:
        - session_id
        - content
        - size
      title: AgentLogsResponse
      description: Response for GET /agents/{session_id}/logs.
    AgentMetrics:
      properties:
        adapter:
          type: string
          title: Adapter
        model:
          type: string
          title: Model
        total_tasks:
          type: integer
          title: Total Tasks
          default: 0
        succeeded:
          type: integer
          title: Succeeded
          default: 0
        failed:
          type: integer
          title: Failed
          default: 0
        avg_completion_secs:
          type: number
          title: Avg Completion Secs
          default: 0
        total_cost_usd:
          type: number
          title: Total Cost Usd
          default: 0
        quality_gate_pass_rate:
          type: number
          title: Quality Gate Pass Rate
          default: 1
        success_rate:
          type: number
          title: Success Rate
          description: Fraction of tasks that succeeded (0.0-1.0).
          readOnly: true
        cost_per_task:
          type: number
          title: Cost Per Task
          description: Average cost per task in USD.
          readOnly: true
      type: object
      required:
        - adapter
        - model
        - success_rate
        - cost_per_task
      title: AgentMetrics
      description: Aggregated performance metrics for a single (adapter, model) pair.
    ApprovalDecisionRequest:
      properties:
        reason:
          type: string
          title: Reason
          default: ""
      type: object
      title: ApprovalDecisionRequest
      description: Body for POST /approvals/{task_id}/approve or /reject.
    ArchiveRecord:
      properties:
        task_id:
          type: string
          title: Task Id
        title:
          type: string
          title: Title
        role:
          type: string
          title: Role
        tenant_id:
          type: string
          title: Tenant Id
        status:
          type: string
          title: Status
        created_at:
          type: number
          title: Created At
        completed_at:
          type: number
          title: Completed At
        duration_seconds:
          type: number
          title: Duration Seconds
        result_summary:
          anyOf:
            - type: string
            - type: "null"
          title: Result Summary
        cost_usd:
          anyOf:
            - type: number
            - type: "null"
          title: Cost Usd
        assigned_agent:
          anyOf:
            - type: string
            - type: "null"
          title: Assigned Agent
        owned_files:
          items:
            type: string
          type: array
          title: Owned Files
        claimed_by_session:
          anyOf:
            - type: string
            - type: "null"
          title: Claimed By Session
      type: object
      required:
        - task_id
        - title
        - role
        - tenant_id
        - status
        - created_at
        - completed_at
        - duration_seconds
        - result_summary
        - cost_usd
        - assigned_agent
        - owned_files
        - claimed_by_session
      title: ArchiveRecord
      description: Archive JSONL entry written when a task reaches a terminal state.
    AuthProvidersResponse:
      properties:
        oidc_enabled:
          type: boolean
          title: Oidc Enabled
          default: false
        saml_enabled:
          type: boolean
          title: Saml Enabled
          default: false
        legacy_token_enabled:
          type: boolean
          title: Legacy Token Enabled
          default: false
        device_flow_enabled:
          type: boolean
          title: Device Flow Enabled
          default: true
      type: object
      title: AuthProvidersResponse
      description: Available authentication providers.
    BatchAction:
      type: string
      enum:
        - cancel
        - retry
        - reprioritize
        - tag
      title: BatchAction
      description: Supported batch operation types.
    BatchClaimRequest:
      properties:
        task_ids:
          items:
            type: string
          type: array
          title: Task Ids
        agent_id:
          type: string
          title: Agent Id
        claimed_by_session:
          anyOf:
            - type: string
            - type: "null"
          title: Claimed By Session
      type: object
      required:
        - task_ids
        - agent_id
      title: BatchClaimRequest
      description: Body for POST /tasks/claim-batch.
    BatchClaimResponse:
      properties:
        claimed:
          items:
            type: string
          type: array
          title: Claimed
        failed:
          items:
            type: string
          type: array
          title: Failed
      type: object
      required:
        - claimed
        - failed
      title: BatchClaimResponse
      description: Response for POST /tasks/claim-batch.
    BatchCreateRequest:
      properties:
        tasks:
          items:
            $ref: "#/components/schemas/TaskCreate"
          type: array
          title: Tasks
      type: object
      required:
        - tasks
      title: BatchCreateRequest
      description: Body for POST /tasks/batch.
    BatchCreateResponse:
      properties:
        created:
          items:
            $ref: "#/components/schemas/TaskResponse"
          type: array
          title: Created
        skipped_titles:
          items:
            type: string
          type: array
          title: Skipped Titles
      type: object
      required:
        - created
        - skipped_titles
      title: BatchCreateResponse
      description: Response for POST /tasks/batch.
    BatchRequest:
      properties:
        action:
          $ref: "#/components/schemas/BatchAction"
        ids:
          items:
            type: string
          type: array
          title: Ids
        priority:
          anyOf:
            - type: integer
            - type: "null"
          title: Priority
        tags:
          anyOf:
            - items:
                type: string
              type: array
            - type: "null"
          title: Tags
      type: object
      required:
        - action
        - ids
      title: BatchRequest
      description: Request body for POST /tasks/batch-ops.
    BatchResult:
      properties:
        succeeded:
          items:
            type: string
          type: array
          title: Succeeded
        failed:
          additionalProperties:
            type: string
          type: object
          title: Failed
      type: object
      title: BatchResult
      description: Response body for POST /tasks/batch-ops.
    BroadcastRequest:
      properties:
        message:
          type: string
          title: Message
          default: ""
      type: object
      title: BroadcastRequest
      description: |-
        Body for ``POST /broadcast``.

        Typing the body with a model lets FastAPI reject non-object or
        malformed JSON with a ``422`` instead of letting ``dict.get`` (or a
        ``JSONDecodeError``) raise an unhandled exception. ``message``
        defaults to an empty string so a missing field still funnels into
        the existing ``400 message is required`` path.
    BulletinMessageResponse:
      properties:
        agent_id:
          type: string
          title: Agent Id
        type:
          type: string
          title: Type
        content:
          type: string
          title: Content
        timestamp:
          type: number
          title: Timestamp
        cell_id:
          anyOf:
            - type: string
            - type: "null"
          title: Cell Id
      type: object
      required:
        - agent_id
        - type
        - content
        - timestamp
        - cell_id
      title: BulletinMessageResponse
      description: Single bulletin message in responses.
    BulletinPostRequest:
      properties:
        agent_id:
          type: string
          title: Agent Id
        type:
          type: string
          enum:
            - alert
            - blocker
            - finding
            - status
            - dependency
          title: Type
          default: status
        content:
          type: string
          title: Content
        cell_id:
          anyOf:
            - type: string
            - type: "null"
          title: Cell Id
      type: object
      required:
        - agent_id
        - content
      title: BulletinPostRequest
      description: Body for POST /bulletin.
    ChannelQueryRequest:
      properties:
        sender_agent:
          type: string
          title: Sender Agent
        topic:
          type: string
          title: Topic
        content:
          type: string
          title: Content
        target_agent:
          anyOf:
            - type: string
            - type: "null"
          title: Target Agent
        target_role:
          anyOf:
            - type: string
            - type: "null"
          title: Target Role
        ttl_seconds:
          type: number
          title: Ttl Seconds
          default: 300
      type: object
      required:
        - sender_agent
        - topic
        - content
      title: ChannelQueryRequest
      description: Body for POST /channel/query.
    ChannelQueryResponse:
      properties:
        id:
          type: string
          title: Id
        sender_agent:
          type: string
          title: Sender Agent
        topic:
          type: string
          title: Topic
        content:
          type: string
          title: Content
        target_agent:
          anyOf:
            - type: string
            - type: "null"
          title: Target Agent
        target_role:
          anyOf:
            - type: string
            - type: "null"
          title: Target Role
        timestamp:
          type: number
          title: Timestamp
        expires_at:
          type: number
          title: Expires At
        resolved:
          type: boolean
          title: Resolved
      type: object
      required:
        - id
        - sender_agent
        - topic
        - content
        - target_agent
        - target_role
        - timestamp
        - expires_at
        - resolved
      title: ChannelQueryResponse
      description: Single channel query in API responses.
    ChannelResponseRequest:
      properties:
        responder_agent:
          type: string
          title: Responder Agent
        content:
          type: string
          title: Content
      type: object
      required:
        - responder_agent
        - content
      title: ChannelResponseRequest
      description: Body for POST /channel/{query_id}/respond.
    ChannelResponseResponse:
      properties:
        id:
          type: string
          title: Id
        query_id:
          type: string
          title: Query Id
        responder_agent:
          type: string
          title: Responder Agent
        content:
          type: string
          title: Content
        timestamp:
          type: number
          title: Timestamp
      type: object
      required:
        - id
        - query_id
        - responder_agent
        - content
        - timestamp
      title: ChannelResponseResponse
      description: Single channel response in API responses.
    ClaimGossipRequest:
      properties:
        receipts:
          items:
            additionalProperties: true
            type: object
          type: array
          title: Receipts
        head:
          anyOf:
            - type: string
            - type: "null"
          title: Head
        node_id:
          anyOf:
            - type: string
            - type: "null"
          title: Node Id
      type: object
      required:
        - receipts
      title: ClaimGossipRequest
      description: |-
        Body for POST /cluster/claims/gossip - push signed claim receipts to a peer.

        ``receipts`` are the raw :class:`ClaimReceipt` wire dicts in journal order.
        ``head`` is the sender's journal head, echoed back so the sender can tell
        convergence from divergence without a second round trip.
    ClaimGossipResponse:
      properties:
        head:
          type: string
          title: Head
        accepted:
          type: integer
          title: Accepted
        results:
          items:
            $ref: "#/components/schemas/ClaimGossipResult"
          type: array
          title: Results
        forked:
          type: boolean
          title: Forked
          default: false
      type: object
      required:
        - head
        - accepted
        - results
      title: ClaimGossipResponse
      description: |-
        Response for POST /cluster/claims/gossip.

        ``forked`` is surfaced at the top level because a fork is the one outcome
        that must not be lost in a per-receipt list an integrator might ignore.
    ClaimGossipResult:
      properties:
        entry_hash:
          type: string
          title: Entry Hash
        status:
          type: string
          title: Status
        reason:
          anyOf:
            - type: string
            - type: "null"
          title: Reason
        divergence_index:
          anyOf:
            - type: integer
            - type: "null"
          title: Divergence Index
      type: object
      required:
        - entry_hash
        - status
      title: ClaimGossipResult
      description: Per-receipt outcome of a gossip push.
    ClaimReceiptRequest:
      properties:
        claimer_id:
          type: string
          maxLength: 1000
          minLength: 1
          title: Claimer Id
        claimer_card_fingerprint:
          anyOf:
            - type: string
              maxLength: 1000
            - type: "null"
          title: Claimer Card Fingerprint
        role:
          anyOf:
            - type: string
              maxLength: 64
            - type: "null"
          title: Role
        project:
          anyOf:
            - type: string
              maxLength: 1000
            - type: "null"
          title: Project
        capability:
          anyOf:
            - type: string
              maxLength: 1000
            - type: "null"
          title: Capability
        completed_ids:
          items:
            type: string
          type: array
          title: Completed Ids
        max_attempts:
          anyOf:
            - type: integer
              minimum: 0
            - type: "null"
          title: Max Attempts
      type: object
      required:
        - claimer_id
      title: ClaimReceiptRequest
      description: |-
        Body for POST /tasks/claim-receipt (#2555).

        Drives the dependency-gated claim path over MCP and returns a signed,
        content-addressed :class:`ClaimReceipt` instead of a mutable task
        projection. The eligibility predicates mirror
        :class:`bernstein.core.tasks.claim.ClaimFilter`: a task is offered only
        when its ``depends_on`` are all present in ``completed_ids`` (the
        dependency gate), and a filter that matches no eligible row still returns
        a signed refusal receipt (never a silent skip).
    ClusterStatusResponse:
      properties:
        topology:
          type: string
          title: Topology
        total_nodes:
          type: integer
          title: Total Nodes
        online_nodes:
          type: integer
          title: Online Nodes
        offline_nodes:
          type: integer
          title: Offline Nodes
        total_capacity:
          type: integer
          title: Total Capacity
        available_slots:
          type: integer
          title: Available Slots
        active_agents:
          type: integer
          title: Active Agents
        nodes:
          items:
            $ref: "#/components/schemas/NodeResponse"
          type: array
          title: Nodes
      type: object
      required:
        - topology
        - total_nodes
        - online_nodes
        - offline_nodes
        - total_capacity
        - available_slots
        - active_agents
        - nodes
      title: ClusterStatusResponse
      description: Response for GET /cluster/status.
    CompletionSignalSchema:
      properties:
        type:
          type: string
          enum:
            - path_exists
            - glob_exists
            - test_passes
            - file_contains
            - llm_review
            - llm_judge
          title: Type
        value:
          type: string
          title: Value
      type: object
      required:
        - type
        - value
      title: CompletionSignalSchema
      description: Pydantic schema for a single completion signal in API requests.
    DependencyStatus:
      properties:
        name:
          type: string
          title: Name
        status:
          type: string
          title: Status
        latency_ms:
          type: number
          title: Latency Ms
          default: 0
        detail:
          type: string
          title: Detail
          default: ""
      type: object
      required:
        - name
        - status
      title: DependencyStatus
      description: Status of a single dependency.
    DeviceAuthorizeRequest:
      properties:
        user_code:
          type: string
          title: User Code
      type: object
      required:
        - user_code
      title: DeviceAuthorizeRequest
      description: Body for POST /auth/cli/authorize - authorize a device code.
    DeviceCodeRequest:
      properties:
        client_name:
          type: string
          title: Client Name
          default: bernstein-cli
      type: object
      title: DeviceCodeRequest
      description: Body for POST /auth/cli/device - initiate device auth flow.
    DeviceCodeResponse:
      properties:
        device_code:
          type: string
          title: Device Code
        user_code:
          type: string
          title: User Code
        verification_uri:
          type: string
          title: Verification Uri
        expires_in:
          type: integer
          title: Expires In
        interval:
          type: integer
          title: Interval
      type: object
      required:
        - device_code
        - user_code
        - verification_uri
        - expires_in
        - interval
      title: DeviceCodeResponse
      description: Response for device code request.
    DevicePollRequest:
      properties:
        device_code:
          type: string
          title: Device Code
        grant_type:
          type: string
          title: Grant Type
          default: urn:ietf:params:oauth:grant-type:device_code
      type: object
      required:
        - device_code
      title: DevicePollRequest
      description: Body for POST /auth/cli/token - poll for device authorization.
    DevicePollResponse:
      properties:
        access_token:
          type: string
          title: Access Token
          default: ""
        expires_at:
          anyOf:
            - type: number
            - type: "null"
          title: Expires At
        refresh_token:
          anyOf:
            - type: string
            - type: "null"
          title: Refresh Token
        token_type:
          type: string
          title: Token Type
          default: Bearer
        status:
          type: string
          title: Status
          default: pending
      type: object
      title: DevicePollResponse
      description: Response for device token poll.
    DiffFile:
      properties:
        path:
          type: string
          title: Path
        old_path:
          anyOf:
            - type: string
            - type: "null"
          title: Old Path
        status:
          type: string
          title: Status
          default: modified
        additions:
          type: integer
          title: Additions
          default: 0
        deletions:
          type: integer
          title: Deletions
          default: 0
        binary:
          type: boolean
          title: Binary
          default: false
        language:
          anyOf:
            - type: string
            - type: "null"
          title: Language
        hunks:
          items:
            $ref: "#/components/schemas/DiffHunk"
          type: array
          title: Hunks
      type: object
      required:
        - path
      title: DiffFile
      description: Per-file diff entry.
    DiffHunk:
      properties:
        header:
          type: string
          title: Header
        old_start:
          type: integer
          title: Old Start
        old_lines:
          type: integer
          title: Old Lines
        new_start:
          type: integer
          title: New Start
        new_lines:
          type: integer
          title: New Lines
        lines:
          items:
            type: string
          type: array
          title: Lines
      type: object
      required:
        - header
        - old_start
        - old_lines
        - new_start
        - new_lines
        - lines
      title: DiffHunk
      description: A single hunk in a file diff.
    GraphQLRequest:
      properties:
        query:
          type: string
          title: Query
        variables:
          anyOf:
            - additionalProperties: true
              type: object
            - type: "null"
          title: Variables
        operationName:
          anyOf:
            - type: string
            - type: "null"
          title: Operationname
      type: object
      required:
        - query
      title: GraphQLRequest
      description: GraphQL request body.
    GroupMappingEntry:
      properties:
        group:
          type: string
          title: Group
        role:
          type: string
          title: Role
      type: object
      required:
        - group
        - role
      title: GroupMappingEntry
      description: A single group → role mapping.
    GroupMappingsResponse:
      properties:
        mappings:
          items:
            $ref: "#/components/schemas/GroupMappingEntry"
          type: array
          title: Mappings
      type: object
      required:
        - mappings
      title: GroupMappingsResponse
      description: Response for GET /auth/group-mappings.
    GroupMappingsUpdateRequest:
      properties:
        mappings:
          items:
            $ref: "#/components/schemas/GroupMappingEntry"
          type: array
          title: Mappings
      type: object
      required:
        - mappings
      title: GroupMappingsUpdateRequest
      description: Body for PUT /auth/group-mappings.
    HTTPValidationError:
      properties:
        detail:
          items:
            $ref: "#/components/schemas/ValidationError"
          type: array
          title: Detail
      type: object
      title: HTTPValidationError
    HealthDepsResponse:
      properties:
        status:
          type: string
          title: Status
        uptime_s:
          type: number
          title: Uptime S
        timestamp:
          type: number
          title: Timestamp
        dependencies:
          items:
            $ref: "#/components/schemas/DependencyStatus"
          type: array
          title: Dependencies
      type: object
      required:
        - status
        - uptime_s
        - timestamp
      title: HealthDepsResponse
      description: Full health response with dependency checks.
    HealthResponse:
      properties:
        status:
          type: string
          title: Status
        uptime_s:
          type: number
          title: Uptime S
        task_count:
          type: integer
          title: Task Count
        agent_count:
          type: integer
          title: Agent Count
        task_queue_depth:
          type: integer
          title: Task Queue Depth
          default: 0
        memory_mb:
          type: number
          title: Memory Mb
          default: 0
        restart_count:
          type: integer
          title: Restart Count
          default: 0
        is_readonly:
          type: boolean
          title: Is Readonly
          default: false
        components:
          additionalProperties:
            additionalProperties: true
            type: object
          type: object
          title: Components
      type: object
      required:
        - status
        - uptime_s
        - task_count
        - agent_count
      title: HealthResponse
      description: Response for GET /health.
    HeartbeatRequest:
      properties:
        role:
          type: string
          title: Role
          default: ""
        status:
          type: string
          enum:
            - starting
            - working
            - idle
            - dead
          title: Status
          default: working
      type: object
      title: HeartbeatRequest
      description: Body for POST /agents/{agent_id}/heartbeat.
    HeartbeatResponse:
      properties:
        agent_id:
          type: string
          title: Agent Id
        acknowledged:
          type: boolean
          title: Acknowledged
        server_ts:
          type: number
          title: Server Ts
      type: object
      required:
        - agent_id
        - acknowledged
        - server_ts
      title: HeartbeatResponse
      description: Response for heartbeat.
    HoldCreateRequest:
      properties:
        reason:
          type: string
          title: Reason
          description: Why the caller wants the orchestrator to stay up
        ttl_seconds:
          anyOf:
            - type: number
              exclusiveMinimum: 0
            - type: "null"
          title: Ttl Seconds
          description: Grace-window auto-expiry; server default if omitted
      additionalProperties: false
      type: object
      required:
        - reason
      title: HoldCreateRequest
      description: Body for POST /orchestrator/holds.
    HoldListResponse:
      properties:
        holds:
          items:
            $ref: "#/components/schemas/HoldResponse"
          type: array
          title: Holds
        count:
          type: integer
          title: Count
      type: object
      required:
        - holds
        - count
      title: HoldListResponse
      description: Response for GET /orchestrator/holds.
    HoldResponse:
      properties:
        id:
          type: string
          title: Id
        reason:
          type: string
          title: Reason
        created_at:
          type: number
          title: Created At
        ttl_seconds:
          type: number
          title: Ttl Seconds
        expires_at:
          type: number
          title: Expires At
        last_renewed_at:
          anyOf:
            - type: number
            - type: "null"
          title: Last Renewed At
      type: object
      required:
        - id
        - reason
        - created_at
        - ttl_seconds
        - expires_at
      title: HoldResponse
      description: Serialised hold in API responses.
    ImpactResponse:
      properties:
        file_query:
          type: string
          title: File Query
        matched_files:
          items:
            type: string
          type: array
          title: Matched Files
        impacted_files:
          items:
            type: string
          type: array
          title: Impacted Files
        built_at:
          type: string
          title: Built At
      type: object
      required:
        - file_query
        - matched_files
        - impacted_files
        - built_at
      title: ImpactResponse
      description: Response body for ``GET /graph/impact``.
    ListApprovalsResponse:
      properties:
        pending:
          items:
            $ref: "#/components/schemas/PendingApproval"
          type: array
          title: Pending
      type: object
      required:
        - pending
      title: ListApprovalsResponse
      description: Response for GET /approvals.
    LoginProvider:
      type: string
      enum:
        - oidc
        - saml
      title: LoginProvider
      description: |-
        SSO providers accepted by ``GET /auth/login``.

        Typing the ``provider`` query param with this enum lets FastAPI
        reject unknown values with a ``422`` at the validation layer instead
        of the handler falling through to a generic error for an input it
        was never going to support.
    MergeOrderResponse:
      properties:
        repos:
          items:
            type: string
          type: array
          title: Repos
      type: object
      required:
        - repos
      title: MergeOrderResponse
      description: Topological repository merge order.
    NodeCapacitySchema:
      properties:
        max_agents:
          type: integer
          title: Max Agents
          default: 6
        available_slots:
          type: integer
          title: Available Slots
          default: 6
        active_agents:
          type: integer
          title: Active Agents
          default: 0
        gpu_available:
          type: boolean
          title: Gpu Available
          default: false
        supported_models:
          items:
            type: string
          type: array
          title: Supported Models
      type: object
      title: NodeCapacitySchema
      description: Advertised capacity of a cluster node.
    NodeHeartbeatRequest:
      properties:
        capacity:
          anyOf:
            - $ref: "#/components/schemas/NodeCapacitySchema"
            - type: "null"
      type: object
      title: NodeHeartbeatRequest
      description: Body for POST /cluster/nodes/{node_id}/heartbeat.
    NodeRegisterRequest:
      properties:
        name:
          type: string
          title: Name
          default: ""
        url:
          type: string
          title: Url
          default: ""
        capacity:
          $ref: "#/components/schemas/NodeCapacitySchema"
        labels:
          additionalProperties:
            type: string
          type: object
          title: Labels
        cell_ids:
          items:
            type: string
          type: array
          title: Cell Ids
      type: object
      title: NodeRegisterRequest
      description: Body for POST /cluster/nodes.
    NodeResponse:
      properties:
        id:
          type: string
          title: Id
        name:
          type: string
          title: Name
        url:
          type: string
          title: Url
        status:
          type: string
          title: Status
        capacity:
          $ref: "#/components/schemas/NodeCapacitySchema"
        last_heartbeat:
          type: number
          title: Last Heartbeat
        registered_at:
          type: number
          title: Registered At
        labels:
          additionalProperties:
            type: string
          type: object
          title: Labels
        cell_ids:
          items:
            type: string
          type: array
          title: Cell Ids
      type: object
      required:
        - id
        - name
        - url
        - status
        - capacity
        - last_heartbeat
        - registered_at
        - labels
        - cell_ids
      title: NodeResponse
      description: Serialised node in API responses.
    PaginatedSearchResponse:
      properties:
        tasks:
          items:
            $ref: "#/components/schemas/TaskResponse"
          type: array
          title: Tasks
        total:
          type: integer
          title: Total
        page:
          type: integer
          title: Page
        per_page:
          type: integer
          title: Per Page
        total_pages:
          type: integer
          title: Total Pages
        sort:
          type: string
          title: Sort
        order:
          type: string
          title: Order
        filters:
          additionalProperties:
            type: string
          type: object
          title: Filters
      type: object
      required:
        - tasks
        - total
        - page
        - per_page
        - total_pages
        - sort
        - order
      title: PaginatedSearchResponse
      description: Paginated task search response with metadata.
    PartialMergeRequest:
      properties:
        files:
          items:
            type: string
          type: array
          title: Files
        message:
          type: string
          title: Message
          default: ""
      type: object
      required:
        - files
      title: PartialMergeRequest
      description: |-
        Body for POST /tasks/{task_id}/partial-merge.

        Requests an incremental merge of specific files from the agent's branch
        into the main branch before the task finishes.  Only files already
        committed in the agent's worktree branch are processed.
    PartialMergeResponse:
      properties:
        success:
          type: boolean
          title: Success
        merged_files:
          items:
            type: string
          type: array
          title: Merged Files
        skipped_already_merged:
          items:
            type: string
          type: array
          title: Skipped Already Merged
        uncommitted_files:
          items:
            type: string
          type: array
          title: Uncommitted Files
        conflicting_files:
          items:
            type: string
          type: array
          title: Conflicting Files
        commit_sha:
          type: string
          title: Commit Sha
        error:
          type: string
          title: Error
      type: object
      required:
        - success
        - merged_files
        - skipped_already_merged
        - uncommitted_files
        - conflicting_files
        - commit_sha
        - error
      title: PartialMergeResponse
      description: Response for POST /tasks/{task_id}/partial-merge.
    PendingApproval:
      properties:
        task_id:
          type: string
          title: Task Id
        task_title:
          type: string
          title: Task Title
        session_id:
          type: string
          title: Session Id
        diff:
          type: string
          title: Diff
          default: ""
        test_summary:
          type: string
          title: Test Summary
          default: ""
        mechanism:
          type: string
          title: Mechanism
          default: task_review
        prompt:
          type: string
          title: Prompt
          default: ""
        default_action:
          type: string
          title: Default Action
          default: ""
        timeout_at_iso:
          type: string
          title: Timeout At Iso
          default: ""
        created_iso:
          type: string
          title: Created Iso
          default: ""
        timeout_seconds:
          anyOf:
            - type: integer
            - type: "null"
          title: Timeout Seconds
        unblocks:
          type: string
          title: Unblocks
          default: task completion and merge
        resolution_endpoint:
          type: string
          title: Resolution Endpoint
          default: ""
      type: object
      required:
        - task_id
        - task_title
        - session_id
      title: PendingApproval
      description: A single pending approval request (task-level or pre-spawn).
    PlanDecisionRequest:
      properties:
        reason:
          type: string
          title: Reason
          default: ""
      type: object
      title: PlanDecisionRequest
      description: Body for POST /plans/{plan_id}/approve or /reject.
    ProgressEntry:
      properties:
        timestamp:
          type: number
          title: Timestamp
        message:
          type: string
          title: Message
        percent:
          type: integer
          title: Percent
      type: object
      required:
        - timestamp
        - message
        - percent
      title: ProgressEntry
      description: Single entry in a task's progress_log.
    PromoteRequest:
      properties:
        reason:
          type: string
          title: Reason
          default: manual
        promoted_by:
          type: string
          title: Promoted By
          default: operator
      type: object
      title: PromoteRequest
      description: |-
        Request body for a manual promotion to the next stage.

        Attributes:
            reason: Human-readable reason for the promotion.
            promoted_by: Who triggered the promotion (operator name or ID).
    QueuedApprovalResponse:
      properties:
        id:
          type: string
          title: Id
        session_id:
          type: string
          title: Session Id
        agent_role:
          type: string
          title: Agent Role
        tool_name:
          type: string
          title: Tool Name
        tool_args:
          additionalProperties: true
          type: object
          title: Tool Args
        created_at:
          type: number
          title: Created At
        ttl_seconds:
          type: integer
          title: Ttl Seconds
        nonce:
          type: string
          title: Nonce
      type: object
      required:
        - id
        - session_id
        - agent_role
        - tool_name
        - tool_args
        - created_at
        - ttl_seconds
        - nonce
      title: QueuedApprovalResponse
      description: |-
        One queued tool-call approval from the op-002 approval queue.

        The ``nonce`` field is the hex-encoded single-use token the reply
        must echo. It travels only over the human-channel surface (TUI,
        dashboard, chat bridge) and never reaches agent stdin or any
        rendered prompt template.
    QueuedApprovalsResponse:
      properties:
        pending:
          items:
            $ref: "#/components/schemas/QueuedApprovalResponse"
          type: array
          title: Pending
      type: object
      required:
        - pending
      title: QueuedApprovalsResponse
      description: Response envelope for ``GET /approvals/queue``.
    RecordEventRequest:
      properties:
        task_id:
          type: string
          title: Task Id
        success:
          type: boolean
          title: Success
        duration_s:
          type: number
          title: Duration S
          default: 0
        cost_usd:
          type: number
          title: Cost Usd
          default: 0
        initial_stage:
          type: string
          title: Initial Stage
          default: sandbox
      type: object
      required:
        - task_id
        - success
      title: RecordEventRequest
      description: |-
        Body for recording a task completion/failure event.

        Attributes:
            task_id: Task identifier.
            success: Whether the task succeeded.
            duration_s: Task wall-clock duration in seconds.
            cost_usd: Estimated cost of the task in USD.
            initial_stage: Stage to initialise the record at when no record exists yet.
    ResolveRequest:
      properties:
        decision:
          type: string
          enum:
            - allow
            - reject
            - always
          title: Decision
        nonce:
          type: string
          title: Nonce
          default: ""
        reason:
          type: string
          title: Reason
          default: ""
      type: object
      required:
        - decision
      title: ResolveRequest
      description: |-
        Body for ``POST /approvals/{id}/resolve``.

        The reply must echo the exact ``nonce`` hex string issued when the
        approval was queued. ``nonce`` defaults to an empty string at the
        schema layer so a missing field flows through the handler as a
        nonce mismatch (``409 NONCE_MISMATCH``) rather than a Pydantic 422
        validation error, matching the documented contract.
    ReviewActionRequest:
      properties:
        decision:
          type: string
          title: Decision
        note:
          type: string
          title: Note
          default: ""
      type: object
      required:
        - decision
      title: ReviewActionRequest
      description: Body of a board review action (approve / request-changes / merge).
    SBOMArtifactEntry:
      properties:
        filename:
          type: string
          title: Filename
        path:
          type: string
          title: Path
        size_bytes:
          type: integer
          title: Size Bytes
      type: object
      required:
        - filename
        - path
        - size_bytes
      title: SBOMArtifactEntry
      description: A single SBOM artifact file entry.
    SBOMGenerateRequest:
      properties:
        sbom_format:
          type: string
          title: Sbom Format
          description: "Output format: 'cyclonedx-json' or 'spdx-json'."
          default: cyclonedx-json
        source:
          type: string
          title: Source
          description: Package source label (pip, npm, requirements.txt, etc.).
          default: pip
        run_scan:
          type: boolean
          title: Run Scan
          description: Run vulnerability scanning (osv-scanner or grype) after generation.
          default: true
        block_on_critical:
          type: boolean
          title: Block On Critical
          description: Raise 422 when critical vulnerabilities are found.
          default: true
      type: object
      title: SBOMGenerateRequest
      description: Body for POST /sbom/generate.
    SBOMGenerateResponse:
      properties:
        serial_number:
          type: string
          title: Serial Number
        sbom_format:
          type: string
          title: Sbom Format
        component_count:
          type: integer
          title: Component Count
        artifact_path:
          type: string
          title: Artifact Path
        scan_result:
          anyOf:
            - $ref: "#/components/schemas/SBOMScanResultResponse"
            - type: "null"
      type: object
      required:
        - serial_number
        - sbom_format
        - component_count
        - artifact_path
      title: SBOMGenerateResponse
      description: Response from POST /sbom/generate.
    SBOMListResponse:
      properties:
        artifacts:
          items:
            $ref: "#/components/schemas/SBOMArtifactEntry"
          type: array
          title: Artifacts
        artifact_dir:
          type: string
          title: Artifact Dir
      type: object
      required:
        - artifacts
        - artifact_dir
      title: SBOMListResponse
      description: Response from GET /sbom/artifacts.
    SBOMScanResultResponse:
      properties:
        scanner:
          type: string
          title: Scanner
        finding_count:
          type: integer
          title: Finding Count
        highest_severity:
          type: string
          title: Highest Severity
        findings:
          items:
            $ref: "#/components/schemas/SBOMVulnFindingResponse"
          type: array
          title: Findings
        errors:
          items:
            type: string
          type: array
          title: Errors
        passed_gate:
          type: boolean
          title: Passed Gate
      type: object
      required:
        - scanner
        - finding_count
        - highest_severity
        - findings
        - errors
        - passed_gate
      title: SBOMScanResultResponse
      description: Serialised scan result.
    SBOMVulnFindingResponse:
      properties:
        component_name:
          type: string
          title: Component Name
        component_version:
          type: string
          title: Component Version
        vuln_id:
          type: string
          title: Vuln Id
        severity:
          type: string
          title: Severity
        summary:
          type: string
          title: Summary
        fix_version:
          type: string
          title: Fix Version
        scanner:
          type: string
          title: Scanner
      type: object
      required:
        - component_name
        - component_version
        - vuln_id
        - severity
        - summary
        - fix_version
        - scanner
      title: SBOMVulnFindingResponse
      description: Serialised vulnerability finding.
    SnapshotEntry:
      properties:
        timestamp:
          type: number
          title: Timestamp
        files_changed:
          type: integer
          title: Files Changed
        tests_passing:
          type: integer
          title: Tests Passing
        errors:
          type: integer
          title: Errors
        last_file:
          type: string
          title: Last File
      type: object
      required:
        - timestamp
        - files_changed
        - tests_passing
        - errors
        - last_file
      title: SnapshotEntry
      description: A single machine-readable progress snapshot for stall detection.
    TaskArtifactContentResponse:
      properties:
        task_id:
          type: string
          title: Task Id
        key:
          type: string
          title: Key
        artifact_type:
          type: string
          title: Artifact Type
        content_hash:
          type: string
          title: Content Hash
        version:
          type: integer
          title: Version
        prev_version_hash:
          type: string
          title: Prev Version Hash
        spine_entry_hash:
          type: string
          title: Spine Entry Hash
        journal_index:
          type: integer
          title: Journal Index
        journal_event_hash:
          type: string
          title: Journal Event Hash
        link_kind:
          type: string
          title: Link Kind
          default: ""
        size:
          type: integer
          title: Size
          default: 0
        verified:
          type: boolean
          title: Verified
          default: true
        verify_reason:
          type: string
          title: Verify Reason
          default: ""
        content:
          anyOf:
            - additionalProperties: true
              type: object
            - type: "null"
          title: Content
      type: object
      required:
        - task_id
        - key
        - artifact_type
        - content_hash
        - version
        - prev_version_hash
        - spine_entry_hash
        - journal_index
        - journal_event_hash
      title: TaskArtifactContentResponse
      description: |-
        A posted artifact version plus its decoded content (for rendering).

        ``content`` carries the type-specific fields (``body`` for a report,
        ``columns``/``rows`` for a table, ``url``/``kind`` for a link). When the
        stored blob fails its hash check ``verified`` is False and ``content`` is
        omitted -- the surface must render *tampered*, never the bytes.
    TaskArtifactPost:
      properties:
        key:
          type: string
          maxLength: 128
          minLength: 1
          pattern: ^[A-Za-z0-9][A-Za-z0-9_.\-]{0,127}$
          title: Key
        artifact_type:
          type: string
          pattern: ^(report|table|link|finding)$
          title: Artifact Type
        poster:
          type: string
          maxLength: 1000
          minLength: 1
          title: Poster
        body:
          type: string
          maxLength: 1048576
          title: Body
          default: ""
        columns:
          items:
            type: string
          type: array
          title: Columns
        rows:
          items:
            items:
              type: string
            type: array
          type: array
          title: Rows
        url:
          type: string
          maxLength: 4096
          title: Url
          default: ""
        link_kind:
          type: string
          maxLength: 64
          title: Link Kind
          default: ""
        sarif_result:
          additionalProperties: true
          type: object
          title: Sarif Result
        tool:
          type: string
          maxLength: 1000
          title: Tool
          default: ""
        tool_version:
          type: string
          maxLength: 128
          title: Tool Version
          default: ""
        pinned_ruleset_or_feed_digest:
          type: string
          maxLength: 256
          title: Pinned Ruleset Or Feed Digest
          default: ""
        invocation_argv_hash:
          type: string
          maxLength: 256
          title: Invocation Argv Hash
          default: ""
        target:
          type: string
          maxLength: 4096
          title: Target
          default: ""
      type: object
      required:
        - key
        - artifact_type
        - poster
      title: TaskArtifactPost
      description: |-
        Body for POST /tasks/{task_id}/artifacts (#2553).

        An agent-posted, journal-anchored artifact. ``artifact_type`` selects the
        payload shape: ``report`` uses ``body`` (markdown); ``table`` uses
        ``columns`` and ``rows``; ``link`` uses ``url`` and ``link_kind``
        (``preview`` / ``dashboard`` / ``document``). ``poster`` is the claim
        identity: a caller may only post against a task whose claim it holds.

        There is deliberately no progress field. Progress is a chain-computed
        projection of journaled work, never postable.
    TaskBlockRequest:
      properties:
        reason:
          type: string
          title: Reason
          default: ""
      type: object
      title: TaskBlockRequest
      description: Body for POST /tasks/{task_id}/block.
    TaskCancelRequest:
      properties:
        reason:
          type: string
          title: Reason
          default: ""
      type: object
      title: TaskCancelRequest
      description: Body for POST /tasks/{task_id}/cancel.
    TaskCompleteRequest:
      properties:
        result_summary:
          type: string
          title: Result Summary
          default: ""
        payload:
          anyOf:
            - additionalProperties: true
              type: object
            - type: "null"
          title: Payload
      type: object
      title: TaskCompleteRequest
      description: |-
        Body for POST /tasks/{task_id}/complete.

        ``result_summary`` is the legacy free-form summary and stays accepted
        unchanged. ``payload`` carries a structured terminal payload under the
        worker completion contract (#2244) - either a completion or a typed
        refusal - and is schema-validated at the API boundary; an invalid
        payload is a typed ``contract_violation`` failure, never a silent
        accept. When ``payload`` is provided, ``result_summary`` is ignored.
    TaskCountsResponse:
      properties:
        open:
          type: integer
          title: Open
          default: 0
        claimed:
          type: integer
          title: Claimed
          default: 0
        in_progress:
          type: integer
          title: In Progress
          default: 0
        done:
          type: integer
          title: Done
          default: 0
        closed:
          type: integer
          title: Closed
          default: 0
        failed:
          type: integer
          title: Failed
          default: 0
        blocked:
          type: integer
          title: Blocked
          default: 0
        cancelled:
          type: integer
          title: Cancelled
          default: 0
        planned:
          type: integer
          title: Planned
          default: 0
        pending_approval:
          type: integer
          title: Pending Approval
          default: 0
        waiting_for_subtasks:
          type: integer
          title: Waiting For Subtasks
          default: 0
        orphaned:
          type: integer
          title: Orphaned
          default: 0
        abandoned:
          type: integer
          title: Abandoned
          default: 0
        blocked_by_abandon:
          type: integer
          title: Blocked By Abandon
          default: 0
        blocked_by_failed_dep:
          type: integer
          title: Blocked By Failed Dep
          default: 0
        refused:
          type: integer
          title: Refused
          default: 0
        suspended:
          type: integer
          title: Suspended
          default: 0
        total:
          type: integer
          title: Total
          default: 0
      type: object
      title: TaskCountsResponse
      description: |-
        Lightweight status counts - no task bodies.

        Every value in :class:`bernstein.core.tasks.models.TaskStatus` is exposed
        as a field so the GUI's status-chip badges can render real numbers
        instead of ``-``.  Adding fields here is non-breaking - existing clients
        that consume only ``open``/``claimed``/``done`` continue to work and the
        new fields default to ``0``.
    TaskCreate:
      properties:
        id:
          anyOf:
            - type: string
              maxLength: 1000
            - type: "null"
          title: Id
        title:
          type: string
          maxLength: 500
          title: Title
        description:
          type: string
          maxLength: 100000
          title: Description
        role:
          type: string
          maxLength: 1000
          title: Role
          default: auto
        tenant_id:
          type: string
          maxLength: 1000
          title: Tenant Id
          default: default
        priority:
          type: integer
          title: Priority
          default: 2
        scope:
          type: string
          maxLength: 1000
          title: Scope
          default: medium
        complexity:
          type: string
          maxLength: 1000
          title: Complexity
          default: medium
        eu_ai_act_risk:
          type: string
          maxLength: 1000
          title: Eu Ai Act Risk
          default: minimal
        approval_required:
          type: boolean
          title: Approval Required
          default: false
        risk_level:
          type: string
          maxLength: 1000
          title: Risk Level
          default: low
        estimated_minutes:
          anyOf:
            - type: integer
            - type: "null"
          title: Estimated Minutes
        depends_on:
          items:
            type: string
          type: array
          maxItems: 100
          title: Depends On
        parent_task_id:
          anyOf:
            - type: string
              maxLength: 1000
            - type: "null"
          title: Parent Task Id
        depends_on_repo:
          anyOf:
            - type: string
              maxLength: 4096
            - type: "null"
          title: Depends On Repo
        owned_files:
          items:
            type: string
          type: array
          maxItems: 100
          title: Owned Files
        cell_id:
          anyOf:
            - type: string
              maxLength: 1000
            - type: "null"
          title: Cell Id
        repo:
          anyOf:
            - type: string
              maxLength: 4096
            - type: "null"
          title: Repo
        task_type:
          type: string
          maxLength: 1000
          title: Task Type
          default: standard
        upgrade_details:
          anyOf:
            - additionalProperties: true
              type: object
            - type: "null"
          title: Upgrade Details
        model:
          anyOf:
            - type: string
              maxLength: 1000
            - type: "null"
          title: Model
        effort:
          anyOf:
            - type: string
              maxLength: 1000
            - type: "null"
          title: Effort
        cli:
          anyOf:
            - type: string
              maxLength: 1000
            - type: "null"
          title: Cli
        batch_eligible:
          type: boolean
          title: Batch Eligible
          default: false
        completion_signals:
          items:
            $ref: "#/components/schemas/CompletionSignalSchema"
          type: array
          maxItems: 100
          title: Completion Signals
        slack_context:
          anyOf:
            - additionalProperties: true
              type: object
            - type: "null"
          title: Slack Context
        metadata:
          additionalProperties: true
          type: object
          title: Metadata
        deadline:
          anyOf:
            - type: number
            - type: "null"
          title: Deadline
        parent_session_id:
          anyOf:
            - type: string
              maxLength: 1000
            - type: "null"
          title: Parent Session Id
        parent_context:
          anyOf:
            - type: string
              maxLength: 100000
            - type: "null"
          title: Parent Context
        retry_count:
          anyOf:
            - type: integer
            - type: "null"
          title: Retry Count
        max_retries:
          anyOf:
            - type: integer
            - type: "null"
          title: Max Retries
        retry_delay_s:
          anyOf:
            - type: number
            - type: "null"
          title: Retry Delay S
        terminal_reason:
          anyOf:
            - type: string
              maxLength: 100000
            - type: "null"
          title: Terminal Reason
        max_output_tokens:
          anyOf:
            - type: integer
            - type: "null"
          title: Max Output Tokens
        meta_messages:
          anyOf:
            - items:
                type: string
              type: array
              maxItems: 100
            - type: "null"
          title: Meta Messages
        max_turns:
          anyOf:
            - type: integer
              maximum: 10000
              minimum: 1
            - type: "null"
          title: Max Turns
        artifact_spec:
          anyOf:
            - additionalProperties: true
              type: object
            - type: "null"
          title: Artifact Spec
      type: object
      required:
        - title
        - description
      title: TaskCreate
      description: Body for POST /tasks.
    TaskDetailResponse:
      properties:
        task:
          $ref: "#/components/schemas/TaskResponse"
        log_tail:
          type: string
          title: Log Tail
        log_size:
          type: integer
          title: Log Size
        progress_entries:
          items:
            additionalProperties: true
            type: object
          type: array
          title: Progress Entries
        agent_status:
          type: string
          title: Agent Status
          default: ""
        artifacts:
          items:
            additionalProperties: true
            type: object
          type: array
          title: Artifacts
        progress:
          anyOf:
            - additionalProperties: true
              type: object
            - type: "null"
          title: Progress
      type: object
      required:
        - task
        - log_tail
        - log_size
      title: TaskDetailResponse
      description: Detailed task view including log tail and progress.
    TaskDiffResponse:
      properties:
        task_id:
          type: string
          title: Task Id
        branch:
          anyOf:
            - type: string
            - type: "null"
          title: Branch
        base_ref:
          type: string
          title: Base Ref
        head_ref:
          anyOf:
            - type: string
            - type: "null"
          title: Head Ref
        additions:
          type: integer
          title: Additions
        deletions:
          type: integer
          title: Deletions
        files:
          items:
            $ref: "#/components/schemas/DiffFile"
          type: array
          title: Files
        unified:
          type: string
          title: Unified
        truncated:
          type: boolean
          title: Truncated
          default: false
        generated_at:
          type: number
          title: Generated At
        note:
          anyOf:
            - type: string
            - type: "null"
          title: Note
      type: object
      required:
        - task_id
        - branch
        - base_ref
        - head_ref
        - additions
        - deletions
        - files
        - unified
        - generated_at
      title: TaskDiffResponse
      description: Diff payload for a task's working branch vs the base ref.
    TaskFailRequest:
      properties:
        reason:
          type: string
          title: Reason
          default: ""
      type: object
      title: TaskFailRequest
      description: Body for POST /tasks/{task_id}/fail.
    TaskMessagePost:
      properties:
        sender:
          type: string
          maxLength: 1000
          minLength: 1
          title: Sender
        kind:
          type: string
          maxLength: 64
          minLength: 1
          title: Kind
        body:
          type: string
          maxLength: 4096
          minLength: 1
          title: Body
        sender_card_fingerprint:
          anyOf:
            - type: string
              maxLength: 1000
            - type: "null"
          title: Sender Card Fingerprint
      type: object
      required:
        - sender
        - kind
        - body
      title: TaskMessagePost
      description: |-
        Body for POST /tasks/{task_id}/messages (#2357).

        Typed, size-capped worker mailbox payload. ``kind`` must be one of the
        closed vocabulary (``finding`` / ``artefact_ref`` / ``question``).
        The message body is capped by the mailbox chain to 4096 UTF-8 bytes
        (see ``task_mailbox.MAX_MESSAGE_BODY_BYTES``); the API model mirrors
        this limit so oversized bodies fail validation up front instead of
        being rejected downstream. The byte-strict cap remains authoritative
        in the mailbox for multibyte payloads.
    TaskMessageResponse:
      properties:
        seq:
          type: integer
          title: Seq
        task_id:
          type: string
          title: Task Id
        sender:
          type: string
          title: Sender
        sender_card_fingerprint:
          type: string
          title: Sender Card Fingerprint
        kind:
          type: string
          title: Kind
        body:
          type: string
          title: Body
        body_hash:
          type: string
          title: Body Hash
        redaction_count:
          type: integer
          title: Redaction Count
        timestamp:
          type: number
          title: Timestamp
        prev_entry_hash:
          type: string
          title: Prev Entry Hash
        entry_hash:
          type: string
          title: Entry Hash
        signature:
          type: string
          title: Signature
        signer_public_key_pem:
          type: string
          title: Signer Public Key Pem
      type: object
      required:
        - seq
        - task_id
        - sender
        - sender_card_fingerprint
        - kind
        - body
        - body_hash
        - redaction_count
        - timestamp
        - prev_entry_hash
        - entry_hash
        - signature
        - signer_public_key_pem
      title: TaskMessageResponse
      description: One delivered mailbox message (chain order = delivery order).
    TaskPatchRequest:
      properties:
        role:
          anyOf:
            - type: string
            - type: "null"
          title: Role
        priority:
          anyOf:
            - type: integer
            - type: "null"
          title: Priority
        model:
          anyOf:
            - type: string
            - type: "null"
          title: Model
      type: object
      title: TaskPatchRequest
      description: Body for PATCH /tasks/{task_id} - manager corrections.
    TaskProgressRequest:
      properties:
        message:
          type: string
          title: Message
          default: ""
        percent:
          type: integer
          title: Percent
          default: 0
        files_changed:
          anyOf:
            - type: integer
            - type: "null"
          title: Files Changed
        lines_changed:
          anyOf:
            - type: integer
            - type: "null"
          title: Lines Changed
        tests_passing:
          anyOf:
            - type: integer
            - type: "null"
          title: Tests Passing
        errors:
          anyOf:
            - type: integer
            - type: "null"
          title: Errors
        last_file:
          type: string
          title: Last File
          default: ""
        last_command:
          type: string
          title: Last Command
          default: ""
      type: object
      title: TaskProgressRequest
      description: Body for POST /tasks/{task_id}/progress.
    TaskProgressResponse:
      properties:
        task_id:
          type: string
          title: Task Id
        schema_version:
          type: integer
          title: Schema Version
        checkpoints:
          type: integer
          title: Checkpoints
        diffs_captured:
          type: integer
          title: Diffs Captured
        gate_attempts:
          type: integer
          title: Gate Attempts
        evidence_declared:
          type: integer
          title: Evidence Declared
        evidence_passed:
          type: integer
          title: Evidence Passed
        ledger_phase:
          type: string
          title: Ledger Phase
        ledger_attempts:
          type: integer
          title: Ledger Attempts
        terminal:
          type: boolean
          title: Terminal
        earned_steps:
          type: integer
          title: Earned Steps
        phase_ordinal:
          type: integer
          title: Phase Ordinal
        vector_hash:
          type: string
          title: Vector Hash
      type: object
      required:
        - task_id
        - schema_version
        - checkpoints
        - diffs_captured
        - gate_attempts
        - evidence_declared
        - evidence_passed
        - ledger_phase
        - ledger_attempts
        - terminal
        - earned_steps
        - phase_ordinal
        - vector_hash
      title: TaskProgressResponse
      description: |-
        The chain-computed progress vector for a task (#2553).

        A pure projection of journaled work: checkpoints, diffs, gates, evidence
        producers, and ledger transitions. ``vector_hash`` is the stable hash of the
        canonical vector; two projections of the same run agree byte-for-byte.
    TaskReleaseRequest:
      properties:
        reason:
          type: string
          title: Reason
          default: ""
      type: object
      title: TaskReleaseRequest
      description: Body for POST /tasks/{task_id}/release.
    TaskReopenRequest:
      properties:
        reason:
          type: string
          title: Reason
          default: ""
      type: object
      title: TaskReopenRequest
      description: Body for POST /tasks/{task_id}/reopen.
    TaskResponse:
      properties:
        id:
          type: string
          title: Id
        title:
          type: string
          title: Title
        description:
          type: string
          title: Description
        role:
          type: string
          title: Role
        tenant_id:
          type: string
          title: Tenant Id
        priority:
          type: integer
          title: Priority
        scope:
          type: string
          title: Scope
        complexity:
          type: string
          title: Complexity
        eu_ai_act_risk:
          type: string
          title: Eu Ai Act Risk
        approval_required:
          type: boolean
          title: Approval Required
        risk_level:
          type: string
          title: Risk Level
        estimated_minutes:
          anyOf:
            - type: integer
            - type: "null"
          title: Estimated Minutes
        status:
          type: string
          title: Status
        depends_on:
          items:
            type: string
          type: array
          title: Depends On
        parent_task_id:
          anyOf:
            - type: string
            - type: "null"
          title: Parent Task Id
        depends_on_repo:
          anyOf:
            - type: string
            - type: "null"
          title: Depends On Repo
        owned_files:
          items:
            type: string
          type: array
          title: Owned Files
        assigned_agent:
          anyOf:
            - type: string
            - type: "null"
          title: Assigned Agent
        result_summary:
          anyOf:
            - type: string
            - type: "null"
          title: Result Summary
        cell_id:
          anyOf:
            - type: string
            - type: "null"
          title: Cell Id
        repo:
          anyOf:
            - type: string
            - type: "null"
          title: Repo
        task_type:
          type: string
          title: Task Type
        upgrade_details:
          anyOf:
            - additionalProperties: true
              type: object
            - type: "null"
          title: Upgrade Details
        model:
          anyOf:
            - type: string
            - type: "null"
          title: Model
        effort:
          anyOf:
            - type: string
            - type: "null"
          title: Effort
        cli:
          anyOf:
            - type: string
            - type: "null"
          title: Cli
        batch_eligible:
          type: boolean
          title: Batch Eligible
          default: false
        completion_signals:
          items:
            additionalProperties:
              type: string
            type: object
          type: array
          title: Completion Signals
        slack_context:
          anyOf:
            - additionalProperties: true
              type: object
            - type: "null"
          title: Slack Context
        metadata:
          additionalProperties: true
          type: object
          title: Metadata
        created_at:
          type: number
          title: Created At
        claimed_at:
          anyOf:
            - type: number
            - type: "null"
          title: Claimed At
        completed_at:
          anyOf:
            - type: number
            - type: "null"
          title: Completed At
        closed_at:
          anyOf:
            - type: number
            - type: "null"
          title: Closed At
        deadline:
          anyOf:
            - type: number
            - type: "null"
          title: Deadline
        progress_log:
          items:
            $ref: "#/components/schemas/ProgressEntry"
          type: array
          title: Progress Log
        version:
          type: integer
          title: Version
          default: 1
        parent_session_id:
          anyOf:
            - type: string
            - type: "null"
          title: Parent Session Id
        retry_count:
          type: integer
          title: Retry Count
          default: 0
        max_retries:
          type: integer
          title: Max Retries
          default: 3
        retry_delay_s:
          type: number
          title: Retry Delay S
          default: 0
        terminal_reason:
          anyOf:
            - type: string
            - type: "null"
          title: Terminal Reason
        max_output_tokens:
          anyOf:
            - type: integer
            - type: "null"
          title: Max Output Tokens
        meta_messages:
          items:
            type: string
          type: array
          title: Meta Messages
        max_turns:
          anyOf:
            - type: integer
            - type: "null"
          title: Max Turns
        artifact_spec:
          anyOf:
            - additionalProperties: true
              type: object
            - type: "null"
          title: Artifact Spec
      type: object
      required:
        - id
        - title
        - description
        - role
        - tenant_id
        - priority
        - scope
        - complexity
        - eu_ai_act_risk
        - approval_required
        - risk_level
        - estimated_minutes
        - status
        - depends_on
        - parent_task_id
        - depends_on_repo
        - owned_files
        - assigned_agent
        - result_summary
        - cell_id
        - repo
        - task_type
        - upgrade_details
        - model
        - effort
        - created_at
      title: TaskResponse
      description: Serialised task returned by every task endpoint.
    TaskSelfCreate:
      properties:
        parent_task_id:
          type: string
          title: Parent Task Id
        title:
          type: string
          title: Title
        description:
          type: string
          title: Description
        role:
          type: string
          title: Role
          default: auto
        priority:
          type: integer
          title: Priority
          default: 2
        scope:
          type: string
          title: Scope
          default: medium
        complexity:
          type: string
          title: Complexity
          default: medium
        estimated_minutes:
          anyOf:
            - type: integer
            - type: "null"
          title: Estimated Minutes
        depends_on:
          items:
            type: string
          type: array
          title: Depends On
        owned_files:
          items:
            type: string
          type: array
          title: Owned Files
      type: object
      required:
        - parent_task_id
        - title
        - description
      title: TaskSelfCreate
      description: |-
        Body for POST /tasks/self-create - agent-initiated subtask creation.

        Agents use this to decompose work into subtasks during execution.
        The parent_task_id is required and links the new subtask to the calling
        agent's current task.
    TaskStealAction:
      properties:
        donor_node_id:
          type: string
          title: Donor Node Id
        receiver_node_id:
          type: string
          title: Receiver Node Id
        task_ids:
          items:
            type: string
          type: array
          title: Task Ids
      type: object
      required:
        - donor_node_id
        - receiver_node_id
        - task_ids
      title: TaskStealAction
      description: "A single steal action: move tasks from donor to receiver."
    TaskStealRequest:
      properties:
        queue_depths:
          additionalProperties:
            type: integer
          type: object
          title: Queue Depths
      type: object
      title: TaskStealRequest
      description: Body for POST /cluster/steal - report queue depths and request rebalancing.
    TaskStealResponse:
      properties:
        actions:
          items:
            $ref: "#/components/schemas/TaskStealAction"
          type: array
          title: Actions
        total_stolen:
          type: integer
          title: Total Stolen
      type: object
      required:
        - actions
        - total_stolen
      title: TaskStealResponse
      description: Response for POST /cluster/steal.
    TaskSteerPost:
      properties:
        kind:
          type: string
          maxLength: 64
          minLength: 1
          title: Kind
        principal:
          type: string
          maxLength: 1000
          title: Principal
          default: ""
        guidance:
          type: string
          maxLength: 2048
          title: Guidance
          default: ""
        redirect_target:
          type: string
          maxLength: 2048
          title: Redirect Target
          default: ""
        reason:
          type: string
          maxLength: 2048
          title: Reason
          default: ""
        session_id:
          type: string
          maxLength: 1000
          title: Session Id
          default: ""
        adapter:
          type: string
          maxLength: 1000
          title: Adapter
          default: ""
        worktree:
          type: string
          maxLength: 4096
          title: Worktree
          default: ""
        displayed_payload_hash:
          anyOf:
            - type: string
              maxLength: 1000
            - type: "null"
          title: Displayed Payload Hash
      type: object
      required:
        - kind
      title: TaskSteerPost
      description: |-
        Body for POST /tasks/{task_id}/steer (#2508).

        An operator steering command: pause, resume, guidance, redirect, or
        abort. Free-text fields are capped so the mailbox delivery envelope
        always fits the mailbox body cap. ``displayed_payload_hash`` is the hash
        the confirmation UI computed over what it showed the operator; when
        supplied the server rejects the action if it differs from the executed
        command, so the receipt binds exactly the confirmed payload.
    TaskSteerResponse:
      properties:
        kind:
          type: string
          title: Kind
        task_id:
          type: string
          title: Task Id
        principal:
          type: string
          title: Principal
        scope:
          type: string
          title: Scope
        payload_hash:
          type: string
          title: Payload Hash
        receipt_hash:
          type: string
          title: Receipt Hash
        timestamp:
          type: number
          title: Timestamp
        mailbox_seq:
          type: integer
          title: Mailbox Seq
        mailbox_entry_hash:
          type: string
          title: Mailbox Entry Hash
        checkpoint_event_hash:
          type: string
          title: Checkpoint Event Hash
          default: ""
        abort_signal_written:
          type: boolean
          title: Abort Signal Written
          default: false
      type: object
      required:
        - kind
        - task_id
        - principal
        - scope
        - payload_hash
        - receipt_hash
        - timestamp
        - mailbox_seq
        - mailbox_entry_hash
      title: TaskSteerResponse
      description: |-
        The receipt a steering action produced (#2508).

        The response IS the receipt: the chain-anchored ``receipt_hash`` the
        delivered effect references, the ``payload_hash`` it binds, and the
        mailbox journal position the effect was delivered at.
    TaskWaitForSubtasksRequest:
      properties:
        subtask_count:
          type: integer
          title: Subtask Count
          default: 0
      type: object
      title: TaskWaitForSubtasksRequest
      description: Body for POST /tasks/{task_id}/wait-for-subtasks.
    TraceTimelineEvent:
      properties:
        id:
          type: string
          title: Id
        ts:
          type: number
          title: Ts
        kind:
          type: string
          title: Kind
        actor:
          type: string
          title: Actor
          default: ""
        summary:
          type: string
          title: Summary
          default: ""
        outcome:
          type: string
          title: Outcome
          default: neutral
        trace_id:
          type: string
          title: Trace Id
          default: ""
        session_id:
          type: string
          title: Session Id
          default: ""
        payload:
          additionalProperties: true
          type: object
          title: Payload
      type: object
      required:
        - id
        - ts
        - kind
      title: TraceTimelineEvent
      description: |-
        One event card on the Trace tab timeline.

        Attributes:
            id: Stable per-task event identifier (``"{trace_idx}:{step_idx}"`` for
                steps; ``"{trace_idx}:meta"`` for the synthetic trace-level summary).
            ts: Unix timestamp (seconds, float). 0.0 means unknown.
            kind: Event kind - mirrors the TUI vocabulary
                (``spawn|orient|plan|edit|verify|complete|fail|compact|trace_meta``).
            actor: Best-effort attribution string - usually ``{role}/{model}`` or
                ``{session_id}``. Empty when unknown.
            summary: One-line human-readable description.
            outcome: ``success | failed | unknown | neutral`` - drives colour coding.
            trace_id: Owning trace id (so the FE can group events from the same spawn).
            session_id: Owning session id (mirrors the agent log filename).
            payload: Full event payload for the expandable JSON card.
    TraceTimelineResponse:
      properties:
        task_id:
          type: string
          title: Task Id
        events:
          items:
            $ref: "#/components/schemas/TraceTimelineEvent"
          type: array
          title: Events
        total:
          type: integer
          title: Total
        cursor:
          anyOf:
            - type: integer
            - type: "null"
          title: Cursor
        first_ts:
          anyOf:
            - type: number
            - type: "null"
          title: First Ts
        last_ts:
          anyOf:
            - type: number
            - type: "null"
          title: Last Ts
        has_open_trace:
          type: boolean
          title: Has Open Trace
          default: false
      type: object
      required:
        - task_id
        - events
        - total
      title: TraceTimelineResponse
      description: Container returned by ``GET /dashboard/tasks/{task_id}/trace``.
    UserProfileResponse:
      properties:
        id:
          type: string
          title: Id
        email:
          type: string
          title: Email
        display_name:
          type: string
          title: Display Name
        role:
          type: string
          title: Role
        sso_provider:
          type: string
          title: Sso Provider
        sso_groups:
          items:
            type: string
          type: array
          title: Sso Groups
        permissions:
          items:
            type: string
          type: array
          title: Permissions
      type: object
      required:
        - id
        - email
        - display_name
        - role
        - sso_provider
        - sso_groups
        - permissions
      title: UserProfileResponse
      description: Response for GET /auth/me.
    ValidationError:
      properties:
        loc:
          items:
            anyOf:
              - type: string
              - type: integer
          type: array
          title: Location
        msg:
          type: string
          title: Message
        type:
          type: string
          title: Error Type
        input:
          title: Input
        ctx:
          type: object
          title: Context
      type: object
      required:
        - loc
        - msg
        - type
      title: ValidationError
    VerifyChainRequest:
      properties:
        from_chunk:
          anyOf:
            - type: integer
            - type: "null"
          title: From Chunk
      type: object
      title: VerifyChainRequest
      description: Body for ``POST /audit/verify`` (re-verify chain or chunk).
    WebhookTaskCreate:
      properties:
        id:
          anyOf:
            - type: string
              maxLength: 1000
            - type: "null"
          title: Id
        title:
          type: string
          maxLength: 500
          title: Title
        description:
          type: string
          maxLength: 100000
          title: Description
        role:
          type: string
          title: Role
          default: backend
        tenant_id:
          type: string
          maxLength: 1000
          title: Tenant Id
          default: default
        priority:
          type: integer
          title: Priority
          default: 2
        scope:
          type: string
          maxLength: 1000
          title: Scope
          default: medium
        complexity:
          type: string
          maxLength: 1000
          title: Complexity
          default: medium
        eu_ai_act_risk:
          type: string
          maxLength: 1000
          title: Eu Ai Act Risk
          default: minimal
        approval_required:
          type: boolean
          title: Approval Required
          default: false
        risk_level:
          type: string
          maxLength: 1000
          title: Risk Level
          default: low
        estimated_minutes:
          anyOf:
            - type: integer
            - type: "null"
          title: Estimated Minutes
        depends_on:
          items:
            type: string
          type: array
          maxItems: 100
          title: Depends On
        parent_task_id:
          anyOf:
            - type: string
              maxLength: 1000
            - type: "null"
          title: Parent Task Id
        depends_on_repo:
          anyOf:
            - type: string
              maxLength: 4096
            - type: "null"
          title: Depends On Repo
        owned_files:
          items:
            type: string
          type: array
          maxItems: 100
          title: Owned Files
        cell_id:
          anyOf:
            - type: string
              maxLength: 1000
            - type: "null"
          title: Cell Id
        repo:
          anyOf:
            - type: string
              maxLength: 4096
            - type: "null"
          title: Repo
        task_type:
          type: string
          maxLength: 1000
          title: Task Type
          default: standard
        upgrade_details:
          anyOf:
            - additionalProperties: true
              type: object
            - type: "null"
          title: Upgrade Details
        model:
          anyOf:
            - type: string
              maxLength: 1000
            - type: "null"
          title: Model
        effort:
          anyOf:
            - type: string
              maxLength: 1000
            - type: "null"
          title: Effort
        cli:
          anyOf:
            - type: string
              maxLength: 1000
            - type: "null"
          title: Cli
        batch_eligible:
          type: boolean
          title: Batch Eligible
          default: false
        completion_signals:
          items:
            $ref: "#/components/schemas/CompletionSignalSchema"
          type: array
          maxItems: 100
          title: Completion Signals
        slack_context:
          anyOf:
            - additionalProperties: true
              type: object
            - type: "null"
          title: Slack Context
        metadata:
          additionalProperties: true
          type: object
          title: Metadata
        deadline:
          anyOf:
            - type: number
            - type: "null"
          title: Deadline
        parent_session_id:
          anyOf:
            - type: string
              maxLength: 1000
            - type: "null"
          title: Parent Session Id
        parent_context:
          anyOf:
            - type: string
              maxLength: 100000
            - type: "null"
          title: Parent Context
        retry_count:
          anyOf:
            - type: integer
            - type: "null"
          title: Retry Count
        max_retries:
          anyOf:
            - type: integer
            - type: "null"
          title: Max Retries
        retry_delay_s:
          anyOf:
            - type: number
            - type: "null"
          title: Retry Delay S
        terminal_reason:
          anyOf:
            - type: string
              maxLength: 100000
            - type: "null"
          title: Terminal Reason
        max_output_tokens:
          anyOf:
            - type: integer
            - type: "null"
          title: Max Output Tokens
        meta_messages:
          anyOf:
            - items:
                type: string
              type: array
              maxItems: 100
            - type: "null"
          title: Meta Messages
        max_turns:
          anyOf:
            - type: integer
              maximum: 10000
              minimum: 1
            - type: "null"
          title: Max Turns
        artifact_spec:
          anyOf:
            - additionalProperties: true
              type: object
            - type: "null"
          title: Artifact Spec
      type: object
      required:
        - title
        - description
      title: WebhookTaskCreate
      description: Body for POST /webhook.
    WebhookTaskResponse:
      properties:
        task:
          $ref: "#/components/schemas/TaskResponse"
        receipt:
          anyOf:
            - additionalProperties: true
              type: object
            - type: "null"
          title: Receipt
      type: object
      required:
        - task
      title: WebhookTaskResponse
      description: |-
        Serialized task returned by POST /webhook.

        ``receipt`` carries the signed, chain-anchored trigger receipt for the
        admitted trigger (#2512) so the calling automation platform stores a proof
        of what it asked for, not just a task reference. It is optional: an install
        whose bridge state is unavailable still creates the task and returns
        ``None`` rather than failing the caller.
    WorkspaceRepoResponse:
      properties:
        name:
          type: string
          title: Name
        path:
          type: string
          title: Path
        branch:
          type: string
          title: Branch
        clean:
          type: boolean
          title: Clean
        ahead:
          type: integer
          title: Ahead
        behind:
          type: integer
          title: Behind
      type: object
      required:
        - name
        - path
        - branch
        - clean
        - ahead
        - behind
      title: WorkspaceRepoResponse
      description: Workspace repository status entry.
    WorkspaceResponse:
      properties:
        repos:
          items:
            $ref: "#/components/schemas/WorkspaceRepoResponse"
          type: array
          title: Repos
      type: object
      required:
        - repos
      title: WorkspaceResponse
      description: Workspace repository status payload.
servers:
  - url: http://{host}:{port}
    description: Task server started by the Bernstein CLI on the operator's own machine. There is no
      hosted instance; substitute the host and port of your install.
    variables:
      host:
        default: 127.0.0.1
        description: Interface the task server binds to. Loopback by default.
      port:
        default: "8052"
        description: Task server port. Override with BERNSTEIN_PORT.
