openapi: 3.1.0
info:
  title: Painted Wolf Code Host API
  version: 1.0.0
  license:
    name: Apache License 2.0
    identifier: Apache-2.0
  description: |
    REST + SSE contract for the Painted Wolf Code local service.

    - JSON field names use snake_case on the wire.
    - Resource IDs are UUID strings unless noted.
    - Timestamps are RFC 3339 date-time strings.
    - SSE EventHub payloads use the envelope in `docs/schemas/events/envelope.json`.
servers:
  - url: http://127.0.0.1:8787
    description: Default local sidecar
security:
  - bearerAuth: []
tags:
  - name: sessions
    description: Chat sessions, transcripts, prompts, context, and session artifacts.
  - name: workflows
    description: Workflow catalogs, session composition, execution, and human transitions.
  - name: projects
    description: Project identity, roots, source access, and project-scoped history.
  - name: blueprints
    description: Reusable project and workflow launch configurations.
  - name: delegations
    description: Coordinator work delegation and leg lifecycle.
  - name: workers
    description: Background worker tasks and cancellation.
  - name: board
    description: Session planning and execution board snapshots.
  - name: events
    description: Authenticated server-sent event subscriptions.
  - name: cost
    description: Session and project model-usage accounting.
  - name: scan
    description: Security scan execution, findings, and scanner configuration.
  - name: mcp
    description: MCP provider configuration, discovery, OAuth, and health.
  - name: providers
    description: Language-model providers, credentials, models, and policy.
  - name: web-research
    description: Web-research provider configuration, credentials, and index status.
  - name: rules
    description: Effective project guidance and rule surfaces.
  - name: settings
    description: Device and project preferences, approvals, and model policy.
  - name: git
    description: Command endpoints for the active project's git working tree (status, checkout, discard, commit, push, pull, stash). These are RPC-style verbs, not REST resources — each POST performs a named git action against the session's roots rather than mutating an addressable object.
  - name: search
    description: Cross-source search, replacement, and export.
  - name: debug
    description: Local diagnostics and performance telemetry.
  - name: health
    description: Sidecar readiness, compatibility recovery, and host preflight facts.
  - name: host
    description: Host identity, contract version, and the authenticated caller.
  - name: local-data
    description: Command endpoint for clearing rebuildable ephemeral local buckets. Like the git routes, `clear` is a deliberate RPC-style action verb, not a REST resource; it never touches the durable store or credentials.
  - name: backup
    description: Local durable-install backup and staged restore. Distinct from the scrubbed diagnostics bundle and from rebuildable Cache clears.
  - name: extensions
    description: Extension packs, installed units, suggestions, and desired state.
  - name: contributions
    description: Runtime projection of the captured contribution frame and the two invocation routes for host-executed contribution commands.
  - name: host-resources
    description: Host capability inventory and user-granted access policy.
  - name: attention
    description: Cross-project view of the sessions asking something of the human. Read as a snapshot here; kept live on the device-scoped `attention` SSE topic.
paths:
  /health:
    get:
      tags:
        - health
      operationId: getHealth
      summary: Health check
      security: []
      responses:
        '200':
          description: Server is ready
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HealthResponse'
        '405':
          $ref: '#/components/responses/MethodNotAllowed'
        '500':
          $ref: '#/components/responses/InternalError'
  /v1/preflight:
    get:
      tags:
        - health
      operationId: getPreflight
      summary: Environment readiness probes
      description: 'Machine-state facts about the host the app depends on but does not control: macOS version, config-dir writability, free disk, the bundled git engine, and the bundled scanner and browser engines. Probes run in the sidecar so they resolve binaries the same way the tools do. Reporting only — preflight never installs or downloads anything.'
      responses:
        '200':
          description: Probe results in registration order
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PreflightReport'
        '401':
          $ref: '#/components/responses/Unauthorized'
        default:
          $ref: '#/components/responses/Error'
  /v1/diagnostics/export:
    get:
      tags:
        - health
      operationId: exportDiagnostics
      summary: Redacted local diagnostics bundle
      description: Assembles a zip containing health, the preflight report, resolved config, and recent log tails — every byte passed through the shared secret scrubber, with secret stores excluded entirely. The response is bytes for the user to save. Nothing is uploaded.
      responses:
        '200':
          description: Zip archive
          content:
            application/zip:
              schema:
                type: string
                format: binary
        '401':
          $ref: '#/components/responses/Unauthorized'
        '500':
          $ref: '#/components/responses/Error'
          description: Diagnostics export assembly failed (diagnostics_export_failed)
        default:
          $ref: '#/components/responses/Error'
  /v1/sessions:
    post:
      tags:
        - sessions
      operationId: createSession
      x-person-action: record
      summary: Create a coordinator session
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateSessionRequest'
      responses:
        '202':
          description: Durable session accepted; workspace preparation completes asynchronously
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Session'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '503':
          description: Provider discovery failed while validating the model assignment; provider_catalog_unavailable is retryable and no assignment is applied
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        default:
          $ref: '#/components/responses/Error'
  /v1/sessions/{id}:
    get:
      tags:
        - sessions
      operationId: getSession
      summary: Get session by ID
      parameters:
        - $ref: '#/components/parameters/SessionId'
      responses:
        '200':
          description: Session
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Session'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        default:
          $ref: '#/components/responses/Error'
    patch:
      tags:
        - sessions
      operationId: updateSession
      x-person-action: record
      summary: Mutate session lifecycle state (title, archived, pinned, pin position)
      parameters:
        - $ref: '#/components/parameters/SessionId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateSessionRequest'
      responses:
        '200':
          description: Updated session
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Session'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
    delete:
      tags:
        - sessions
      operationId: deleteSession
      x-person-action: record
      summary: Permanently delete a chat session and its history
      description: |
        Removes the session, its transcript, and its session-keyed projections. Refused with session_not_idle while a turn is in flight; worker child sessions are never deletable through this surface.
      parameters:
        - $ref: '#/components/parameters/SessionId'
      responses:
        '204':
          description: Session deleted
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/sessions/{id}/bootstrap:
    get:
      tags:
        - sessions
      operationId: getSessionBootstrap
      summary: Read the authoritative first-paint session snapshot
      parameters:
        - $ref: '#/components/parameters/SessionId'
      responses:
        '200':
          description: Session and first-paint state in one response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SessionBootstrap'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        default:
          $ref: '#/components/responses/Error'
  /v1/sessions/{id}/navigation:
    post:
      tags:
        - sessions
      operationId: resolveMessageNavigation
      x-person-action: none
      summary: Resolve a settled message's navigation destinations
      parameters:
        - $ref: '#/components/parameters/SessionId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ResolveMessageNavigationRequest'
      responses:
        '200':
          description: Bounded navigation resolutions, including incomplete coverage and candidate pages
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MessageNavigationResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/sessions/{id}/messages:
    get:
      tags:
        - sessions
      operationId: listSessionMessages
      summary: List a paginated Ord window of session messages
      description: |
        Returns one contiguous message window ordered by immutable Ord, plus the
        transcript watermark. Omit before/after to fetch the newest page (live
        tail), or from=oldest for the first page. Pass a page's before_cursor as
        before for the older window, or its after_cursor as after for the newer
        one; before_message_id and after_message_id open the window beside a
        message the client already holds. A request names at most one of these
        positions. worker_id restricts a reused child session to one immutable
        worker run before pagination is applied.
      parameters:
        - $ref: '#/components/parameters/SessionId'
        - name: limit
          in: query
          required: false
          schema:
            type: integer
            minimum: 1
            maximum: 500
            default: 100
          description: Max messages in the window (default 100, max 500)
        - name: before
          in: query
          required: false
          schema:
            type: string
          description: Opaque pagination cursor — return older messages before this cursor
        - name: after
          in: query
          required: false
          schema:
            type: string
          description: Opaque pagination cursor — return newer messages after this cursor
        - name: before_message_id
          in: query
          required: false
          schema:
            type: string
            format: uuid
          description: Return the window of messages older than this message of the chat
        - name: after_message_id
          in: query
          required: false
          schema:
            type: string
            format: uuid
          description: Return the window of messages newer than this message of the chat
        - name: from
          in: query
          required: false
          schema:
            type: string
            enum:
              - newest
              - oldest
            default: newest
          description: Which end of the transcript an unpositioned window starts from
        - name: worker_id
          in: query
          required: false
          schema:
            type: string
            format: uuid
          description: Restrict messages to the associated worker run
      responses:
        '200':
          description: One Ord window plus cursors and the transcript watermark
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SessionTranscriptPage'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        default:
          $ref: '#/components/responses/Error'
  /v1/sessions/{id}/messages/{message_id}/content:
    get:
      tags:
        - sessions
      operationId: getChatContent
      x-positional-window: true
      summary: Read a bounded range of retained chat content
      parameters:
        - $ref: '#/components/parameters/SessionId'
        - name: message_id
          in: path
          required: true
          schema:
            type: string
            format: uuid
        - name: field
          in: query
          required: true
          schema:
            type: string
            enum:
              - content
              - tool_output
              - tool_args
        - name: tool_call_id
          in: query
          schema:
            type: string
            pattern: ^[A-Za-z0-9_.:-]+$
        - name: sha256
          in: query
          required: true
          schema:
            type: string
            pattern: ^[0-9a-f]{64}$
        - name: offset
          in: query
          schema:
            type: integer
            minimum: 0
        - name: limit
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 16384
            default: 8192
        - name: row
          in: query
          schema:
            type: integer
            minimum: 0
          description: Read up to 64 display rows starting here instead of a rune range.
        - name: locate
          in: query
          schema:
            type: integer
            minimum: 0
          description: Read the display rows containing this rune offset.
      responses:
        '200':
          description: Screened content from the exact requested revision
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ChatContentPage'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        default:
          $ref: '#/components/responses/Error'
  /v1/sessions/{id}/messages/{message_id}/content/search:
    get:
      tags:
        - sessions
      operationId: searchChatContent
      summary: Search retained chat content without downloading its body
      parameters:
        - $ref: '#/components/parameters/SessionId'
        - name: message_id
          in: path
          required: true
          schema:
            type: string
            format: uuid
        - name: field
          in: query
          required: true
          schema:
            type: string
            enum:
              - content
              - tool_output
              - tool_args
        - name: tool_call_id
          in: query
          schema:
            type: string
            pattern: ^[A-Za-z0-9_.:-]+$
        - name: sha256
          in: query
          required: true
          schema:
            type: string
            pattern: ^[0-9a-f]{64}$
        - name: q
          in: query
          required: true
          schema:
            type: string
            minLength: 1
            maxLength: 1024
        - name: cursor
          in: query
          schema:
            type: string
        - name: limit
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 500
            default: 50
        - name: case_sensitive
          in: query
          schema:
            type: boolean
      responses:
        '200':
          description: Bounded matches at rune offsets in the requested revision
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ChatContentSearchPage'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        default:
          $ref: '#/components/responses/Error'
  /v1/sessions/{id}/invocations:
    get:
      tags:
        - sessions
      operationId: listSessionInvocations
      summary: List durable invocation receipts for a session
      parameters:
        - $ref: '#/components/parameters/SessionId'
        - name: cursor
          in: query
          required: false
          description: Keyset pagination cursor
          schema:
            type: string
        - name: limit
          in: query
          required: false
          description: Maximum receipts to return
          schema:
            type: integer
            minimum: 1
            maximum: 500
            default: 50
      responses:
        '200':
          description: Invocation receipts in dispatch order
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/InvocationReceiptList'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        default:
          $ref: '#/components/responses/Error'
  /v1/sessions/{id}/export:
    get:
      tags:
        - sessions
      operationId: exportSessionTranscript
      summary: Export one session transcript as Markdown or JSON
      description: Downloads a portable copy of one conversation. format=md (default) is a human-readable conversation with transcript chrome filtered out. format=json is the full-fidelity SessionTranscriptPage. Filename is derived from the session title and the first eight characters of the session id.
      parameters:
        - $ref: '#/components/parameters/SessionId'
        - name: format
          in: query
          required: false
          schema:
            type: string
            enum:
              - md
              - json
            default: md
          description: Export format (default md)
      responses:
        '200':
          description: Transcript export attachment
          content:
            text/markdown:
              schema:
                type: string
            application/json:
              schema:
                $ref: '#/components/schemas/SessionTranscriptPage'
        '400':
          description: Unknown export format
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          description: Session not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        default:
          $ref: '#/components/responses/Error'
  /v1/sessions/{id}/prompts:
    post:
      tags:
        - sessions
      operationId: sendPrompt
      x-person-action: record
      summary: Send user prompt to session
      parameters:
        - $ref: '#/components/parameters/SessionId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PromptRequest'
      responses:
        '202':
          description: Prompt enqueued — coordinator turn runs asynchronously; transcript and errors arrive on SSE
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PromptAcceptedResponse'
        '400':
          description: Invalid prompt or attachment rejected with unsupported_attachment, attachment_not_found, or reference_out_of_jail
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '413':
          description: Inline prose or an attachment exceeded a size cap (prompt_text_too_large, attachment_too_large)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '503':
          description: Prompt or project attachment storage is unavailable
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        default:
          $ref: '#/components/responses/Error'
  /v1/sessions/{id}/abort:
    post:
      tags:
        - sessions
      operationId: abortSession
      x-person-action: record
      summary: Stop all activity attached to a session tree
      description: Stops the session and every attached child activity. The request rejects new work while it cancels and joins queued prompts, child sessions, workers, workflows, checkpoints, tool invocations, and background session tasks. It returns after the session is idle and leaves persisted messages and results intact.
      parameters:
        - $ref: '#/components/parameters/SessionId'
      requestBody:
        required: false
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AbortSessionRequest'
      responses:
        '200':
          description: Session after abort (status idle)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Session'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/sessions/{id}/rewind/preview:
    post:
      tags:
        - sessions
      operationId: previewSessionRewind
      x-person-action: none
      summary: Preview the source changes and conflicts for a rewind
      parameters:
        - $ref: '#/components/parameters/SessionId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/RewindPreviewRequest'
      responses:
        '200':
          description: Exact plan identity or structured conflicts; no source or transcript mutation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RewindPreviewResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '422':
          $ref: '#/components/responses/UnprocessableContent'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/sessions/{id}/rewind:
    post:
      tags:
        - sessions
      operationId: rewindSession
      x-person-action: record
      summary: Undo a turn — restore files and drop the ask and everything after it
      description: 'Restores the project and session to the state immediately before the anchor user message''s turn started: all selected source effects are reversed through exact retained versions, and the anchor plus every later transcript row is removed. Host-executed and not a tool — the model must not be able to undo itself. The session must be idle; a running turn or an in-flight worker rejects with session_not_idle. Unrelated to the HITL /checkpoints approval bus. See docs/session.md § Session recovery.'
      parameters:
        - $ref: '#/components/parameters/SessionId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/RewindSessionRequest'
      responses:
        '200':
          description: What the rewind restored and removed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RewindSessionResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '422':
          $ref: '#/components/responses/UnprocessableContent'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/sessions/{id}/seen:
    post:
      tags:
        - sessions
      operationId: markSessionSeen
      x-person-action: none
      summary: Record that the person has read this session
      description: 'Stamps `seen_at` with the host clock. Clients call this while the conversation is readable on screen in a focused window, not merely loaded. The stamp clears the attention view''s `finished` class and opens the session''s project for the recents order (`last_opened_at`). The host supplies the time, so no client can mark a turn read before it lands. Idempotent: the newest call wins, and a turn completing after the stamp makes the session unread again.'
      parameters:
        - $ref: '#/components/parameters/SessionId'
      responses:
        '200':
          description: Session carrying the new seen_at
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Session'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        default:
          $ref: '#/components/responses/Error'
  /v1/sessions/{id}/background/{process_id}/stop:
    post:
      tags:
        - sessions
      operationId: stopBackgroundProcess
      x-person-action: record
      summary: Stop a running background process
      description: Command endpoint that terminates a background command process, or cancels a held call, started in the session. RPC-style, not a REST resource — background process lifecycle is a host control action, and generic tool invocation stays on the agent loop rather than the HTTP surface.
      parameters:
        - $ref: '#/components/parameters/SessionId'
        - name: process_id
          in: path
          required: true
          schema:
            type: string
            pattern: ^[A-Za-z0-9_-]+$
      responses:
        '200':
          description: Stop result
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BackgroundProcessStopResult'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/sessions/{id}/background:
    get:
      tags:
        - sessions
      operationId: listSessionBackgroundProcesses
      summary: List recoverable background processes
      description: 'Snapshot of visible background processes for a session, bounded to active and retained recoverable background processes for this session: command jobs and read-only calls held past their foreground wait, which report a state and no output. Den uses this read-only endpoint to rebuild its process cards after a frontend reload; incremental output remains on the process SSE topic.'
      parameters:
        - $ref: '#/components/parameters/SessionId'
      responses:
        '200':
          description: Visible background processes
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BackgroundProcessListResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        default:
          $ref: '#/components/responses/Error'
  /v1/sessions/{id}/background/{process_id}/output:
    get:
      tags:
        - sessions
      operationId: getSessionBackgroundProcessOutput
      summary: Get the retained output tail of a background process
      description: 'Returns the screened tail of one visible background process''s retained output with its lifecycle state. The snapshot is bounded by the host and takes no position: truncated reports cut or evicted output, and live output continues on the process event topic.'
      parameters:
        - $ref: '#/components/parameters/SessionId'
        - name: process_id
          in: path
          required: true
          schema:
            type: string
            pattern: ^[A-Za-z0-9_-]+$
      responses:
        '200':
          description: Process output snapshot
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BackgroundProcessOutput'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        default:
          $ref: '#/components/responses/Error'
  /v1/sessions/{id}/previews:
    get:
      tags:
        - sessions
      operationId: listSessionPreviews
      summary: List live preview page attachments for a session
      description: Read-only preview attachment snapshot for frontend reload recovery, bounded to active browser pages held by this session. Returned rows identify held pages only; a visible Den pane starts its own watch to receive fresh image frames over SSE.
      parameters:
        - $ref: '#/components/parameters/SessionId'
      responses:
        '200':
          description: Current preview page attachments
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PreviewListResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        default:
          $ref: '#/components/responses/Error'
  /v1/sessions/{id}/previews/watch:
    post:
      tags:
        - sessions
      operationId: watchPreview
      x-person-action: none
      summary: Toggle live page preview watch for a session
      description: Command endpoint that starts or stops the host CDP screencast for a held (or in-flight drive) page when Den's preview pane is visible. Read-only mirror only — no input is accepted. Frames stream on SSE topic preview.
      parameters:
        - $ref: '#/components/parameters/SessionId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PreviewWatchRequest'
      responses:
        '200':
          description: Watch result
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PreviewWatchResult'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/sessions/{id}/stream:
    get:
      tags:
        - sessions
      operationId: streamSession
      summary: Stream one message for a session
      description: Each SSE data frame is a JSON object. `token` carries message text; `reset` selects replacement instead of append, including when `token` is empty; `tool_calls` replaces the in-flight tool-call list when present; `done` terminates the stream. Live frames are full snapshots with `reset` true. Settled replay resets on its first chunk and appends later chunks; a persisted fallback is one reset snapshot.
      parameters:
        - $ref: '#/components/parameters/SessionId'
        - name: message
          in: query
          required: false
          schema:
            type: string
            format: uuid
          description: Message ID to stream
      responses:
        '200':
          description: Server-sent events stream
          content:
            text/event-stream:
              schema:
                $ref: '#/components/schemas/PromptStreamChunk'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        default:
          $ref: '#/components/responses/Error'
  /v1/sessions/{id}/compact:
    post:
      tags:
        - sessions
      operationId: compactSession
      x-person-action: record
      summary: Force context compaction for a session
      parameters:
        - $ref: '#/components/parameters/SessionId'
      responses:
        '200':
          description: Compaction result
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SessionCompactResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/sessions/{id}/elevated-access:
    get:
      tags:
        - sessions
      operationId: getElevatedAccess
      summary: Get live elevated access for a chat and its workers
      parameters:
        - $ref: '#/components/parameters/SessionId'
      responses:
        '200':
          description: Host-selected standing elevated authority
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ElevatedAccessSummary'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        default:
          $ref: '#/components/responses/Error'
  /v1/sessions/{id}/elevated-access/revoke:
    post:
      tags:
        - sessions
      operationId: revokeElevatedAccess
      x-person-action: record
      summary: Revoke all currently applicable elevated-access approvals
      description: Shared project and device records are revoked for all consumers. Running processes retain already-issued access.
      parameters:
        - $ref: '#/components/parameters/SessionId'
      responses:
        '200':
          description: Per-record outcomes and remaining live authority
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RevokeElevatedAccessResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/sessions/{id}/context:
    get:
      tags:
        - sessions
      operationId: getSessionContext
      summary: Debug session context token budget and compaction stats
      parameters:
        - $ref: '#/components/parameters/SessionId'
      responses:
        '200':
          description: Session context stats
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SessionContextResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        default:
          $ref: '#/components/responses/Error'
  /v1/sessions/{id}/composer-secrets:
    post:
      tags:
        - sessions
      operationId: createComposerSecret
      x-person-action: record
      summary: Protect selected composer text for this task
      parameters:
        - $ref: '#/components/parameters/SessionId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateComposerSecretRequest'
      responses:
        '201':
          description: Value-free managed secret metadata
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ManagedSecret'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
        default:
          $ref: '#/components/responses/Error'
  /v1/sessions/{id}/findings:
    get:
      tags:
        - sessions
      operationId: getSessionFindings
      summary: Findings digest for a session
      parameters:
        - $ref: '#/components/parameters/SessionId'
      responses:
        '200':
          description: Findings digest
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FindingsDigest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        default:
          $ref: '#/components/responses/Error'
  /v1/sessions/{id}/drafts/{slot_id}/versions:
    get:
      tags:
        - sessions
      operationId: listDraftVersions
      summary: Superseded coordinator draft bodies for one slot
      description: Bounded read of prior draft attempt snapshots for the specified coordinator draft slot.
      parameters:
        - $ref: '#/components/parameters/SessionId'
        - $ref: '#/components/parameters/DraftSlotId'
      responses:
        '200':
          description: Prior draft attempt snapshots in version order
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DraftVersionsResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        default:
          $ref: '#/components/responses/Error'
  /v1/sessions/{id}/progress:
    get:
      tags:
        - sessions
      operationId: getSessionProgress
      summary: Coordinator progress digest for a session
      parameters:
        - $ref: '#/components/parameters/SessionId'
      responses:
        '200':
          description: Progress digest
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProgressDigest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        default:
          $ref: '#/components/responses/Error'
  /v1/sessions/{id}/artifacts:
    get:
      tags:
        - sessions
      operationId: listSessionArtifacts
      summary: List durable visual artifact metadata for a session tree
      parameters:
        - $ref: '#/components/parameters/SessionId'
        - name: cursor
          in: query
          required: false
          schema:
            type: string
          description: Opaque cursor from the previous page's next_cursor.
        - name: limit
          in: query
          required: false
          schema:
            type: integer
            minimum: 1
            maximum: 500
            default: 50
          description: Max items to return
      responses:
        '200':
          description: Artifact metadata for the session tree (no bytes)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ArtifactListResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        default:
          $ref: '#/components/responses/Error'
    post:
      tags:
        - sessions
      operationId: createSessionArtifact
      x-person-action: record
      summary: Persist a live-tool recording as a visual artifact
      description: |-
        Accepts a client-created low-frame-rate baseline H.264 MP4 recording of authenticated live-preview frames produced by a held page tool. The sidecar enforces the recording MIME type and artifact byte cap before writing through the shared visual artifact store.

        Identity is durable, not process-local. operation_id is one client action, so a retried upload lands on the record it already wrote rather than failing; holder + page + assistant_message_id + tool_call_id is one invocation-specific product slot, so re-recording that invocation replaces its recording rather than multiplying it. Both identities survive a sidecar restart. The upload path session is the page holder (coordinator root or worker child), and Den remounts the recording at the originating ordered tool row.
      parameters:
        - $ref: '#/components/parameters/SessionId'
        - name: page_id
          in: query
          required: true
          description: Held page-tool attachment that produced the preview frames.
          schema:
            type: string
            pattern: ^[A-Za-z0-9_.:-]+$
        - name: assistant_message_id
          in: query
          required: true
          description: Ordered assistant transcript row that opened the recording episode.
          schema:
            type: string
            format: uuid
        - name: tool_call_id
          in: query
          required: true
          description: Tool invocation within the containing assistant row.
          schema:
            type: string
            pattern: ^[A-Za-z0-9_.:-]+$
        - name: operation_id
          in: query
          required: true
          description: Client-chosen id for this upload action. Replaying it returns the same artifact instead of writing a second one.
          schema:
            type: string
            format: uuid
        - name: recorded_at
          in: query
          required: false
          schema:
            type: string
            format: date-time
        - name: duration_ms
          in: query
          required: false
          schema:
            type: integer
            format: int64
            minimum: 0
      requestBody:
        required: true
        content:
          video/mp4:
            schema:
              type: string
              format: binary
      responses:
        '201':
          description: Persisted live-tool recording
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/VisualArtifact'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/sessions/{id}/artifacts/{artifact_id}:
    get:
      tags:
        - sessions
      operationId: getSessionArtifact
      summary: Fetch visual artifact bytes for a session tree
      parameters:
        - $ref: '#/components/parameters/SessionId'
        - name: artifact_id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Artifact bytes
          content:
            image/*:
              schema:
                type: string
                format: binary
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          description: The artifact belongs to a different session tree (artifact_foreign)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: No such session (session_not_found) or artifact (artifact_not_found)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '410':
          description: The artifact was deleted (artifact_deleted) or its bytes are no longer retained (artifact_unavailable)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        default:
          $ref: '#/components/responses/Error'
  /v1/sessions/{id}/queue:
    get:
      tags:
        - sessions
      operationId: getSessionQueue
      summary: Editable next-turn queue for a session
      parameters:
        - $ref: '#/components/parameters/SessionId'
      responses:
        '200':
          description: Queue draft
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/QueueDraft'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        default:
          $ref: '#/components/responses/Error'
    patch:
      tags:
        - sessions
      operationId: updateSessionQueue
      x-person-action: record
      summary: Mutate the editable next-turn queue
      parameters:
        - $ref: '#/components/parameters/SessionId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/QueueMutateRequest'
      responses:
        '200':
          description: Updated queue draft
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/QueueDraft'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/sessions/{id}/coordinator-context:
    get:
      tags:
        - sessions
      operationId: getCoordinatorContext
      summary: Host-built coordinator workflow run context (same block as Prompt prepend)
      parameters:
        - $ref: '#/components/parameters/SessionId'
      responses:
        '200':
          description: Coordinator run context
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CoordinatorRunContext'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        default:
          $ref: '#/components/responses/Error'
  /v1/sessions/{id}/checkpoints:
    get:
      tags:
        - sessions
      operationId: listCheckpoints
      summary: List human checkpoints for a session
      parameters:
        - $ref: '#/components/parameters/SessionId'
        - name: kind
          in: query
          required: false
          schema:
            $ref: '#/components/schemas/CheckpointKind'
        - name: status
          in: query
          required: false
          schema:
            $ref: '#/components/schemas/CheckpointStatus'
            default: pending
        - name: include_children
          in: query
          description: Include pending checkpoints belonging to worker children of this session.
          schema:
            type: boolean
            default: false
      responses:
        '200':
          description: Checkpoints
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CheckpointListResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        default:
          $ref: '#/components/responses/Error'
  /v1/sessions/{id}/checkpoints/{checkpoint_id}:
    post:
      tags:
        - sessions
      operationId: resolveCheckpoint
      x-person-action: record
      summary: Resolve a pending human checkpoint
      parameters:
        - $ref: '#/components/parameters/SessionId'
        - name: checkpoint_id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ResolveCheckpointRequest'
      responses:
        '200':
          description: Resolved checkpoint
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CheckpointResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/Error'
        '409':
          $ref: '#/components/responses/Error'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects:
    get:
      tags:
        - projects
      operationId: listProjects
      summary: List projects (recent-first)
      parameters:
        - name: limit
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 200
            default: 100
        - name: cursor
          in: query
          description: Opaque cursor returned as next_cursor by the previous page.
          schema:
            type: string
      responses:
        '200':
          description: Projects ordered by last_opened_at descending
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProjectListResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '409':
          $ref: '#/components/responses/Conflict'
        default:
          $ref: '#/components/responses/Error'
    post:
      tags:
        - projects
      operationId: createProject
      x-person-action: record
      summary: Create a project (lazy materialization)
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateProjectRequest'
      responses:
        '201':
          description: Project created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Project'
        '400':
          description: |
            Root path refused as a write jail (write_root_is_filesystem_root, write_root_is_home, write_root_not_absolute, write_root_under_secret_store) or otherwise invalid.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects/clone:
    post:
      tags:
        - projects
      operationId: cloneProject
      x-person-action: record
      summary: Clone a remote into a new folder and open it as a project
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CloneProjectRequest'
      responses:
        '201':
          description: Project created from the clone
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Project'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          description: A folder with that name already exists at the destination (FOLDER_EXISTS)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '502':
          $ref: '#/components/responses/BadGateway'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects/detect:
    get:
      tags:
        - projects
      operationId: detectFolder
      summary: Project identity and extension suggestions for a folder
      description: |
        Returns path, project_id when the folder is already a root, and project extension suggestions allowed by its trust settings.
      parameters:
        - name: path
          in: query
          required: true
          schema:
            type: string
          description: Absolute path of the folder to preview.
      responses:
        '200':
          description: Folder identity and extension suggestion preview
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FolderDetect'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects/{id}:
    get:
      tags:
        - projects
      operationId: getProject
      summary: Get a project with embedded roots
      parameters:
        - $ref: '#/components/parameters/ProjectId'
      responses:
        '200':
          description: Project
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Project'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        default:
          $ref: '#/components/responses/Error'
    patch:
      tags:
        - projects
      operationId: updateProject
      x-person-action: record
      summary: Rename a project workspace
      parameters:
        - $ref: '#/components/parameters/ProjectId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateProjectRequest'
      responses:
        '200':
          description: Updated project
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Project'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects/{id}/removal-assessment:
    get:
      tags:
        - projects
      operationId: assessProjectRemoval
      summary: Assess optional extension cleanup for project deletion
      parameters:
        - $ref: '#/components/parameters/ProjectId'
      responses:
        '200':
          description: Host-owned removal evidence or outcome
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProjectRemovalAssessment'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects/{id}/removals:
    post:
      tags:
        - projects
      operationId: createProjectRemoval
      x-person-action: record
      summary: Delete a project and settle reviewed optional extension cleanup
      parameters:
        - $ref: '#/components/parameters/ProjectId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ProjectRemovalRequest'
      responses:
        '202':
          description: Host-owned removal evidence or outcome
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProjectRemovalResult'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects/{id}/removals/{operation_id}:
    get:
      tags:
        - projects
      operationId: getProjectRemoval
      summary: Read the recorded project removal outcome
      parameters:
        - $ref: '#/components/parameters/ProjectId'
        - name: operation_id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Host-owned removal evidence or outcome
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProjectRemovalResult'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects/{id}/sessions:
    get:
      tags:
        - projects
      operationId: listProjectSessions
      summary: Page a project's chat sessions
      description: |
        Metadata-only chat browsing for one project. Worker child sessions are never listed. The default view is the active set; archived=true selects the archived set instead. pinned narrows either set to pinned or unpinned chats.
      parameters:
        - $ref: '#/components/parameters/ProjectId'
        - name: archived
          in: query
          schema:
            type: boolean
            default: false
        - name: pinned
          in: query
          description: true lists only pinned chats, false only unpinned ones; omitted lists both
          schema:
            type: boolean
        - name: q
          in: query
          description: Case-insensitive title substring filter
          schema:
            type: string
        - name: sort
          in: query
          description: pin requires pinned=true
          schema:
            $ref: '#/components/schemas/SessionListSort'
            default: activity
        - name: order
          in: query
          description: Defaults per sort key — activity and created sort desc, title and pin asc
          schema:
            $ref: '#/components/schemas/SessionListOrder'
        - name: limit
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 200
            default: 50
        - name: cursor
          in: query
          description: Opaque next_cursor from the preceding page
          schema:
            type: string
      responses:
        '200':
          description: One seek page of session summaries plus total under the same filter
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SessionListPage'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects/{id}/trust:
    get:
      tags:
        - projects
      operationId: getProjectTrust
      summary: What this project supplies to the agent and which of it applies
      parameters:
        - $ref: '#/components/parameters/ProjectId'
      responses:
        '200':
          description: Discovered surfaces with enable and seen state
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProjectTrust'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
        default:
          $ref: '#/components/responses/Error'
    patch:
      tags:
        - projects
      operationId: updateProjectTrust
      x-person-action: record
      summary: Switch project surfaces on or off
      parameters:
        - $ref: '#/components/parameters/ProjectId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateProjectTrustRequest'
      responses:
        '200':
          description: Updated project trust
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProjectTrust'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects/{id}/trust/review:
    post:
      tags:
        - projects
      operationId: openProjectTrustReview
      x-person-action: none
      summary: Mark the shared Trust comparison read without replacing it
      parameters:
        - $ref: '#/components/parameters/ProjectId'
      responses:
        '200':
          description: Shared Trust state with the captured comparison marked read
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProjectTrust'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Error'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects/{id}/agent-context:
    get:
      tags:
        - projects
      operationId: getProjectAgentContext
      summary: Resolve actual repository guidance for one project path
      parameters:
        - $ref: '#/components/parameters/ProjectId'
        - $ref: '#/components/parameters/SourceSessionIdQuery'
        - name: root_id
          in: query
          required: true
          schema:
            type: string
            format: uuid
        - name: path
          in: query
          required: false
          schema:
            type: string
            default: .
      responses:
        '200':
          description: Applied AGENTS.md chain and available project skills for the requested scope
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProjectAgentContext'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects/{id}/artifacts:
    get:
      tags:
        - projects
      operationId: listProjectArtifacts
      summary: List durable visual artifact metadata for a project
      parameters:
        - $ref: '#/components/parameters/ProjectId'
        - name: limit
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 500
            default: 100
        - name: cursor
          in: query
          description: Opaque cursor returned as next_cursor by the previous page.
          schema:
            type: string
      responses:
        '200':
          description: Artifact metadata for the project (no bytes)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ArtifactListResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects/{id}/artifacts/{artifact_id}:
    delete:
      tags:
        - projects
      operationId: deleteProjectArtifact
      x-person-action: record
      summary: Delete one durable visual artifact
      description: 'Tombstones the artifact and drops its stored bytes when nothing else needs them. The record itself survives: the id may already be spliced into a model-visible tool result, which cannot be rewritten, so it keeps resolving — as a deleted artifact. The response reports what referred to it at the moment of deletion.'
      parameters:
        - $ref: '#/components/parameters/ProjectId'
        - name: artifact_id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '204':
          description: Artifact tombstoned
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          description: No such artifact in this project
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects/{id}/secrets:
    get:
      tags:
        - projects
      operationId: listProjectManagedSecrets
      summary: List managed secret metadata for a project
      description: Returns names, creation origins, scopes, references, and lifecycle state. Secret values are never returned.
      parameters:
        - $ref: '#/components/parameters/ProjectId'
      responses:
        '200':
          description: Value-free managed-secret inventory
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ManagedSecretList'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        default:
          $ref: '#/components/responses/Error'
    post:
      tags:
        - projects
      operationId: createProjectManagedSecret
      x-person-action: record
      summary: Store a value a person entered in project settings
      description: 'Mints a project-scoped capability from a credential the person typed in. The value travels in this request body and nowhere else: it is written to the encrypted credential vault and is not returned by this response or any later read.'
      parameters:
        - $ref: '#/components/parameters/ProjectId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateManagedSecretRequest'
      responses:
        '201':
          description: The stored capability, without its value
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ManagedSecret'
        '400':
          description: The value or its labels cannot become a managed secret
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects/{id}/secrets/{secret_id}:
    patch:
      tags:
        - projects
      operationId: updateProjectManagedSecret
      x-person-action: record
      summary: Administer one managed secret
      description: Updates labels, agent-use deadline, and scope. Nothing here reaches the stored value, an external credential, or a project file, and a revoked capability is terminal.
      parameters:
        - $ref: '#/components/parameters/ProjectId'
        - name: secret_id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateManagedSecretRequest'
      responses:
        '200':
          description: Updated capability metadata
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ManagedSecret'
        '400':
          description: The update does not apply to this capability
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: No such secret in this project
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '409':
          description: The capability is revoked, which is terminal
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
        default:
          $ref: '#/components/responses/Error'
    delete:
      tags:
        - projects
      operationId: revokeProjectManagedSecret
      x-person-action: record
      summary: Permanently revoke one managed secret
      description: Disables reference resolution and authenticated reveal permanently. Protected bytes remain only for provider screening until project deletion.
      parameters:
        - $ref: '#/components/parameters/ProjectId'
        - name: secret_id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '204':
          description: Revoked capability metadata
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: No such secret in this project
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '409':
          $ref: '#/components/responses/Conflict'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects/{id}/secrets/{secret_id}/value:
    put:
      tags:
        - projects
      operationId: replaceProjectManagedSecretValue
      x-person-action: record
      summary: Replace or restore one managed secret's stored value
      description: Puts new bytes behind an unchanged reference. Only Painted Wolf's protected copy changes; this does not modify a project file or rotate, revoke, or activate an external credential. The replaced value is retired rather than deleted, so provider screening keeps redacting a credential the agent has already seen; a retired value never resolves. The same call restores a capability whose stored value went missing.
      parameters:
        - $ref: '#/components/parameters/ProjectId'
        - name: secret_id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ReplaceManagedSecretValueRequest'
      responses:
        '200':
          description: Capability metadata at its new version
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ManagedSecret'
        '400':
          description: The value cannot be stored, or already is the stored value
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: No such secret in this project
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '409':
          description: The capability is revoked, which is terminal
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects/{id}/secrets/{secret_id}/uses:
    get:
      tags:
        - projects
      operationId: listProjectManagedSecretUses
      summary: Recent substitution attempts for one managed secret
      description: A bounded rolling window of what the host did with this reference, newest first — successful substitutions and refusals alike. Records who asked, never the value and never the argument it was going into.
      parameters:
        - $ref: '#/components/parameters/ProjectId'
        - name: secret_id
          in: path
          required: true
          schema:
            type: string
            format: uuid
        - name: limit
          in: query
          required: false
          schema:
            type: integer
            minimum: 1
            maximum: 200
            default: 50
        - name: cursor
          in: query
          required: false
          description: Opaque cursor returned as next_cursor by the previous page.
          schema:
            type: string
      responses:
        '200':
          description: Recent uses within the retained window
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ManagedSecretUseList'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: No such secret in this project
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '409':
          $ref: '#/components/responses/Conflict'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects/{id}/secrets/{secret_id}/reveal-challenges:
    post:
      tags:
        - projects
      operationId: beginProjectManagedSecretReveal
      x-person-action: none
      summary: Begin one authenticated human reveal
      description: Creates a short-lived, single-use challenge for the installed desktop shell. This operation returns no credential bytes. A bearer-authenticated caller without the native shell's challenge-bound proof cannot complete the reveal.
      parameters:
        - $ref: '#/components/parameters/ProjectId'
        - name: secret_id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/BeginManagedSecretRevealRequest'
      responses:
        '201':
          description: Challenge to authenticate and sign
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ManagedSecretRevealChallenge'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: No such secret in this project
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '409':
          description: The capability is revoked or has no readable current value
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '503':
          description: Native authenticated reveal is unavailable for this app session
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects/{id}/secrets/{secret_id}/reveal-challenges/{challenge_id}/complete:
    post:
      tags:
        - projects
      operationId: completeProjectManagedSecretReveal
      x-person-action: record
      summary: Complete one authenticated human reveal
      description: Consumes one fresh challenge and a native-shell signature produced only after operating-system user verification. The challenge is consumed by the first completion attempt. Success is durably audited before the current value is returned. The response body is categorically withheld from every debug capture.
      parameters:
        - $ref: '#/components/parameters/ProjectId'
        - name: secret_id
          in: path
          required: true
          schema:
            type: string
            format: uuid
        - name: challenge_id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CompleteManagedSecretRevealRequest'
      responses:
        '200':
          description: Current value for immediate human presentation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ManagedSecretRevealResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          description: The challenge expired, was already consumed, or its proof was invalid
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: No such secret in this project
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '409':
          description: The capability or current value changed after the challenge began
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects/{id}/promotion:
    post:
      tags:
        - projects
      operationId: promoteProject
      x-person-action: record
      summary: Save a draft into a folder through a restart-safe transition
      parameters:
        - $ref: '#/components/parameters/ProjectId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PromoteProjectRequest'
      responses:
        '201':
          description: Promoted project tracking the folder
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Project'
        '202':
          description: Promote queued until the project finishes its current turn
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Project'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          description: The chosen folder is not empty (FOLDER_NOT_EMPTY)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
    delete:
      tags:
        - projects
      operationId: cancelProjectPromotion
      x-person-action: record
      summary: Cancel an uncommitted draft save and restore the chosen empty folder
      parameters:
        - $ref: '#/components/parameters/ProjectId'
      responses:
        '204':
          description: Draft project promotion canceled
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          description: Installed content changed or the promotion already committed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects/{id}/roots:
    post:
      tags:
        - projects
      operationId: attachProjectRoot
      x-person-action: record
      summary: Attach a folder root to a project
      parameters:
        - $ref: '#/components/parameters/ProjectId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AttachProjectRootRequest'
      responses:
        '201':
          description: Root attached
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Project'
        '400':
          description: |
            Root path refused as a write jail (write_root_is_filesystem_root, write_root_is_home, write_root_not_absolute, write_root_under_secret_store) or otherwise invalid.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          description: Duplicate path on this project
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects/{id}/roots/{root_id}:
    delete:
      tags:
        - projects
      operationId: detachProjectRoot
      x-person-action: record
      summary: Detach a folder root from a project
      parameters:
        - $ref: '#/components/parameters/ProjectId'
        - $ref: '#/components/parameters/RootId'
        - name: force
          in: query
          required: false
          schema:
            type: boolean
            default: false
          description: When true, cancel in-flight dependents then detach; otherwise bound work refuses the detach with root_busy.
      responses:
        '204':
          description: Root detached
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          description: Folder has in-flight dependents (root_busy)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
    patch:
      tags:
        - projects
      operationId: updateProjectRoot
      x-person-action: record
      summary: Update a folder root (set primary or label)
      parameters:
        - $ref: '#/components/parameters/ProjectId'
        - $ref: '#/components/parameters/RootId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateProjectRootRequest'
      responses:
        '200':
          description: Root updated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Project'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects/{id}/source/operations:
    get:
      tags:
        - projects
      operationId: listSourceOperations
      summary: List active and recent file operations
      description: Returns operations owned by the initiating person, with active operations first and recent terminal results next.
      parameters:
        - $ref: '#/components/parameters/ProjectId'
        - name: cursor
          in: query
          required: false
          schema:
            type: string
          description: Keyset pagination cursor from previous next_cursor
        - name: limit
          in: query
          required: false
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 50
      responses:
        '200':
          description: Active and recent operations
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SourceOperationList'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects/{id}/source/operations/{operation_id}/cancel:
    post:
      tags:
        - projects
      operationId: cancelSourceOperation
      x-person-action: record
      summary: Cancel a file operation
      parameters:
        - $ref: '#/components/parameters/ProjectId'
        - name: operation_id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '204':
          description: Cancellation requested before filesystem publication
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects/{id}/source/operations/{operation_id}/retry:
    post:
      tags:
        - projects
      operationId: retrySourceOperation
      x-person-action: record
      summary: Retry a file operation
      parameters:
        - $ref: '#/components/parameters/ProjectId'
        - name: operation_id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '202':
          description: Durable retry accepted
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SourceOperationStatus'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '422':
          $ref: '#/components/responses/UnprocessableContent'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects/{id}/source/operations/{operation_id}:
    get:
      tags:
        - projects
      operationId: getSourceOperation
      summary: Read file operation progress and completion
      description: Available only to the initiating person who can still invoke the original operation. Receipts survive restart. An interrupted operation is retried only by an explicit retry request after root authority is revalidated.
      parameters:
        - $ref: '#/components/parameters/ProjectId'
        - name: operation_id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Current operation state
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SourceOperationStatus'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects/{id}/source:
    get:
      tags:
        - projects
      operationId: getProjectSource
      summary: Read a project file for the in-app source viewer
      description: |
        Returns UTF-8 file content under the same path jail as agent tools
        (evidence.NormalizeCitationPath + sandbox symlink escape rejection).
        Editable ceiling is 4 MiB. Over-limit and binary (including image)
        reads return metadata with empty content — never a truncated prefix.
        Image bytes load from GET …/source/raw. UTF-16 text with a byte-order
        mark is decoded for editing and restored to the matching on-disk
        encoding on save. BOM-less UTF-16 is reported as binary metadata by
        default; a human may explicitly reopen it with decode_as=utf-16le or
        decode_as=utf-16be. Unsupported codepage text is refused with 415
        unsupported_encoding (never source_binary).
        Successful text reads carry encoding and writable.

        Pass worker_id to read a write worker's file as the worker left it. A write
        worker mutates an isolated overlay copy, not the project roots, so until
        promote lands the change the path exists only on that overlay. With
        worker_id an available overlay is the exclusive source, including when
        a file is absent there; it never falls through to a primary copy.
        Without an available overlay, only the project roots are searched. A worker_id that is unknown or belongs to another project
        contributes no overlay — the read falls back to the project roots.

        Pass root_id to pin the read to one attached root. Omitted, every root
        is searched primary-first — ambiguous when two roots hold the same
        relative path, so root-addressed surfaces (the Files tree) always pin.
      parameters:
        - $ref: '#/components/parameters/ProjectId'
        - $ref: '#/components/parameters/SourceSessionIdQuery'
        - name: path
          in: query
          required: true
          description: Repo-relative path (slash-separated)
          schema:
            type: string
            minLength: 1
        - name: root_id
          in: query
          required: false
          description: Attached root to pin the read to; omitted searches every root primary-first
          schema:
            type: string
            format: uuid
        - name: worker_id
          in: query
          required: false
          description: |
            Worker job whose live overlay contains this path. Reads stay within
            that overlay while it is available, including absent files.
          schema:
            type: string
            format: uuid
        - name: include_deleted
          in: query
          description: Open retained deletion history when the exact root-addressed path is absent. Existing files always take precedence. Requires root_id; does not search history for other paths.
          schema:
            type: boolean
            default: false
        - name: decode_as
          in: query
          required: false
          description: |
            Explicit human choice to reopen an otherwise unsupported file as
            BOM-less UTF-16. Only utf-16le and utf-16be are accepted; supported
            files must be opened without this parameter.
          schema:
            type: string
            enum:
              - utf-16le
              - utf-16be
      responses:
        '200':
          description: Editable text content, or metadata for over-limit / binary / image
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProjectSourceReadResponse'
        '400':
          description: Empty or invalid path, or an invalid query parameter
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          description: Path denied by sandbox / outside project roots
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: Project or file not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '409':
          description: Physical workspace changed while the file was being read
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          description: |
            Unsupported codepage text (unsupported_encoding). details.detected is
            unknown. Distinct from binary metadata on 200.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        default:
          $ref: '#/components/responses/Error'
    post:
      tags:
        - projects
      operationId: createProjectSourceEntry
      x-person-action: record
      summary: Create a new, empty project file or folder from the Files tree
      description: |
        Creates a file or folder that does not exist yet, under the same path
        jail as the source read; kind picks which. Missing parent folders are
        created along the way, so one call handles a nested path. The target
        must be a new path — an existing file or folder is refused with 409
        source_already_exists rather than overwritten or adopted; use PUT to
        change a file that already exists. A new file starts empty and is then
        read like any other file, so its first content arrives through PUT.
        Creation always lands on the project roots — worker overlays are
        read-only.
      parameters:
        - $ref: '#/components/parameters/ProjectId'
        - $ref: '#/components/parameters/SourceSessionIdQuery'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateProjectSourceEntryRequest'
      responses:
        '201':
          description: Entry created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProjectSourceEntryCreatedResponse'
        '202':
          description: Accepted durable file operation. Read status using operation_id; disconnecting does not cancel it.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SourceOperationStatus'
        '400':
          description: Missing path, or kind is not file/folder
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          description: Path denied by sandbox / outside project roots
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: Project or root not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '409':
          description: A file or folder already exists at that path
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '422':
          $ref: '#/components/responses/UnprocessableContent'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
    put:
      tags:
        - projects
      operationId: replaceProjectSource
      x-person-action: record
      summary: Save an in-app editor change to a project file
      description: |
        Overwrites an existing project file with full replacement content,
        under the same path jail as the source read. Only files the viewer
        could fully load are writable: the target must already exist, be a
        regular non-binary file, and be at most 4 MiB before and after the
        save. encoding must exactly echo the representation named by the read,
        and base_sha256 must echo the sha256 the read (or previous save)
        reported for this file; when the on-disk file no longer hashes to
        that value the save is refused with 409 source_write_conflict so an
        edit can never silently clobber a concurrent change. Saves always
        land on the project roots — worker overlays are read-only.
      parameters:
        - $ref: '#/components/parameters/ProjectId'
        - $ref: '#/components/parameters/SourceSessionIdQuery'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PutProjectSourceRequest'
      responses:
        '200':
          description: Save completed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProjectSourceWriteResponse'
        '400':
          description: Missing path / encoding / base_sha256, unsupported encoding, or content over the save cap
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          description: Path denied by sandbox / outside project roots
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: Project or file not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '409':
          description: File changed on disk since it was loaded
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '413':
          description: Content exceeds the editor cap (source_content_too_large)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '415':
          description: Unsupported binary content
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '422':
          $ref: '#/components/responses/UnprocessableContent'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
    delete:
      tags:
        - projects
      operationId: deleteProjectSource
      x-person-action: record
      summary: Move a project file or folder to Trash
      description: |
        Moves one file or folder into the macOS Trash under the same path jail
        as the source read. The entry is never permanently deleted — if the
        Trash move fails the path is left untouched and the call returns 500
        source_trash_failed. Non-empty folders require recursive=true; without
        it the call returns 409 source_not_empty. Engine and VCS metadata
        paths (.git, the project overlay directory) and the root itself are refused.
      parameters:
        - $ref: '#/components/parameters/ProjectId'
        - $ref: '#/components/parameters/SourceSessionIdQuery'
        - name: path
          in: query
          required: true
          description: Root-relative path to move to Trash
          schema:
            type: string
            minLength: 1
        - name: operation_id
          in: query
          required: true
          description: Stable mutation identity; exact retries return the original result and conflicting reuse returns 409 idempotency_conflict.
          schema:
            type: string
            format: uuid
        - name: root_id
          in: query
          required: false
          description: Attached root to pin; omitted uses the primary root
          schema:
            type: string
            format: uuid
        - name: recursive
          in: query
          required: false
          description: Required true when deleting a non-empty folder
          schema:
            type: boolean
      responses:
        '204':
          description: Entry moved to Trash
        '400':
          description: Missing path, or path is the root itself
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          description: Path denied by sandbox / outside project roots
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: Project or path not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '409':
          description: Directory is not empty and recursive was not set
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '422':
          $ref: '#/components/responses/UnprocessableContent'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          description: Trash move failed; source path is untouched
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects/{id}/source/workspace:
    get:
      tags:
        - projects
      operationId: getSourceWorkspace
      summary: Resolve the physical source workspace for a project and chat
      description: |
        Identity and physical roots of the tree the Files surfaces enumerate, derived from the project's session-resolved root set. The root map is authoritative for both file reads and human path actions such as Copy path, Reveal, and Open in editor. The identity addresses a tree for listings, events, and watcher coverage; it is not a history identity, and nothing durable is keyed by it. Editor documents follow the logical file and need no workspace.
      parameters:
        - $ref: '#/components/parameters/ProjectId'
        - $ref: '#/components/parameters/SourceSessionIdQuery'
      responses:
        '200':
          description: Physical workspace identity and resolved roots
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SourceWorkspace'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          description: Project or requested session not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '409':
          $ref: '#/components/responses/Conflict'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects/{id}/agent-presence:
    get:
      tags:
        - projects
      operationId: getAgentPresence
      summary: Agent sessions in the project's files
      description: 'What each chat in the project, with its workers, is reading, is about to change, and has drafted. The projection is ephemeral: it lives in host memory, is bounded per chat, and is empty after a host restart. Live updates arrive on the project-scoped `agent_presence` SSE topic, one complete chat value per event.'
      parameters:
        - $ref: '#/components/parameters/ProjectId'
      responses:
        '200':
          description: Current agent presence
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AgentPresenceSnapshot'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          description: Project not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects/{id}/editor-documents:
    post:
      tags:
        - projects
      operationId: openEditorDocument
      x-person-action: none
      summary: Open or resume a workspace-pinned editor document
      description: Opens writable text as a collaborative document, preserving any unsaved draft. Read-only files return source_read_only and remain available through the source reader.
      parameters:
        - $ref: '#/components/parameters/ProjectId'
        - $ref: '#/components/parameters/SourceSessionIdQuery'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/OpenEditorDocumentRequest'
      responses:
        '200':
          description: Reused existing editor document
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EditorDocument'
        '201':
          description: Canonical editor document
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EditorDocument'
        '400':
          description: Invalid document identity or request body
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          description: Physical workspace changed while the document was opening
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '413':
          description: Content exceeds the editor cap (source_content_too_large)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects/{id}/secret-ignores:
    get:
      tags:
        - projects
      operationId: listProjectSecretIgnores
      summary: Read the project's ignored public values
      parameters:
        - $ref: '#/components/parameters/ProjectId'
        - name: root_id
          in: query
          required: false
          description: Attached folder whose ignore file holds the declarations.
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: File declarations and their current status
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SecretIgnoreList'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/Error'
        '409':
          $ref: '#/components/responses/Conflict'
        '500':
          $ref: '#/components/responses/InternalError'
    post:
      tags:
        - projects
      operationId: createProjectSecretIgnore
      x-person-action: record
      summary: Declare one exact public value in the project's ignore file
      description: Writes plaintext following an explicit human review. Does not release any held request. The entry id makes identical retries idempotent.
      parameters:
        - $ref: '#/components/parameters/ProjectId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AddSecretIgnoreRequest'
      responses:
        '201':
          description: Current declarations
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SecretIgnoreList'
        '400':
          $ref: '#/components/responses/Error'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/Error'
        '409':
          $ref: '#/components/responses/Error'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '500':
          $ref: '#/components/responses/InternalError'
  /v1/projects/{id}/secret-ignores/{entry_id}:
    delete:
      tags:
        - projects
      operationId: deleteProjectSecretIgnore
      x-person-action: record
      summary: Withdraw one declared public value from the project's ignore file
      description: Removes the entry the listing published, preserving the file's other section and its comments. Withdrawing an exception only narrows what the project ignores, so it does not require the scanning trust surface. Covered findings return to normal screening without a rescan.
      parameters:
        - $ref: '#/components/parameters/ProjectId'
        - name: entry_id
          in: path
          required: true
          description: The entry id this project's listing published.
          schema:
            type: string
            pattern: ^[A-Za-z0-9_.:-]+$
        - name: root_id
          in: query
          required: true
          description: The attached folder whose ignore file holds the entry.
          schema:
            type: string
            format: uuid
      responses:
        '204':
          description: Current declarations removed
        '400':
          $ref: '#/components/responses/Error'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/Error'
        '409':
          $ref: '#/components/responses/Error'
        '500':
          $ref: '#/components/responses/InternalError'
  /v1/projects/{id}/secret-ignore-candidates/{candidate_id}:
    get:
      tags:
        - projects
      operationId: getSecretIgnoreCandidate
      summary: Review a live unprotected secret detection
      description: Only available during the originating approval and never for protected values. Plaintext is returned only for the explicit human review and is not persisted in the checkpoint.
      parameters:
        - $ref: '#/components/parameters/ProjectId'
        - name: candidate_id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Exact bytes for review
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SecretIgnoreCandidate'
        '400':
          $ref: '#/components/responses/Error'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/Error'
        '409':
          $ref: '#/components/responses/Conflict'
        '500':
          $ref: '#/components/responses/InternalError'
  /v1/projects/{id}/editor-documents/retention:
    put:
      tags:
        - projects
      operationId: replaceEditorDocumentRetention
      x-person-action: none
      summary: Replace this person's open tab references for a window
      parameters:
        - $ref: '#/components/parameters/ProjectId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ReplaceEditorDocumentRetentionRequest'
      responses:
        '204':
          description: The window's references were replaced atomically
        '400':
          $ref: '#/components/responses/Error'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects/{id}/editor-documents/status:
    post:
      tags:
        - projects
      operationId: readEditorDocumentStatuses
      x-person-action: none
      summary: Read bounded document metadata without loading text or joining replicas
      parameters:
        - $ref: '#/components/parameters/ProjectId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/EditorDocumentStatusesRequest'
      responses:
        '200':
          description: Known document metadata and unavailable identities within the project
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EditorDocumentStatuses'
        '400':
          $ref: '#/components/responses/Error'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects/{id}/editor-documents/{document_id}:
    put:
      tags:
        - projects
      operationId: replaceEditorDocument
      x-person-action: none
      summary: Persist a draft using a compare-and-swap revision
      description: Replaces the host draft without writing the workspace file. The draft must be encodable within the 4 MiB source-file cap; invalid or oversize content is rejected without changing the stored revision.
      parameters:
        - $ref: '#/components/parameters/ProjectId'
        - name: document_id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ReplaceEditorDocumentRequest'
      responses:
        '200':
          description: Updated document
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EditorDocument'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          description: Stale revision, workspace mismatch, or reused operation identity
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '413':
          description: Content exceeds the editor cap (source_content_too_large)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects/{id}/editor-documents/{document_id}/changes:
    get:
      tags:
        - projects
      operationId: listEditorDocumentChanges
      summary: List semantic collaborative edits and their undo state
      parameters:
        - $ref: '#/components/parameters/ProjectId'
        - name: document_id
          in: path
          required: true
          schema:
            type: string
            format: uuid
        - name: cursor
          in: query
          required: false
          schema:
            type: string
        - name: limit
          in: query
          required: false
          schema:
            type: integer
            minimum: 1
            maximum: 500
            default: 50
      responses:
        '200':
          description: Semantic document changes
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EditorDocumentChanges'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: Document not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '409':
          $ref: '#/components/responses/Conflict'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects/{id}/editor-documents/{document_id}/changes/{change_id}/revert:
    post:
      tags:
        - projects
      operationId: revertEditorDocumentChange
      x-person-action: record
      summary: Selectively undo one semantic edit while preserving later contributions
      parameters:
        - $ref: '#/components/parameters/ProjectId'
        - name: document_id
          in: path
          required: true
          schema:
            type: string
            format: uuid
        - name: change_id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/RevertEditorDocumentChangeRequest'
      responses:
        '200':
          description: Accepted draft after selective undo
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EditorDocument'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          description: Edit cannot be undone in the current document state
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects/{id}/editor-documents/{document_id}/snapshots:
    post:
      tags:
        - projects
      operationId: createEditorDocumentSnapshot
      x-person-action: none
      summary: Pin an immutable accepted snapshot for a read or save
      parameters:
        - $ref: '#/components/parameters/ProjectId'
        - name: document_id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PinEditorDocumentRequest'
      responses:
        '201':
          description: Accepted document and synchronization state
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EditorDocument'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          description: Replica requires synchronization or retained draft recovery
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects/{id}/editor-documents/{document_id}/resolve:
    post:
      tags:
        - projects
      operationId: resolveEditorDocumentConflict
      x-person-action: record
      summary: Apply and publish a reviewed merge against exact document and disk versions
      parameters:
        - $ref: '#/components/parameters/ProjectId'
        - $ref: '#/components/parameters/SourceSessionIdQuery'
        - name: document_id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ResolveEditorDocumentRequest'
      responses:
        '200':
          description: Accepted document and synchronization state
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EditorDocument'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          description: Replica requires synchronization or retained draft recovery
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects/{id}/editor-documents/{document_id}/sync:
    post:
      tags:
        - projects
      operationId: syncEditorDocument
      x-person-action: none
      summary: Synchronize a collaborative replica and join presence
      parameters:
        - $ref: '#/components/parameters/ProjectId'
        - name: document_id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SyncEditorDocumentRequest'
      responses:
        '200':
          description: Accepted document and synchronization state
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EditorReplicaFrame'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          description: Replica requires synchronization or retained draft recovery
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '413':
          description: Content exceeds the editor cap (source_content_too_large)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects/{id}/editor-documents/{document_id}/updates:
    post:
      tags:
        - projects
      operationId: submitEditorDocumentUpdate
      x-person-action: none
      summary: Durably accept an idempotent collaborative update
      parameters:
        - $ref: '#/components/parameters/ProjectId'
        - name: document_id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SubmitEditorDocumentUpdateRequest'
      responses:
        '200':
          description: Accepted document and synchronization state
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EditorReplicaFrame'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          description: Replica requires synchronization or retained draft recovery
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '413':
          description: Content exceeds the editor cap (source_content_too_large)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects/{id}/editor-documents/{document_id}/leave:
    post:
      tags:
        - projects
      operationId: leaveEditorDocument
      x-person-action: none
      summary: Leave document presence
      parameters:
        - $ref: '#/components/parameters/ProjectId'
        - name: document_id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/LeaveEditorDocumentRequest'
      responses:
        '204':
          description: Presence updated
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          description: Replica requires synchronization or retained draft recovery
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects/{id}/editor-documents/{document_id}/presence:
    post:
      tags:
        - projects
      operationId: publishEditorDocumentPresence
      x-person-action: none
      summary: Publish ephemeral relative cursor positions
      parameters:
        - $ref: '#/components/parameters/ProjectId'
        - name: document_id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateEditorDocumentPresenceRequest'
      responses:
        '204':
          description: Presence updated
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          description: Replica requires synchronization or retained draft recovery
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects/{id}/editor-documents/{document_id}/discard:
    post:
      tags:
        - projects
      operationId: discardEditorDocument
      x-person-action: record
      summary: Atomically discard the draft back to its durable base
      parameters:
        - $ref: '#/components/parameters/ProjectId'
        - name: document_id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/EditorDocumentCommandRequest'
      responses:
        '200':
          description: Clean document
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EditorDocument'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          description: Stale revision, workspace mismatch, or reused operation identity
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects/{id}/editor-documents/{document_id}/reload:
    post:
      tags:
        - projects
      operationId: reloadEditorDocument
      x-person-action: record
      summary: Replace the draft and base from the pinned workspace file
      parameters:
        - $ref: '#/components/parameters/ProjectId'
        - $ref: '#/components/parameters/SourceSessionIdQuery'
        - name: document_id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/EditorDocumentCommandRequest'
      responses:
        '200':
          description: Reloaded document
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EditorDocument'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          description: Stale revision, workspace mismatch, or reused operation identity
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects/{id}/editor-documents/{document_id}/observe:
    post:
      tags:
        - projects
      operationId: observeEditorDocument
      x-person-action: none
      summary: Compare an editor document's saved base with the workspace file
      description: Reconciles through the same host transition as filesystem observation. Clean documents adopt current editable bytes; dirty documents retain their draft and saved base and update divergence. Unsupported current bytes leave the document diverged so the client can refresh its presentation.
      parameters:
        - $ref: '#/components/parameters/ProjectId'
        - name: document_id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ObserveEditorDocumentRequest'
      responses:
        '200':
          description: Reconciled editor document
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EditorDocument'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          description: Workspace mismatch
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects/{id}/editor-documents/{document_id}/save:
    post:
      tags:
        - projects
      operationId: saveEditorDocument
      x-person-action: record
      summary: Atomically save the workspace-pinned document
      description: The operation id makes a retry resume the same save rather than create another file mutation.
      parameters:
        - $ref: '#/components/parameters/ProjectId'
        - $ref: '#/components/parameters/SourceSessionIdQuery'
        - name: document_id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SaveEditorDocumentRequest'
      responses:
        '200':
          description: Saved document
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EditorDocument'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          description: Stale document revision, workspace mismatch, or external file change
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '413':
          description: Content exceeds the editor cap (source_content_too_large)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects/{id}/editor-documents/{document_id}/secret-spans/preview:
    post:
      tags:
        - projects
      operationId: previewEditorSecretMark
      x-person-action: none
      summary: Report what marking a range in this document would capture
      description: |
        Resolves a selected rune range against the document draft and returns the resolved offsets, a shape receipt, and eligibility. Nothing is stored and no value is returned; the host slices its own copy of the draft so the client never sends the bytes it is asking about.
      parameters:
        - $ref: '#/components/parameters/ProjectId'
        - name: document_id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SecretMarkRange'
      responses:
        '200':
          description: What the range would capture
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SecretMarkPreview'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: No such document in this project
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '409':
          description: Stale document revision
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects/{id}/editor-documents/{document_id}/secret-spans/mark:
    post:
      tags:
        - projects
      operationId: markEditorSecret
      x-person-action: record
      summary: Mint a managed secret from bytes already present in this file
      description: |
        Stores the value in the encrypted credential vault, admits its exact bytes to outbound screening, and returns the value-free capability. The file is not modified: a `{{paintedwolf-secret:…}}` reference is rejected by filesystem tools rather than materialized, so the source file remains the credential source of truth and marking is a zero-edit operation.
      parameters:
        - $ref: '#/components/parameters/ProjectId'
        - name: document_id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SecretMarkRequest'
      responses:
        '200':
          description: The managed capability
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ManagedSecret'
        '400':
          description: The range cannot become a protected value
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: No such document in this project
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '409':
          description: Stale document revision
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects/{id}/attachments:
    post:
      tags:
        - projects
      operationId: uploadAttachment
      x-person-action: record
      summary: Stream one attachment body into project host data
      description: |
        The body is the raw file, not a JSON envelope, so nothing is buffered whole in memory on either side. The host detects the format from the leading bytes, decodes a single-member compression container when it finds one, and materializes the result content-addressed. A video is decoded once in the managed browser, and one whose codec does not play is refused with attachment_undecodable. The returned blob_id is what a prompt references; the client never learns a filesystem path.
      parameters:
        - $ref: '#/components/parameters/ProjectId'
        - name: filename
          in: query
          required: true
          description: Display name for the upload; a hint the host sanitizes, never a path it opens
          schema:
            type: string
        - name: mime
          in: query
          required: false
          description: The file's own media type as the client knows it, a detection hint. The body is always sent as application/octet-stream.
          schema:
            type: string
            maxLength: 255
            pattern: ^[a-z0-9!#$&^_.+-]+/[a-z0-9!#$&^_.+-]+$
      requestBody:
        required: true
        content:
          application/octet-stream:
            schema:
              type: string
              format: binary
      responses:
        '201':
          description: Body materialized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AttachmentUploadResponse'
        '400':
          description: Invalid filename or attachment rejected with unsupported_attachment or attachment_undecodable
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '413':
          description: Attachment exceeded a size cap (attachment_too_large)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '503':
          description: Project attachment storage is unavailable
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects/{id}/source/raw:
    get:
      tags:
        - projects
      operationId: getProjectSourceRaw
      summary: Read a jailed image payload for the in-app image viewer
      description: |
        Streams image bytes under the same path jail as GET /source. Content
        type is sniffed from the first bytes (not the extension). Only the
        locked image MIME set is served; non-images return 415. Payloads over
        8 MiB return 413. SVG is served as image/svg+xml for `<img>` rendering
        only — never as injectable markup.
      parameters:
        - $ref: '#/components/parameters/ProjectId'
        - $ref: '#/components/parameters/SourceSessionIdQuery'
        - name: path
          in: query
          required: true
          description: Repo-relative path (slash-separated)
          schema:
            type: string
            minLength: 1
        - name: root_id
          in: query
          required: false
          description: Attached root to pin the read to
          schema:
            type: string
            format: uuid
        - name: worker_id
          in: query
          required: false
          description: Worker job whose live overlay contains this path
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Image bytes
          content:
            image/png:
              schema:
                type: string
                format: binary
            image/jpeg:
              schema:
                type: string
                format: binary
            image/gif:
              schema:
                type: string
                format: binary
            image/webp:
              schema:
                type: string
                format: binary
            image/svg+xml:
              schema:
                type: string
                format: binary
            image/bmp:
              schema:
                type: string
                format: binary
            image/x-icon:
              schema:
                type: string
                format: binary
            image/avif:
              schema:
                type: string
                format: binary
          headers:
            Content-Length:
              schema:
                type: integer
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          description: Path denied by sandbox
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: Project or file not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '409':
          description: Physical workspace changed after the image metadata read
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '413':
          description: Image exceeds the 8 MiB raw cap
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '415':
          description: Not a sniffed image type
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects/{id}/source/editorconfig:
    get:
      tags:
        - projects
      operationId: getProjectSourceEditorConfig
      summary: Resolve the EditorConfig pairs in effect for a project path
      description: |
        Resolves `.editorconfig` files from the path's directory toward its
        attached root, stopping at `root = true` or at the root itself, with
        nearer files and later sections winning. The host applies the same
        resolution when it checks agent writes. The file itself need not exist.
        Unrecognized keys and values are ignored; `unset` clears a pair from a
        farther file. An absent property is not declared for this path.
      parameters:
        - $ref: '#/components/parameters/ProjectId'
        - $ref: '#/components/parameters/SourceSessionIdQuery'
        - name: path
          in: query
          required: true
          description: Repo-relative path (slash-separated)
          schema:
            type: string
            minLength: 1
        - name: root_id
          in: query
          required: true
          description: Attached root that holds the path
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Pairs in effect for the path
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SourceEditorConfig'
        '400':
          description: Missing or invalid path or root_id
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          description: Path denied by sandbox
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: Project or root not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '409':
          $ref: '#/components/responses/Conflict'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects/{id}/source/editable:
    post:
      tags:
        - projects
      operationId: makeProjectSourceEditable
      x-person-action: record
      summary: Make a read-only project file editable
      description: |
        Adds only the owner-write permission bit to one existing regular file.
        The path is resolved inside the selected project root without symlink
        traversal, and base_sha256 must still match the bytes returned by the
        source read. Repeating the same request is safe. Worker overlays and
        historical versions never call this operation; their toolbar actions
        return to the current project file instead.
      parameters:
        - $ref: '#/components/parameters/ProjectId'
        - $ref: '#/components/parameters/SourceSessionIdQuery'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/MakeProjectSourceEditableRequest'
      responses:
        '200':
          description: File permission updated, or already owner-writable
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MakeProjectSourceEditableResponse'
        '400':
          description: Missing path, root_id, or base_sha256
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          description: Path denied by the project filesystem boundary
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: Project, root, or file not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '409':
          description: File bytes changed since the editor loaded them
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '422':
          description: Host filesystem refused the permission transition
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects/{id}/source/browse:
    get:
      tags:
        - projects
      operationId: browseProjectSource
      summary: List one directory level for the project Files tree
      description: |
        Lists the immediate children of a directory under one attached root so
        the Files tree can expand lazily. Dot-entries and the project overlay
        directory, VCS metadata, and ignored directories are included. Symlinked children are
        classified by their target and dangling links are dropped. Entries come
        back folders first, then case-insensitive by name. The browse follows
        the same root jail as the source read: a dir that resolves outside the
        selected root — including through a symlink — is denied.
      parameters:
        - $ref: '#/components/parameters/ProjectId'
        - $ref: '#/components/parameters/SourceSessionIdQuery'
        - $ref: '#/components/parameters/SourceRootIdQuery'
        - $ref: '#/components/parameters/SourceDirQuery'
      responses:
        '200':
          description: Immediate children of the directory
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SourceDirListing'
        '400':
          description: Project has no attached folder
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          description: Directory resolves outside the selected root
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: Project, root, or directory not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '409':
          description: Physical workspace changed while the directory was being read
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects/{id}/source/history:
    get:
      tags:
        - projects
      operationId: getProjectSourceHistory
      summary: Get reversible project-file lifecycle history
      description: Returns the exact host-owned undo and redo heads. Editor text history is separate.
      parameters:
        - $ref: '#/components/parameters/ProjectId'
      responses:
        '200':
          description: Current lifecycle history heads
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SourceHistoryState'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: Project not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '409':
          $ref: '#/components/responses/Conflict'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects/{id}/source/history/undo:
    post:
      tags:
        - projects
      operationId: undoProjectSourceHistory
      x-person-action: record
      summary: Undo the current project-file lifecycle operation
      description: Applies the exact displayed history head with filesystem fingerprint and destination-vacancy checks.
      parameters:
        - $ref: '#/components/parameters/ProjectId'
        - $ref: '#/components/parameters/SourceSessionIdQuery'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SourceHistoryMutationRequest'
      responses:
        '200':
          description: Canonical inverse transition
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SourceHistoryMutationResponse'
        '202':
          description: Accepted durable file operation. Read status using operation_id; disconnecting does not cancel it.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SourceOperationStatus'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          description: History moved or the filesystem no longer matches the recorded operation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '422':
          $ref: '#/components/responses/UnprocessableContent'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects/{id}/source/history/redo:
    post:
      tags:
        - projects
      operationId: redoProjectSourceHistory
      x-person-action: record
      summary: Redo the current project-file lifecycle operation
      description: Reapplies the exact displayed history head with filesystem fingerprint and destination-vacancy checks.
      parameters:
        - $ref: '#/components/parameters/ProjectId'
        - $ref: '#/components/parameters/SourceSessionIdQuery'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SourceHistoryMutationRequest'
      responses:
        '200':
          description: Canonical reapplied transition
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SourceHistoryMutationResponse'
        '202':
          description: Accepted durable file operation. Read status using operation_id; disconnecting does not cancel it.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SourceOperationStatus'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          description: History moved or the filesystem no longer matches the recorded operation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '422':
          $ref: '#/components/responses/UnprocessableContent'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects/{id}/source/rename:
    post:
      tags:
        - projects
      operationId: renameProjectSource
      x-person-action: record
      summary: Rename or move a project file or folder within one root
      description: |
        Moves one file or folder to a new root-relative path under the same
        attached root. Missing parent folders on the destination are created.
        An occupied destination returns 409 source_already_exists. A path that
        exists only under a different root returns 400 source_cross_root.
        Engine and VCS metadata paths and the root itself are refused.
      parameters:
        - $ref: '#/components/parameters/ProjectId'
        - $ref: '#/components/parameters/SourceSessionIdQuery'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/RenameProjectSourceRequest'
      responses:
        '200':
          description: Canonical destination address
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProjectSourceLifecycleResponse'
        '202':
          description: Accepted durable file operation. Read status using operation_id; disconnecting does not cancel it.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SourceOperationStatus'
        '400':
          description: Missing paths, root itself, or cross-root move
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          description: Path denied by sandbox / outside project roots
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: Project, root, or source path not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '409':
          description: Destination path already exists
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '422':
          $ref: '#/components/responses/UnprocessableContent'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects/{id}/source/copy:
    post:
      tags:
        - projects
      operationId: copyProjectSource
      x-person-action: record
      summary: Copy a project file or folder within one root
      description: |
        Copies one file or folder (recursive) to a new root-relative path under
        the same attached root. Modes are preserved. An occupied destination
        returns 409 source_already_exists. Cross-root copies return 400
        source_cross_root. Engine and VCS metadata paths and the root itself
        are refused.
      parameters:
        - $ref: '#/components/parameters/ProjectId'
        - $ref: '#/components/parameters/SourceSessionIdQuery'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CopyProjectSourceRequest'
      responses:
        '200':
          description: Canonical destination address
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProjectSourceLifecycleResponse'
        '202':
          description: Accepted durable file operation. Read status using operation_id; disconnecting does not cancel it.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SourceOperationStatus'
        '400':
          description: Missing paths, root itself, or cross-root copy
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          description: Path denied by sandbox / outside project roots
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: Project, root, or source path not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '409':
          description: Destination path already exists
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '422':
          $ref: '#/components/responses/UnprocessableContent'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects/{id}/source/index:
    get:
      tags:
        - projects
      operationId: getProjectSourceIndex
      summary: Read the compact revisioned project source index summary
      parameters:
        - $ref: '#/components/parameters/ProjectId'
        - $ref: '#/components/parameters/SourceSessionIdQuery'
      responses:
        '200':
          description: Ready source index summary (possibly refreshing)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SourceIndexSummary'
        '202':
          description: Source index is warming off the request path
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SourceIndexSummary'
        '400':
          description: Project has no attached folder
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: Project not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '409':
          $ref: '#/components/responses/Conflict'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects/{id}/source/search:
    get:
      tags:
        - projects
      operationId: searchProjectSource
      x-multi-stream-list: true
      summary: Search the cached project source index
      parameters:
        - $ref: '#/components/parameters/ProjectId'
        - $ref: '#/components/parameters/SourceSessionIdQuery'
        - name: q
          in: query
          required: false
          schema:
            type: string
          description: 'Any portion of a file''s full path, including the root folder''s own path: a basename, a fuzzy root-relative path, or an absolute, `~/`, `./`, or `file://` path. A code host file link resolves in the root checked out from that repository, and a development server URL by its path. Surrounding quotes, backticks, shell escapes, and a location (`:12:3`, `:10-20`, `#L12`, `#L10-L20`, `(12,3)`) are read, not matched; a pasted compiler, test, or stack trace line contributes only its location. A path that matches nothing is retried without its leading directories.'
        - name: root_id
          in: query
          required: false
          schema:
            type: string
            format: uuid
        - name: limit
          in: query
          required: false
          schema:
            type: integer
            minimum: 1
            maximum: 200
            default: 50
        - name: cursor
          in: query
          required: false
          schema:
            type: string
          description: Pagination cursor from previous page next_cursor
      responses:
        '200':
          description: Ranked results from the newest available index revision
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SourceSearchResponse'
        '202':
          description: Source index is warming off the request path
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SourceSearchResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects/{id}/source/definition:
    post:
      tags:
        - projects
      operationId: resolveProjectSourceDefinition
      x-person-action: none
      summary: Resolve declarations for an identifier
      description: |
        Narrows whole-word search hits in the active root to files that
        declare the exact identifier (AST confirmation). Mentions that are
        not declarations never appear as candidates. One candidate jumps;
        several are ranked for a picker; zero is a successful empty list so
        the client can open prefilled search. Caps set truncated. Languages
        without a grammar return an empty list with 200 — never an error.
      parameters:
        - $ref: '#/components/parameters/ProjectId'
        - $ref: '#/components/parameters/SourceSessionIdQuery'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SourceDefinitionRequest'
      responses:
        '200':
          description: Ranked declaration candidates (may be empty)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SourceDefinitionResponse'
        '400':
          description: Missing path/symbol or no attached folder
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: Project or root not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '409':
          $ref: '#/components/responses/Conflict'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects/{id}/source/symbols:
    get:
      tags:
        - projects
      operationId: listProjectSourceSymbols
      summary: List declarations in one project file
      description: |
        Returns document-order outline symbols (functions, methods, types,
        classes, constants, headings) for one root-relative file. Languages
        without a grammar return an empty list with 200. Capped at 2 000
        symbols with truncated set when the cap is hit.
      parameters:
        - $ref: '#/components/parameters/ProjectId'
        - $ref: '#/components/parameters/SourceSessionIdQuery'
        - name: path
          in: query
          required: true
          description: Root-relative path
          schema:
            type: string
        - name: root_id
          in: query
          required: false
          description: Pin the read to one attached root
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Symbol list (may be empty)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SourceSymbolsResponse'
        '400':
          description: Missing path or no attached folder
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          description: Path outside the project sandbox
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: Project or file not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '409':
          $ref: '#/components/responses/Conflict'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          description: Binary file
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects/{id}/source/walk-summary:
    get:
      tags:
        - projects
      operationId: getProjectSourceWalkSummary
      summary: Read complete Walk chapter counts for visible transcript turns
      parameters:
        - $ref: '#/components/parameters/ProjectId'
        - name: session_id
          in: query
          required: true
          schema:
            type: string
            format: uuid
        - name: message_ids
          in: query
          required: true
          description: Comma-separated opening user message ids, at most 100.
          schema:
            type: string
      responses:
        '200':
          description: Complete grouped step and distinct item counts for requested turns
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SourceWalkSummary'
        '400':
          description: Missing or invalid session_id or message_ids
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects/{id}/source/git-changes/{git_change_id}/review:
    get:
      tags:
        - projects
      operationId: getProjectSourceGitReview
      summary: Review the files recorded by an observed Git movement
      description: |
        Reads Git objects using an observed movement owned by this project.
        Paths and counts are limited to the attached root. No source effects
        are synthesized and neither HEAD nor working-tree bytes are compared.
        Commit, amend, merge, cherry-pick, revert, and clone default to the
        destination commit versus its first parent (empty tree for an initial
        commit). Other movements default to their recorded endpoints, or to the
        destination commit when no previous tip was recorded.
        Missing objects are unavailable, never an empty successful review.
      parameters:
        - $ref: '#/components/parameters/ProjectId'
        - $ref: '#/components/parameters/SourceSessionIdQuery'
        - name: git_change_id
          in: path
          required: true
          schema:
            type: string
            format: uuid
        - name: movement
          in: query
          schema:
            type: boolean
          description: True selects the recorded before/after tips; false selects the destination commit and a parent.
        - name: parent
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 1000
            default: 1
          description: One-based parent index, only for commit comparison.
        - name: limit
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 500
            default: 100
        - name: cursor
          in: query
          description: Opaque pagination cursor from next_cursor.
          schema:
            type: string
      responses:
        '200':
          description: Immutable Git review
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SourceGitReview'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '422':
          $ref: '#/components/responses/UnprocessableContent'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects/{id}/source/revisions:
    get:
      tags:
        - projects
      operationId: resolveProjectSourceRevisions
      summary: Resolve a typed commit, branch, or range to Git comparisons
      description: |
        Reads one revision token in every attached root backed by a Git
        repository, or only root_id, and answers the two commits it names in
        each root where it resolves. A commit compares with its first parent
        (the empty tree for a root commit). A local branch that is checked out
        compares its tip with its parent; any other local branch compares its
        merge base with HEAD with HEAD. A..B compares A with B; A...B compares
        their merge base with B; an omitted side is HEAD. A revision that
        resolves nowhere answers 200 with no comparisons. A token that starts
        with a dash or holds whitespace or control characters is rejected
        before Git runs.
      parameters:
        - $ref: '#/components/parameters/ProjectId'
        - $ref: '#/components/parameters/SourceSessionIdQuery'
        - name: spec
          in: query
          required: true
          schema:
            type: string
            minLength: 1
            maxLength: 256
          description: A commit id or prefix, ref, or A..B / A...B range.
        - name: root_id
          in: query
          required: false
          schema:
            type: string
            format: uuid
          description: Resolve in this root only.
      responses:
        '200':
          description: Comparisons the revision names, primary root first
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SourceRevisionsResponse'
        '400':
          description: The spec is missing or cannot be a revision
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects/{id}/source/revisions/review:
    get:
      tags:
        - projects
      operationId: getProjectSourceRevisionReview
      summary: Review the files between two commits of a root's repository
      description: |
        Pages the files that differ between two full commit ids in the named
        root's repository, limited to that root. An empty before compares
        after with the empty tree. Nothing is recorded, and neither HEAD nor
        working-tree bytes are compared. Missing objects are unavailable,
        never an empty successful review.
      parameters:
        - $ref: '#/components/parameters/ProjectId'
        - $ref: '#/components/parameters/SourceSessionIdQuery'
        - name: root_id
          in: query
          required: true
          schema:
            type: string
            format: uuid
        - name: from_revision
          in: query
          required: false
          schema:
            type: string
            pattern: ^([0-9a-f]{40}|[0-9a-f]{64})?$
          description: Full commit id; omitted compares with the empty tree.
        - name: to_revision
          in: query
          required: true
          schema:
            type: string
            pattern: ^([0-9a-f]{40}|[0-9a-f]{64})$
          description: Full commit id.
        - name: limit
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 500
            default: 100
        - name: cursor
          in: query
          description: Opaque pagination cursor from next_cursor.
          schema:
            type: string
      responses:
        '200':
          description: Immutable Git review
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SourceRevisionReview'
        '400':
          description: A root or full commit id is missing or malformed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '422':
          $ref: '#/components/responses/UnprocessableContent'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects/{id}/source/walk:
    get:
      tags:
        - projects
      operationId: listProjectSourceWalk
      x-multi-stream-list: true
      summary: List Walk effects by lens baseline
      description: |
        File-first cross-session groups. baseline is a query parameter only
        (presentation default; turn:session; turn:session,turn; session:id;
        commit; pin:id). `turn:{session_id}` names that chat's current user
        turn; the host resolves the ordinal in the same read, and the
        response's baseline spells it, so later pages and per-file
        comparisons address the turn this page listed.
        A chat baseline (turn or session) reads that chat's workspace:
        session_id may be omitted and, when sent, must name the same chat.
        The presentation baseline lists what is new since the reader last
        looked at each file: every visible primary-tree effect above the file's
        watermark. Files whose latest look is still current are listed by
        GET /source/seen instead.
        Commit mode pages Git status paths, including paths without recorded
        history; other modes page observed effects, newest first. Both page
        with cursor/next_cursor. A cursor continues only the baseline,
        workspace, and filters of the page that issued it; a turn baseline
        continues the turn the response spelled. Commit rows carry current
        comparison metadata and at most 100 recent recorded effects.
      parameters:
        - $ref: '#/components/parameters/ProjectId'
        - $ref: '#/components/parameters/SourceSessionIdQuery'
        - name: baseline
          in: query
          required: false
          schema:
            type: string
            default: presentation
        - name: cursor
          in: query
          required: false
          description: Opaque next_cursor from the preceding page. Omit for the newest page.
          schema:
            type: string
        - name: limit
          in: query
          required: false
          schema:
            type: integer
            minimum: 1
            maximum: 500
            default: 100
        - name: include_outside_changes
          in: query
          required: false
          description: |
            Session baseline only. Widens the page to the outside changes no
            chat owns within the session's span, from its creation until it was
            archived. Other chats' operations stay out. Rejected on any other
            baseline.
          schema:
            type: boolean
            default: false
        - name: mark_user_edits
          in: query
          required: false
          description: |
            False leaves the person's own writes out of the range, so a file only
            they changed is not listed and the rows carry no user effects. The
            next_cursor still pages the unfiltered range. Rejected
            on a commit baseline, because Git records no author.
          schema:
            type: boolean
            default: true
      responses:
        '200':
          description: Walk lens
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SourceWalkResponse'
        '400':
          description: Invalid baseline or pagination
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          description: Project not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '409':
          $ref: '#/components/responses/Conflict'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects/{id}/source/versions:
    get:
      tags:
        - projects
      operationId: listProjectSourceVersions
      x-multi-stream-list: true
      summary: List one file's merged history — retained versions and git lineage
      description: |
        Every retained state of the file, across primary and worker workspaces,
        newest first. It is not filtered by a presentation baseline or Walk
        range, and it is not limited to states a recorded effect produced: a
        tracked baseline and a preserved pre-image are restorable states and
        page here like any other.

        When the file's root is a git checkout the response also carries one
        page of the file's commit lineage, joined server-side: a commit whose
        blob equals a retained version collapses into that version's row, a
        commit that arrived through an observed ref movement names it, and the
        rest belong to the git-history era below the tracked-since boundary.
        The two lanes page independently. next_cursor continues every
        requested lane that holds more; a continuation answers an exhausted
        lane empty, with git_history_state not_requested for the Git lane.
        A cursor continues only the file, lane, and chat workspace that issued
        it. Select one lane for phased rendering or paging.
      parameters:
        - $ref: '#/components/parameters/ProjectId'
        - $ref: '#/components/parameters/SourceSessionIdQuery'
        - name: file_id
          in: query
          required: true
          schema:
            type: string
            format: uuid
        - name: limit
          in: query
          required: false
          schema:
            type: integer
            minimum: 1
            maximum: 500
            default: 100
        - name: cursor
          in: query
          required: false
          description: Opaque next_cursor from the preceding page. Omit for the newest page.
          schema:
            type: string
        - name: lane
          in: query
          required: false
          description: History lane to return.
          schema:
            type: string
            enum:
              - all
              - retained
              - git
            default: all
      responses:
        '200':
          description: Retained file versions
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SourceFileVersionsResponse'
        '400':
          description: Missing target or invalid pagination
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          description: Project not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '409':
          $ref: '#/components/responses/Conflict'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects/{id}/source/versions/{version_id}/restore:
    post:
      tags:
        - projects
      operationId: restoreProjectSourceVersion
      x-person-action: record
      summary: Restore one retained version as the working file
      description: |
        Applies the selected immutable state as a new user-authored primary
        workspace version. Exact retained bytes are used, including binary
        content. The target remains at its current path across historical
        renames. The required base tip provides compare-and-swap protection;
        a changed or unexpectedly present/absent target returns 409. Content
        that was not captured or is unavailable returns 422 without touching
        the working tree.
      parameters:
        - $ref: '#/components/parameters/ProjectId'
        - $ref: '#/components/parameters/SourceSessionIdQuery'
        - name: version_id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SourceVersionRestoreRequest'
      responses:
        '200':
          description: The selected state is now current
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SourceVersionRestoreResponse'
        '400':
          description: Malformed operation id, target, or base tip
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: Project, logical file, or retained version not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '409':
          description: The working file changed since it was loaded
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '422':
          description: Exact bytes for this retained version are unavailable
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects/{id}/source/commits/{commit_sha}/restore:
    post:
      tags:
        - projects
      operationId: restoreProjectSourceCommitState
      x-person-action: record
      summary: Restore the file's bytes as one commit holds them
      description: |
        Applies a listed commit's blob as a new user-authored primary
        workspace version. Bytes come from the repository object store and
        must hash to the request's blob_oid; a drifted or unreachable object
        returns 422 without touching the working tree. The required base tip
        provides compare-and-swap protection, matching version restore. The
        restored bytes equal the commit's, so the merged listing re-labels
        the new row with the commit identity through the same blob join.
      parameters:
        - $ref: '#/components/parameters/ProjectId'
        - $ref: '#/components/parameters/SourceSessionIdQuery'
        - name: commit_sha
          in: path
          required: true
          schema:
            type: string
            pattern: ^[0-9a-f]{7,64}$
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SourceCommitRestoreRequest'
      responses:
        '200':
          description: The commit's bytes are now current
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SourceCommitRestoreResponse'
        '400':
          description: Malformed operation id, target, or base tip
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: Project, logical file, or commit not found; `source_commit_not_found` when no repository serving the root holds the commit
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '409':
          description: The working file changed since it was loaded
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '422':
          description: The commit's bytes are unreachable or do not hash to blob_oid
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          description: Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /v1/projects/{id}/source/file-briefings:
    get:
      tags:
        - projects
      operationId: getFileBriefing
      summary: Get the AI briefing for one exact source presentation
      description: current accepts worker_id; document requires document_id and document_revision; version requires version_id. Fields for other presentation kinds are rejected.
      parameters:
        - $ref: '#/components/parameters/ProjectId'
        - name: root_id
          in: query
          required: true
          schema:
            type: string
            format: uuid
        - name: path
          in: query
          required: true
          schema:
            type: string
        - name: presentation
          in: query
          required: true
          schema:
            type: string
            enum:
              - current
              - document
              - version
        - name: worker_id
          in: query
          schema:
            type: string
            format: uuid
        - name: document_id
          in: query
          schema:
            type: string
            format: uuid
        - name: document_revision
          in: query
          schema:
            type: integer
            minimum: 1
        - name: version_id
          in: query
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: File briefing lifecycle state
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FileBriefingResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '422':
          $ref: '#/components/responses/UnprocessableContent'
        '503':
          description: File briefings are stopping with the engine (file_briefing_unavailable)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        default:
          $ref: '#/components/responses/Error'
    post:
      tags:
        - projects
      operationId: requestFileBriefing
      x-person-action: none
      summary: Request an AI briefing for one exact source presentation
      parameters:
        - $ref: '#/components/parameters/ProjectId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/FileBriefingRequest'
      responses:
        '200':
          description: Reusable lifecycle state already available
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FileBriefingResponse'
        '202':
          description: Immediate structural preview with AI detail in progress
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FileBriefingResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '422':
          $ref: '#/components/responses/UnprocessableContent'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '503':
          description: File briefings are stopping with the engine (file_briefing_unavailable)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects/{id}/source/seen:
    post:
      tags:
        - projects
      operationId: completeProjectSourcePresentation
      x-person-action: none
      summary: Record that the reader looked at a file
      description: |
        Advances the file's watermark through the displayed effect and records
        the range the look covered, so the file moves from new to seen. Any
        primary-tree effect is a valid token; an earlier or repeated look
        changes nothing. Agent effects at or below the token leave the
        new-from-AI count.
      parameters:
        - $ref: '#/components/parameters/ProjectId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SourcePresentationCompletion'
      responses:
        '204':
          description: Presentation watermark advanced
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          description: The effect token does not match the displayed file
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
    get:
      tags:
        - projects
      operationId: listProjectSourceSeen
      summary: List the files the reader has looked at
      description: |
        Files whose latest look is still current, newest look first, each with
        the effects that look covered. A file with anything new since its look
        is listed by the presentation walk instead, never here.
      parameters:
        - $ref: '#/components/parameters/ProjectId'
        - $ref: '#/components/parameters/SourceSessionIdQuery'
        - name: cursor
          in: query
          required: false
          schema:
            type: string
          description: Opaque next_cursor from the preceding page. Omit for the newest look.
        - name: limit
          in: query
          required: false
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 25
        - name: mark_user_edits
          in: query
          required: false
          description: |
            False ignores the person's own writes: one made since a look does not
            make the file new again, and none are listed among a look's effects.
          schema:
            type: boolean
            default: true
      responses:
        '200':
          description: Seen files
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SourceSeenList'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects/{id}/source/seen/{file_id}:
    delete:
      tags:
        - projects
      operationId: withdrawProjectSourcePresentation
      x-person-action: none
      summary: Mark a file unseen
      description: |
        Withdraws the file's latest look: the watermark returns to where that
        look began, so everything it covered counts as new again, and its agent
        effects rejoin the new-from-AI count.
      parameters:
        - $ref: '#/components/parameters/ProjectId'
        - name: file_id
          in: path
          required: true
          schema:
            type: string
            format: uuid
        - name: through_ordinal
          in: query
          required: true
          description: The look being withdrawn, as the Seen list reported it.
          schema:
            type: integer
            format: int64
            minimum: 1
      responses:
        '204':
          description: The look was withdrawn
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          description: The file has no look left to withdraw
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '409':
          description: A newer look replaced the one named by through_ordinal
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects/{id}/source/storage:
    get:
      tags:
        - projects
      operationId: getProjectSourceStorage
      summary: Get retained content usage relevant to a project
      parameters:
        - $ref: '#/components/parameters/ProjectId'
      responses:
        '200':
          description: Retained content usage by storage lane
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SourceStorage'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects/{id}/security:
    get:
      tags:
        - projects
      operationId: getProjectSecurity
      summary: Get the project's scan coverage, baseline, and running full pass
      parameters:
        - name: root_id
          in: query
          required: false
          schema:
            type: string
            format: uuid
          description: Attached folder identity. Unknown or detached identities are rejected.
        - $ref: '#/components/parameters/ProjectId'
      responses:
        '200':
          description: Security overview for the Security stage
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SecurityOverview'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects/{id}/findings/query:
    post:
      tags:
        - projects
      operationId: queryProjectFindings
      x-person-action: none
      summary: Page the project's finding ledger across every selected scanner
      description: What the project has open and what has left, folded across the authoritative set of each selected scanner. Filtering, ordering, and paging happen in the store, so a page is a page of the project's set rather than a slice the client reorders. Drill down to the run that recorded an entry's last event with POST /v1/projects/{id}/scans/{scan_id}/query.
      parameters:
        - name: root_id
          in: query
          required: false
          schema:
            type: string
            format: uuid
          description: Attached folder identity. Unknown or detached identities are rejected.
        - $ref: '#/components/parameters/ProjectId'
      requestBody:
        required: false
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/FindingLedgerQueryRequest'
      responses:
        '200':
          description: One page of the ledger with the project's totals
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FindingLedgerResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects/{id}/findings/ignores:
    get:
      tags:
        - projects
      operationId: listProjectFindingIgnores
      summary: List the project's cross-engine finding ignores
      description: 'Every entry in force, bundled and project, with how many ledger rows each one covers now. Entries that could not be honoured are reported separately: a mistyped decision disables itself, never the file.'
      parameters:
        - name: root_id
          in: query
          required: false
          schema:
            type: string
            format: uuid
          description: Attached folder identity. Unknown or detached identities are rejected.
        - $ref: '#/components/parameters/ProjectId'
      responses:
        '200':
          description: The project's ignore catalog as it now stands
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FindingIgnoreListResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        default:
          $ref: '#/components/responses/Error'
    post:
      tags:
        - projects
      operationId: createProjectFindingIgnore
      x-person-action: record
      summary: Record one ignore decision in the project's ignore file
      description: Appends the entry to the project's committed overlay file, so the decision is the same artifact a person could have written by hand and is reviewable like any other change to the repository. The predicates conjoin; an entry naming none is refused.
      parameters:
        - name: root_id
          in: query
          required: false
          schema:
            type: string
            format: uuid
          description: Attached folder identity. Unknown or detached identities are rejected.
        - $ref: '#/components/parameters/ProjectId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/FindingIgnoreEntry'
      responses:
        '201':
          description: The project's ignore catalog with the new entry in it
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FindingIgnoreListResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects/{id}/findings/ignores/{entry_id}:
    delete:
      tags:
        - projects
      operationId: deleteProjectFindingIgnore
      x-person-action: record
      summary: Withdraw one ignore decision
      description: 'Takes effect on the next read: findings the entry covered return to the open list without a rescan.'
      parameters:
        - name: root_id
          in: query
          required: false
          schema:
            type: string
            format: uuid
          description: Attached folder identity. Unknown or detached identities are rejected.
        - $ref: '#/components/parameters/ProjectId'
        - name: entry_id
          in: path
          required: true
          schema:
            type: string
            pattern: ^[a-zA-Z0-9_-]+$
      responses:
        '204':
          description: The project's ignore catalog without the entry
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects/{id}/findings/export:
    post:
      tags:
        - projects
      operationId: exportProjectFindings
      x-person-action: none
      summary: Export what a ledger query matches as SARIF or OpenVEX
      description: The query runs in the store, so the document contains what the query matched rather than the page the client happened to hold. SARIF carries each project decision as a suppression on the result it covers; OpenVEX carries the advisory judgements as statements. Absences that established nothing are omitted from OpenVEX rather than published as good news.
      parameters:
        - name: root_id
          in: query
          required: false
          schema:
            type: string
            format: uuid
          description: Attached folder identity. Unknown or detached identities are rejected.
        - $ref: '#/components/parameters/ProjectId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/FindingExportRequest'
      responses:
        '200':
          description: The export document
          content:
            application/sarif+json:
              schema:
                type: string
                format: binary
            application/json:
              schema:
                type: string
                format: binary
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects/{id}/source/pins:
    get:
      tags:
        - projects
      operationId: listProjectSourcePins
      summary: List named source pins
      parameters:
        - $ref: '#/components/parameters/ProjectId'
        - name: limit
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 500
            default: 100
        - name: cursor
          in: query
          description: Opaque next_cursor from the preceding page.
          schema:
            type: string
      responses:
        '200':
          description: Source pins
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SourcePinList'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        default:
          $ref: '#/components/responses/Error'
    post:
      tags:
        - projects
      operationId: createProjectSourcePin
      x-person-action: record
      summary: Pin the current source state
      parameters:
        - $ref: '#/components/parameters/ProjectId'
      requestBody:
        required: false
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SourcePinCreate'
      responses:
        '201':
          description: Source pin created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SourcePin'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects/{id}/source/pins/{pin_id}:
    patch:
      tags:
        - projects
      operationId: updateProjectSourcePin
      x-person-action: record
      summary: Rename a source pin
      parameters:
        - $ref: '#/components/parameters/ProjectId'
        - name: pin_id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SourcePinUpdate'
      responses:
        '200':
          description: Source pin updated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SourcePin'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
    delete:
      tags:
        - projects
      operationId: deleteProjectSourcePin
      x-person-action: record
      summary: Delete a source pin
      parameters:
        - $ref: '#/components/parameters/ProjectId'
        - name: pin_id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '204':
          description: Source pin deleted
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects/{id}/source/comparison:
    get:
      tags:
        - projects
      operationId: getProjectSourceComparison
      summary: Load exact endpoints for a Walk effect, file version, or lens range
      description: |
        Effect mode compares the explicit before_version_id and after_version_id
        stored on one Walk effect. Version mode compares the selected immutable
        version with its parent. Neither mode reads the live filesystem or a
        successor row.

        Commit path mode (path + root_id + baseline=commit) compares the
        attached root's HEAD with the live working path without creating source
        history. expected_head, when present, rejects a changed HEAD with 409;
        an empty expected_head names an unborn repository.

        Scope mode (file_id + baseline) compares the oldest effect
        pre-image in that lens with the current tracked head. It exists for the
        tree and summary lenses; Walk itself always uses effect mode. Under the
        presentation baseline the range starts after the acknowledged version.
        Fully reviewed files answer with in_range false.

        Reviewed mode (file_id + reviewed_through_ordinal) compares the immutable
        endpoints covered by that look. A replaced or withdrawn look answers 409.
        Reading reviewed history never acknowledges subsequent edits.

        Git object mode (blob_oid + root_id) reads the endpoints straight from
        the repository object store: after is the named blob, before the
        optional before_blob_oid. It previews a listed commit — both sides are
        verified against the ids they were fetched by.

        Exactly one mode per request. A baseline holding no change for the file
        answers 200 with in_range false, because an empty range is a valid scope answer.
      parameters:
        - name: display_path
          in: query
          description: File name used for syntax when selecting a Git blob.
          schema:
            type: string
        - $ref: '#/components/parameters/ProjectId'
        - $ref: '#/components/parameters/SourceSessionIdQuery'
        - name: file_id
          in: query
          required: false
          schema:
            type: string
            format: uuid
          description: Scope mode. Stable logical-file identity; paths are not identities.
        - name: path
          in: query
          required: false
          schema:
            type: string
          description: Commit path mode. Root-relative path, including a deleted path.
        - name: expected_head
          in: query
          required: false
          schema:
            type: string
          description: Commit path mode. HEAD observed by the listing, or empty for an unborn repository.
        - name: baseline
          in: query
          required: false
          schema:
            type: string
            default: presentation
          description: Recorded scope mode, or commit path mode. Commit comparisons require root_id and path.
        - name: presentation_after_ordinal
          in: query
          required: false
          schema:
            type: integer
            format: int64
            minimum: 0
          description: Presentation scope only. Retains an open view's returned unread boundary even if another view acknowledges changes.
        - name: reviewed_through_ordinal
          in: query
          required: false
          schema:
            type: integer
            format: int64
            minimum: 1
          description: Reviewed mode. The Seen row's look identity; requires file_id and excludes baseline and mark_user_edits.
        - name: mark_user_edits
          in: query
          required: false
          schema:
            type: boolean
            default: true
          description: |
            Scope mode only. False leaves the person's authored ranges unmarked.
            The before and after endpoints remain actual saved versions.
            Chat and turn scopes select their authors; other chats remain labeled context.
        - name: effect_id
          in: query
          required: false
          schema:
            type: string
            format: uuid
          description: Walk mode. One exact persisted effect.
        - name: version_id
          in: query
          required: false
          schema:
            type: string
            format: uuid
          description: Dropdown mode. One immutable version compared with its parent.
        - name: blob_oid
          in: query
          required: false
          schema:
            type: string
            pattern: ^[0-9a-f]{7,64}$
          description: Git object mode. The after side, fetched from the repository store and verified.
        - name: before_blob_oid
          in: query
          required: false
          schema:
            type: string
            pattern: ^[0-9a-f]{7,64}$
          description: Git object mode. Optional before side; omitted reads as absent.
        - name: root_id
          in: query
          required: false
          schema:
            type: string
            format: uuid
          description: Git object mode. Root whose repository holds the objects.
      responses:
        '200':
          description: Exact comparison pair
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SourceComparison'
        '400':
          description: Invalid baseline, or not exactly one addressing mode
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          description: Project, effect, version, or baseline pin not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '409':
          $ref: '#/components/responses/Conflict'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '422':
          $ref: '#/components/responses/UnprocessableContent'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects/{id}/source/attribution:
    get:
      tags:
        - projects
      operationId: getProjectSourceAttribution
      summary: Agent line provenance intervals for one file
      description: |
        Intervals are workspace-scoped. Pass session_id so a chat bound to a
        worktree reads provenance from the tree it is showing; omitted, the
        project's own roots answer.
      parameters:
        - $ref: '#/components/parameters/ProjectId'
        - $ref: '#/components/parameters/SourceSessionIdQuery'
        - name: path
          in: query
          required: true
          schema:
            type: string
        - name: root_id
          in: query
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Attribution intervals and tip head sha
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SourceAttributionResponse'
        '400':
          description: Missing path or root_id
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        default:
          $ref: '#/components/responses/Error'
  /v1/workers/{id}/changes:
    get:
      tags:
        - workers
      operationId: getWorkerChanges
      summary: List overlay-modified paths for a worker job
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Changed overlay paths
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WorkerJobChangesResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          description: Worker job not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects/{id}/blueprints:
    get:
      tags:
        - blueprints
      operationId: listBlueprints
      summary: List blueprint files under the overlay blueprints/ directory for a project
      parameters:
        - $ref: '#/components/parameters/ProjectId'
        - name: path
          in: query
          required: false
          description: |
            Filter to the blueprint at this project-relative path; the answer holds at most one blueprint.
          schema:
            type: string
            minLength: 1
      responses:
        '200':
          description: Blueprint summaries
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BlueprintListResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        default:
          $ref: '#/components/responses/Error'
    post:
      tags:
        - blueprints
      operationId: createBlueprint
      x-person-action: record
      summary: Create a blueprint markdown file under the overlay blueprints/ directory
      parameters:
        - $ref: '#/components/parameters/ProjectId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateBlueprintRequest'
      responses:
        '201':
          description: Blueprint created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Blueprint'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects/{id}/blueprints/{blueprint_id}:
    get:
      tags:
        - blueprints
      operationId: getBlueprint
      summary: Get blueprint by ID
      parameters:
        - $ref: '#/components/parameters/ProjectId'
        - $ref: '#/components/parameters/BlueprintId'
      responses:
        '200':
          description: Blueprint
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Blueprint'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        default:
          $ref: '#/components/responses/Error'
    patch:
      tags:
        - blueprints
      operationId: updateBlueprint
      x-person-action: record
      summary: Update blueprint content and/or display title
      parameters:
        - $ref: '#/components/parameters/ProjectId'
        - $ref: '#/components/parameters/BlueprintId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateBlueprintRequest'
      responses:
        '200':
          description: Updated blueprint
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Blueprint'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
    delete:
      tags:
        - blueprints
      operationId: deleteBlueprint
      x-person-action: record
      summary: Delete a blueprint file under the overlay blueprints/ directory
      parameters:
        - $ref: '#/components/parameters/ProjectId'
        - $ref: '#/components/parameters/BlueprintId'
      responses:
        '204':
          description: Blueprint deleted
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects/{id}/blueprints/{blueprint_id}/approve:
    post:
      tags:
        - blueprints
      operationId: approveBlueprint
      x-person-action: record
      summary: Atomically approve reviewed blueprint bytes for a workflow revision
      parameters:
        - $ref: '#/components/parameters/ProjectId'
        - $ref: '#/components/parameters/BlueprintId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/BlueprintApproveRequest'
      responses:
        '200':
          description: Approved blueprint
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Blueprint'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects/{id}/blueprints/{blueprint_id}/launch:
    post:
      tags:
        - blueprints
      operationId: launchBlueprint
      x-person-action: record
      summary: Launch a compatible workflow seeded from a file copy
      parameters:
        - $ref: '#/components/parameters/ProjectId'
        - $ref: '#/components/parameters/BlueprintId'
      requestBody:
        required: false
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/LaunchBlueprintRequest'
      responses:
        '201':
          description: Session and workflow run started from a seeded blueprint copy
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/LaunchBlueprintResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '422':
          $ref: '#/components/responses/UnprocessableContent'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/delegations:
    post:
      tags:
        - delegations
      operationId: createDelegation
      x-person-action: record
      summary: Create a delegation
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateDelegationRequest'
      responses:
        '201':
          description: Delegation created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Delegation'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/delegations/{id}:
    get:
      tags:
        - delegations
      operationId: getDelegation
      summary: Get delegation by ID
      parameters:
        - $ref: '#/components/parameters/DelegationId'
      responses:
        '200':
          description: Delegation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Delegation'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        default:
          $ref: '#/components/responses/Error'
  /v1/delegations/{id}/legs:
    get:
      tags:
        - delegations
      operationId: listDelegationLegs
      summary: List legs for a delegation
      parameters:
        - $ref: '#/components/parameters/DelegationId'
      responses:
        '200':
          description: Legs
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DelegationLegListResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        default:
          $ref: '#/components/responses/Error'
  /v1/delegations/{id}/dispatch:
    post:
      tags:
        - delegations
      operationId: dispatchDelegationLeg
      x-person-action: record
      summary: Dispatch a delegation leg to a worker
      parameters:
        - $ref: '#/components/parameters/DelegationId'
      requestBody:
        required: false
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DispatchRequest'
      responses:
        '200':
          description: Dispatched leg
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Leg'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/delegations/{id}/abort:
    post:
      tags:
        - delegations
      operationId: abortDelegation
      x-person-action: record
      summary: Abort a delegation
      parameters:
        - $ref: '#/components/parameters/DelegationId'
      requestBody:
        required: false
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AbortDelegationRequest'
      responses:
        '200':
          description: Aborted delegation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Delegation'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/workflows:
    get:
      tags:
        - workflows
      operationId: listWorkflows
      summary: Catalog of bundled and project-overlay workflow manifests
      parameters:
        - name: project_id
          in: query
          required: false
          schema:
            type: string
            format: uuid
          description: When set, merges the overlay `workflows/` directory from the project path over bundled manifests.
        - name: session_id
          in: query
          required: false
          schema:
            type: string
            format: uuid
          description: When set, merges session-scoped manifests from SQLite over bundled and project tiers.
      responses:
        '200':
          description: Workflow catalog
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WorkflowListResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        default:
          $ref: '#/components/responses/Error'
  /v1/sessions/{id}/workflows/compose:
    post:
      tags:
        - workflows
      operationId: composeWorkflow
      x-person-action: record
      summary: Validate and register a session-scoped workflow manifest
      parameters:
        - $ref: '#/components/parameters/SessionId'
        - name: dry_run
          in: query
          required: false
          schema:
            type: boolean
            default: false
          description: When true, validate only without persisting to session_workflows.
      requestBody:
        required: true
        content:
          application/yaml:
            schema:
              type: string
              description: Workflow manifest YAML body
      responses:
        '200':
          description: Composed session workflow dry-run result
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ComposeWorkflowResponse'
        '201':
          description: Composed session workflow
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ComposeWorkflowResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/Error'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '422':
          $ref: '#/components/responses/WorkflowValidationFailed'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/workflow-templates:
    get:
      tags:
        - workflows
      operationId: listWorkflowTemplates
      summary: List bundled workflow compose templates
      responses:
        '200':
          description: Workflow template catalog
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WorkflowTemplateListResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        default:
          $ref: '#/components/responses/Error'
  /v1/sessions/{id}/workflows/compose-from-template:
    post:
      tags:
        - workflows
      operationId: composeWorkflowFromTemplate
      x-person-action: record
      summary: Compose a session workflow from a bundled template
      parameters:
        - $ref: '#/components/parameters/SessionId'
        - name: dry_run
          in: query
          required: false
          schema:
            type: boolean
            default: false
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ComposeFromTemplateRequest'
      responses:
        '200':
          description: Composed session workflow dry-run result
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ComposeWorkflowResponse'
        '201':
          description: Composed session workflow
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ComposeWorkflowResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/Error'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '422':
          $ref: '#/components/responses/WorkflowValidationFailed'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/sessions/{id}/workflows/{workflow_id}/persist:
    post:
      tags:
        - workflows
      operationId: persistWorkflow
      x-person-action: record
      summary: Promote a session workflow to the project overlay workflows/ directory
      parameters:
        - $ref: '#/components/parameters/SessionId'
        - name: workflow_id
          in: path
          required: true
          schema:
            type: string
            pattern: ^[a-z][a-z0-9-]{1,48}$
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PersistWorkflowRequest'
      responses:
        '201':
          description: Workflow persisted to project overlay
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PersistWorkflowResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          description: Persist not confirmed or HITL approval invalid
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          $ref: '#/components/responses/Error'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '422':
          $ref: '#/components/responses/WorkflowValidationFailed'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/sessions/{id}/workflow-runs:
    get:
      tags:
        - workflows
      operationId: listSessionWorkflowRuns
      summary: Workflow run history for a session (newest first)
      parameters:
        - $ref: '#/components/parameters/SessionId'
        - name: limit
          in: query
          required: false
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 20
        - name: status
          in: query
          required: false
          style: form
          explode: true
          schema:
            type: array
            items:
              $ref: '#/components/schemas/WorkflowRunStatus'
          description: WorkflowRunStatus values to filter by.
        - name: cursor
          in: query
          required: false
          schema:
            type: string
          description: Opaque next_cursor from the preceding page.
      responses:
        '200':
          description: Stable workflow-run history page, newest first
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WorkflowRunPage'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        default:
          $ref: '#/components/responses/Error'
    post:
      tags:
        - workflows
      operationId: startWorkflowRun
      x-person-action: record
      summary: Start or replace a workflow run on a session
      parameters:
        - $ref: '#/components/parameters/SessionId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/StartWorkflowRunRequest'
      responses:
        '201':
          description: Workflow run started
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WorkflowRun'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '422':
          $ref: '#/components/responses/UnprocessableContent'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/sessions/{id}/workflow-runs/active:
    get:
      tags:
        - workflows
      operationId: getActiveWorkflowRun
      summary: Get the active workflow run for a session
      parameters:
        - $ref: '#/components/parameters/SessionId'
      responses:
        '200':
          description: Active workflow run
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ActiveWorkflowRunResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/Error'
        default:
          $ref: '#/components/responses/Error'
  /v1/workflow-runs/{id}:
    get:
      tags:
        - workflows
      operationId: getWorkflowRun
      summary: Get workflow run by ID
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Workflow run
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WorkflowRun'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        default:
          $ref: '#/components/responses/Error'
  /v1/workflow-runs/{id}/report:
    get:
      tags:
        - workflows
      operationId: getWorkflowRunReport
      summary: Download the grounded workflow report PDF
      description: |
        Assembles a ReportInput on demand from durable records (grounded closeout
        message, every review phase's verdict evidence, scan findings and their
        rules, the evidence ledger, and visual artifacts) and renders a
        deterministic PDF: the same records produce byte-identical bytes.
        Returns 404 report_not_found unless the workflow enables reports, the
        run is terminal, the host has recorded report delivery, and a grounded
        run-scoped closeout message exists for that run.
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Workflow report PDF
          headers:
            Content-Disposition:
              schema:
                type: string
              description: attachment; filename="…"
          content:
            application/pdf:
              schema:
                type: string
                format: binary
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          description: report_not_found or workflow_run_not_found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        default:
          $ref: '#/components/responses/Error'
  /v1/workflow-runs/{id}/pause:
    post:
      tags:
        - workflows
      operationId: pauseWorkflowRun
      x-person-action: record
      summary: Pause a workflow run
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/WorkflowControlRequest'
      responses:
        '200':
          description: Paused workflow run
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WorkflowRun'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '422':
          $ref: '#/components/responses/UnprocessableContent'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/workflow-runs/{id}/resume:
    post:
      tags:
        - workflows
      operationId: resumeWorkflowRun
      x-person-action: record
      summary: Resume a paused workflow run
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/WorkflowControlRequest'
      responses:
        '200':
          description: Resumed workflow run
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WorkflowRun'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '422':
          $ref: '#/components/responses/UnprocessableContent'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/workflow-runs/{id}/cancel:
    post:
      tags:
        - workflows
      operationId: cancelWorkflowRun
      x-person-action: record
      summary: Cancel a workflow run
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/WorkflowControlRequest'
      responses:
        '200':
          description: Canceled workflow run
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WorkflowRun'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '422':
          $ref: '#/components/responses/UnprocessableContent'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/workflow-runs/{id}/exit:
    post:
      tags:
        - workflows
      operationId: exitWorkflowRun
      x-person-action: record
      summary: Exit a reviewed active workflow run
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/WorkflowControlRequest'
      responses:
        '200':
          description: Workflow run exited
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WorkflowRun'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/Error'
        '409':
          $ref: '#/components/responses/Conflict'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '422':
          $ref: '#/components/responses/UnprocessableContent'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/workflow-runs/{id}/advance:
    post:
      tags:
        - workflows
      operationId: advanceWorkflowRun
      x-person-action: record
      summary: Advance workflow run to the next phase
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AdvanceWorkflowRunRequest'
      responses:
        '200':
          description: Advanced workflow run
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WorkflowRun'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '422':
          $ref: '#/components/responses/UnprocessableContent'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/workflow-runs/{id}/transitions/{transition_id}:
    post:
      tags:
        - workflows
      operationId: fireWorkflowTransition
      x-person-action: record
      summary: Fire a named choice transition on the current phase (human actor)
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
        - name: transition_id
          in: path
          required: true
          schema:
            type: string
            pattern: ^[a-z0-9_-]+$
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/FireWorkflowTransitionRequest'
      responses:
        '200':
          description: Workflow run after choice transition
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WorkflowRun'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '422':
          $ref: '#/components/responses/UnprocessableContent'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/workflow-runs/{id}/decisions/{phase_id}:
    post:
      tags:
        - workflows
      operationId: resolveWorkflowDecision
      x-person-action: record
      summary: Resolve a structured user decision for a workflow phase
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
        - name: phase_id
          in: path
          required: true
          schema:
            type: string
            pattern: ^[a-z0-9_-]+$
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ResolveWorkflowDecisionRequest'
      responses:
        '200':
          description: Workflow run after decision
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WorkflowRun'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '422':
          $ref: '#/components/responses/UnprocessableContent'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
        default:
          $ref: '#/components/responses/Error'
  /v1/workflow-runs/{id}/feedback/{phase_id}:
    post:
      tags:
        - workflows
      operationId: resolveWorkflowFeedback
      x-person-action: record
      summary: Resolve open-ended user feedback for a workflow phase
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
        - name: phase_id
          in: path
          required: true
          schema:
            type: string
            pattern: ^[a-z0-9_-]+$
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ResolveUserFeedbackRequest'
      responses:
        '200':
          description: Workflow run after feedback
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WorkflowRun'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '422':
          $ref: '#/components/responses/UnprocessableContent'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
        default:
          $ref: '#/components/responses/Error'
  /v1/workflow-runs/{id}/feedback/{phase_id}/secret:
    post:
      tags:
        - workflows
      operationId: resolveWorkflowSecret
      x-person-action: record
      summary: Resolve a protected secret response for a workflow ask
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
        - name: phase_id
          in: path
          required: true
          schema:
            type: string
            pattern: ^[a-z0-9_-]+$
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ResolveUserSecretRequest'
      responses:
        '200':
          description: Workflow run after protected storage
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WorkflowRun'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '422':
          $ref: '#/components/responses/UnprocessableContent'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/workers:
    get:
      tags:
        - workers
      operationId: listWorkers
      summary: List worker tasks for a project
      parameters:
        - name: cursor
          in: query
          schema:
            type: string
        - name: limit
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 500
            default: 100
        - $ref: '#/components/parameters/ProjectIDQuery'
        - name: session_id
          in: query
          required: true
          description: Coordinator session whose worker tasks are returned.
          schema:
            type: string
            format: uuid
        - name: status
          in: query
          required: false
          schema:
            $ref: '#/components/schemas/WorkerStatus'
      responses:
        '200':
          description: Worker tasks
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WorkerListResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        default:
          $ref: '#/components/responses/Error'
  /v1/workers/{id}/cancel:
    post:
      tags:
        - workers
      operationId: cancelWorker
      x-person-action: record
      summary: Cancel a worker task
      parameters:
        - $ref: '#/components/parameters/WorkerId'
      responses:
        '202':
          description: Cancel initiated; terminal status delivered via the worker SSE topic
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WorkerTask'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects/{id}/board:
    get:
      tags:
        - board
      operationId: getBoard
      summary: Get pack board snapshot for a project
      description: Returns a slim coordination view by default. Pass detail_level=forensic only for full worker job records (including workspace baselines) — host-debug payload.
      parameters:
        - $ref: '#/components/parameters/ProjectId'
        - name: session_id
          in: query
          required: true
          description: Coordinator session whose worker roster and overlay state appear on the board.
          schema:
            type: string
            format: uuid
        - name: detail_level
          in: query
          required: false
          description: status | compact | full | forensic (full job records in forensic_workers — debug only)
          schema:
            $ref: '#/components/schemas/BoardDetailLevel'
            default: compact
      responses:
        '200':
          description: Board snapshot
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BoardView'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        default:
          $ref: '#/components/responses/Error'
  /v1/search:
    post:
      tags:
        - search
      operationId: search
      x-person-action: none
      summary: Global federated search across all projects
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SearchRequest'
      responses:
        '200':
          description: Ranked typed hits with facets and histogram
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SearchResponse'
        '400':
          description: Invalid DSL query (search_query_invalid)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/search/replace/preview:
    post:
      tags:
        - search
      operationId: previewSearchReplacement
      x-person-action: none
      summary: Preview a replace-across-files operation
      description: |
        Runs the code search match pipeline and returns per-file hunks with
        sha256 guards. Capped at 500 files / 5 000 hunks with truncated set
        when the cap is hit. Binary and oversize files are omitted.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SearchReplacePreviewRequest'
      responses:
        '200':
          description: Replace preview
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SearchReplacePreviewResponse'
        '400':
          description: Invalid query or pattern
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/search/replace/apply:
    post:
      tags:
        - search
      operationId: applySearchReplacement
      x-person-action: record
      summary: Apply a previewed replace-across-files operation
      description: |
        Applies kept hunk indices per file only when the on-disk sha256 still
        matches the preview. Mismatches are skipped with a reason; remaining
        files continue. Never force-applies.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SearchReplaceApplyRequest'
      responses:
        '200':
          description: Per-file apply outcomes
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SearchReplaceApplyResponse'
        '400':
          description: Invalid request or pattern
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '422':
          $ref: '#/components/responses/UnprocessableContent'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/search/export:
    post:
      tags:
        - search
      operationId: exportSearchResults
      x-person-action: none
      summary: Export global search results as JSONL, CSV, or SARIF
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SearchExportRequest'
      responses:
        '200':
          description: Streamed export attachment
          headers:
            X-Export-Truncated:
              schema:
                type: boolean
              description: True when the export hit a result limit or an executor failed
            Content-Disposition:
              schema:
                type: string
          content:
            application/x-ndjson:
              schema:
                type: string
                format: binary
            text/csv:
              schema:
                type: string
                format: binary
            application/sarif+json:
              schema:
                type: string
                format: binary
        '400':
          description: Invalid query, format, or SARIF scope (search_query_invalid, search_export_invalid_format, search_export_sarif_scope)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/events:
    get:
      tags:
        - events
      operationId: subscribeEvents
      summary: SSE EventHub stream for a project or the device
      description: |
        Each SSE `data:` line is JSON matching `docs/schemas/events/envelope.json`.
        Topic payloads are defined in `docs/schemas/events/{topic}.json`.
        Human checkpoints use topic `checkpoint` (`checkpoint.json`, OpenAPI `CheckpointEvent`).
        Session lifecycle (including async prompt failures) uses topic `session` (`session.json`, OpenAPI `SessionEvent`).
        Six topics are device-scoped and are delivered on every open stream regardless of the `project_id` this one subscribed with: `attention`, `cli_open`, `preflight`, `settings`, `providers`, and `model_policy`. Each reports a fact whose audience is the device rather than one project — `attention` carries the whole cross-project `AttentionView` precisely to report the projects the client is *not* watching, and a readiness probe or a settings change still has to reach a window looking somewhere else. The `project` registry lifecycle topic also reaches every stream, with the affected project retained in its scope, so project switchers stay current. Live delivery and replay use the same audience. Every other topic stays filtered to this stream's project. `events.ValidatePublishScope` refuses a project-less key on any other topic, so the set cannot widen by accident.
      parameters:
        - $ref: '#/components/parameters/EventsProjectIDQuery'
        - $ref: '#/components/parameters/EventsAfterQuery'
        - $ref: '#/components/parameters/EventsClientIDQuery'
      responses:
        '200':
          description: Server-sent events stream
          content:
            text/event-stream:
              schema:
                type: string
                description: SSE lines containing EventEnvelope JSON
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        default:
          $ref: '#/components/responses/Error'
  /v1/attention:
    get:
      tags:
        - attention
      operationId: getAttention
      summary: Cross-project attention snapshot
      description: 'Every session, in every project, that is waiting on the human, running, or errored. Unscoped by project: this answers "what needs me right now" for the projects the user is *not* looking at, which the project-scoped SSE stream cannot. Idle sessions are omitted, so the row count is bounded by live work rather than by history. Live updates arrive on the `attention` SSE topic, which is device-scoped and therefore delivered on every open stream regardless of its project filter.'
      responses:
        '200':
          description: Non-idle sessions across all projects, worst class first
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AttentionView'
        '401':
          $ref: '#/components/responses/Unauthorized'
        default:
          $ref: '#/components/responses/Error'
  /v1/cost/summary:
    get:
      tags:
        - cost
      operationId: getCostSummary
      summary: Get cost summary for session or project
      parameters:
        - name: session_id
          in: query
          required: false
          schema:
            type: string
            format: uuid
          description: Session-scoped cost summary (mutually exclusive with project_id)
        - name: project_id
          in: query
          required: false
          schema:
            type: string
            format: uuid
          description: Project-scoped cost summary (mutually exclusive with session_id)
      responses:
        '200':
          description: Cost summary
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CostSummary'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects/{id}/cost-report:
    get:
      tags:
        - cost
      operationId: getProjectCostReport
      summary: Get project cost totals and per-session breakdown
      parameters:
        - $ref: '#/components/parameters/ProjectId'
        - name: limit
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 200
            default: 40
        - name: cursor
          in: query
          schema:
            type: string
        - name: q
          in: query
          schema:
            type: string
            maxLength: 500
          description: Literal case-insensitive chat title substring.
        - name: sort
          in: query
          schema:
            type: string
            enum:
              - cost
              - tokens
              - activity
              - id
            default: cost
          description: Descending order with the session id as tie breaker. Use id for a stable export traversal.
      responses:
        '200':
          description: Project cost report
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProjectCostReport'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects/{id}/scans:
    get:
      tags:
        - scan
      operationId: listCodeScans
      summary: List code scans for a project
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: Project UUID.
        - name: root_id
          in: query
          required: false
          schema:
            type: string
            format: uuid
          description: Attached folder identity. Unknown or detached identities are rejected.
        - name: limit
          in: query
          required: false
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 20
          description: Maximum scans to return (default 20)
        - name: cursor
          in: query
          required: false
          schema:
            type: string
          description: Opaque next_cursor from the preceding page.
        - name: sort
          in: query
          schema:
            type: string
            enum:
              - date
              - engine
              - status
            default: date
        - name: order
          in: query
          schema:
            type: string
            enum:
              - asc
              - desc
            default: desc
      responses:
        '200':
          description: Stable recent-scan history page (summary view)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CodeScanPage'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        default:
          $ref: '#/components/responses/Error'
    post:
      tags:
        - scan
      operationId: startFullScan
      x-person-action: record
      summary: Run a full pass of every admitted file
      description: 'Records a full pass in which each named scanner scans every file of one source generation, in resumable chunks. Members start together: a scanner still finishing earlier work holds the pass until it is free. An unfinished pass that covers the named scanners is joined, and one that has not started takes the new scanners, rather than another pass starting.'
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: Project UUID.
        - name: root_id
          in: query
          required: false
          schema:
            type: string
            format: uuid
          description: Attached folder identity. Unknown or detached identities are rejected.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/FullScanRequest'
      responses:
        '202':
          description: Full pass accepted
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FullScanResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects/{id}/scans/{scan_id}:
    get:
      tags:
        - scan
      operationId: getCodeScan
      summary: Get code scan result
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: Project UUID.
        - name: scan_id
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: Scan UUID.
        - name: view
          in: query
          required: false
          schema:
            type: string
            enum:
              - summary
              - full
            default: summary
          description: summary returns guidance only; full includes the complete normalized finding set
      responses:
        '200':
          description: Code scan
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CodeScan'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects/{id}/scans/{scan_id}/query:
    post:
      tags:
        - scan
      operationId: queryCodeScan
      x-person-action: none
      summary: Query stored scan guidance
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: Project UUID.
        - name: scan_id
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: Scan UUID.
      requestBody:
        required: false
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ScanQueryRequest'
      responses:
        '200':
          description: Filtered scan guidance
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ScanQueryResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects/{id}/scans/{scan_id}/sarif:
    get:
      tags:
        - scan
      operationId: exportCodeScanSARIF
      summary: Export stored scan findings as SARIF 2.1
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: Project UUID.
        - name: scan_id
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: Scan UUID.
      responses:
        '200':
          description: SARIF 2.1 document rebuilt from stored SecurityFinding rows
          content:
            application/sarif+json:
              schema:
                type: string
                format: binary
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          description: Unknown scan id
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '409':
          description: Scan pending, running, or failed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        default:
          $ref: '#/components/responses/Error'
  /v1/scanners:
    get:
      tags:
        - scan
      operationId: listScanners
      summary: List merged scanner catalog with install status
      responses:
        '200':
          description: Scanner catalog entries
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ScannerListResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        default:
          $ref: '#/components/responses/Error'
    post:
      tags:
        - scan
      operationId: createScanner
      x-person-action: record
      summary: Create a user-layer external scanner or add from catalog
      description: Writes {configdir}/scanners.yaml.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateScannerRequest'
      responses:
        '201':
          description: Created scanner summary
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ScannerSummary'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/scanners/catalog:
    get:
      tags:
        - scan
      operationId: listScannerCatalog
      summary: List known third-party scanners we can drive
      description: Bundled catalog of scanner CLIs the host knows how to invoke. No binaries are shipped or installed; each row carries the install command and whether the binary is currently on PATH.
      responses:
        '200':
          description: Known scanner catalog
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ScannerCatalogResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        default:
          $ref: '#/components/responses/Error'
  /v1/scanners/check:
    post:
      tags:
        - scan
      operationId: checkScanners
      x-person-action: none
      summary: Check installed scanner binaries
      description: Runs each external scanner's probe command (usually --version) and reports whether the installed binary actually works on this device.
      responses:
        '200':
          description: One probe row per external scanner
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ScannerCheckResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/scanners/slots/{category}:
    put:
      tags:
        - scan
      operationId: replaceScannerSlot
      x-person-action: record
      summary: Choose the single scanner for one category slot
      description: 'Exactly one scanner is enabled per slot (sast, sca, secret). Selecting is a replacement: the previous occupant is disabled in the same write.'
      parameters:
        - name: category
          in: path
          required: true
          schema:
            $ref: '#/components/schemas/ScanCategory'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SelectScannerSlotRequest'
      responses:
        '200':
          description: Merged scanner catalog after the swap
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ScannerListResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/scanners/{scanner_id}:
    patch:
      tags:
        - scan
      operationId: updateScanner
      x-person-action: record
      summary: Update a user scanner definition
      description: Host stock may be replaced only by a complete external definition. Slot selection is a separate atomic operation.
      parameters:
        - name: scanner_id
          in: path
          required: true
          schema:
            type: string
            pattern: ^[a-zA-Z0-9_-]+$
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateScannerRequest'
      responses:
        '200':
          description: Updated scanner summary
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ScannerSummary'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
    delete:
      tags:
        - scan
      operationId: deleteScanner
      x-person-action: record
      summary: Delete a user-layer scanner overlay row
      description: Host stock remains. 404 when no user row exists.
      parameters:
        - name: scanner_id
          in: path
          required: true
          schema:
            type: string
            pattern: ^[a-zA-Z0-9_-]+$
      responses:
        '204':
          description: Deleted
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects/{id}/scanners:
    get:
      tags:
        - scan
      operationId: listProjectScanners
      summary: List scanners with project-tier enablement
      parameters:
        - $ref: '#/components/parameters/ProjectId'
      responses:
        '200':
          description: Scanner catalog entries with project enablement
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ScannerListResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects/{id}/scanners/{scanner_id}:
    patch:
      tags:
        - scan
      operationId: updateProjectScanner
      x-person-action: record
      summary: Update project enablement overlay for one scanner
      description: Writes scanners.yaml in the project overlay directory with id+enabled only.
      parameters:
        - $ref: '#/components/parameters/ProjectId'
        - name: scanner_id
          in: path
          required: true
          schema:
            type: string
            pattern: ^[a-zA-Z0-9_-]+$
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateProjectScannerRequest'
      responses:
        '200':
          description: Updated scanner summary for the project merge
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ScannerSummary'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/mcp/recipes:
    get:
      tags:
        - mcp
      operationId: listMcpRecipes
      summary: List known MCP provider recipes
      description: |
        Bundled catalog of common MCP providers. Ids are not a wire enum; adding a row to the host YAML is the whole change. Custom (blank URL or command) is the add-dialog escape hatch, not a catalog row.
      parameters:
        - name: project_id
          in: query
          schema:
            type: string
            format: uuid
          description: When set, `added` reflects the project-scoped catalog view.
      responses:
        '200':
          description: Known MCP recipes
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/McpRecipeCatalogResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        default:
          $ref: '#/components/responses/Error'
  /v1/mcp/providers:
    get:
      tags:
        - mcp
      operationId: listMcpProviders
      summary: List configured MCP providers
      parameters:
        - name: project_id
          in: query
          schema:
            type: string
            format: uuid
          description: When set, merge the project overlay `mcp.yaml` overrides over device defaults.
      responses:
        '200':
          description: MCP providers
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/McpProviderListResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        default:
          $ref: '#/components/responses/Error'
    post:
      tags:
        - mcp
      operationId: createMcpProvider
      x-person-action: record
      summary: Create an MCP provider overlay row
      description: |
        Writes a user-layer overlay by default (project overlay when project_id is set). Default enabled is false. Enabling registers the provider's tools; workflow agent tool policy determines which agents receive them.
      parameters:
        - name: project_id
          in: query
          schema:
            type: string
            format: uuid
          description: When set, write the new row to the project overlay (loopback URL only).
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateMcpProviderRequest'
      responses:
        '201':
          description: Created MCP provider
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/McpProvider'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '502':
          $ref: '#/components/responses/BadGateway'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
        default:
          $ref: '#/components/responses/Error'
  /v1/mcp/providers/{provider_id}:
    patch:
      tags:
        - mcp
      operationId: updateMcpProvider
      x-person-action: record
      summary: Patch an MCP provider overlay
      parameters:
        - name: provider_id
          in: path
          required: true
          schema:
            type: string
            pattern: ^[a-zA-Z0-9_.-]+$
        - name: project_id
          in: query
          schema:
            type: string
            format: uuid
          description: When set, write the patch to the project overlay.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateMcpProviderRequest'
      responses:
        '200':
          description: Updated MCP provider
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/McpProvider'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '502':
          $ref: '#/components/responses/BadGateway'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
        default:
          $ref: '#/components/responses/Error'
    delete:
      tags:
        - mcp
      operationId: deleteMcpProvider
      x-person-action: record
      summary: Remove an MCP provider overlay row
      description: |
        Removes the user or project overlay for this id. Distro base rows remain with device defaults after overlay deletion.
      parameters:
        - name: provider_id
          in: path
          required: true
          schema:
            type: string
            pattern: ^[a-zA-Z0-9_.-]+$
        - name: project_id
          in: query
          schema:
            type: string
            format: uuid
          description: When set, clear the project overlay row only.
      responses:
        '204':
          description: Overlay removed
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '502':
          $ref: '#/components/responses/BadGateway'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
        default:
          $ref: '#/components/responses/Error'
  /v1/mcp/providers/{provider_id}/tools:
    get:
      tags:
        - mcp
      operationId: listMcpProviderTools
      summary: List discovered tools for an MCP provider
      parameters:
        - name: provider_id
          in: path
          required: true
          schema:
            type: string
            pattern: ^[a-zA-Z0-9_.-]+$
        - name: project_id
          in: query
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Discovered tools
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/McpToolListResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '502':
          $ref: '#/components/responses/BadGateway'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
        default:
          $ref: '#/components/responses/Error'
  /v1/mcp/providers/{provider_id}/refresh:
    post:
      tags:
        - mcp
      operationId: refreshMcpProvider
      x-person-action: none
      summary: Reconnect and re-register tools for one MCP provider
      parameters:
        - name: provider_id
          in: path
          required: true
          schema:
            type: string
            pattern: ^[a-zA-Z0-9_.-]+$
        - name: project_id
          in: query
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Updated MCP provider after refresh
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/McpProvider'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '502':
          $ref: '#/components/responses/BadGateway'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
        default:
          $ref: '#/components/responses/Error'
  /v1/mcp/providers/{provider_id}/oauth/start:
    post:
      tags:
        - mcp
      operationId: startMcpOAuth
      x-person-action: none
      summary: Begin MCP Authorization (OAuth 2.1 + PKCE)
      parameters:
        - name: provider_id
          in: path
          required: true
          schema:
            type: string
            pattern: ^[a-zA-Z0-9_.-]+$
        - name: project_id
          in: query
          schema:
            type: string
            format: uuid
          description: Optional project scope for OAuth flow
      responses:
        '200':
          description: Authorize URL for system browser
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/McpOAuthStartResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '502':
          $ref: '#/components/responses/BadGateway'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
        default:
          $ref: '#/components/responses/Error'
  /v1/mcp/providers/{provider_id}/oauth/cancel:
    post:
      tags:
        - mcp
      operationId: cancelMcpOAuth
      x-person-action: none
      summary: Cancel a pending MCP sign-in
      parameters:
        - name: provider_id
          in: path
          required: true
          schema:
            type: string
            pattern: ^[a-zA-Z0-9_.-]+$
        - name: project_id
          in: query
          schema:
            type: string
            format: uuid
          description: Optional project scope for OAuth flow
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/McpOAuthCancelRequest'
      responses:
        '204':
          description: Named attempt is no longer pending; stored credentials are unchanged.
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '502':
          $ref: '#/components/responses/BadGateway'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
        default:
          $ref: '#/components/responses/Error'
  /v1/mcp/providers/{provider_id}/oauth/complete:
    post:
      tags:
        - mcp
      operationId: completeMcpOAuth
      x-person-action: record
      summary: Finish MCP Authorization code exchange
      parameters:
        - name: provider_id
          in: path
          required: true
          schema:
            type: string
            pattern: ^[a-zA-Z0-9_.-]+$
        - name: project_id
          in: query
          schema:
            type: string
            format: uuid
          description: Optional project scope for OAuth flow
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/McpOAuthCompleteRequest'
      responses:
        '200':
          description: Updated MCP provider after sign-in
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/McpProvider'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '502':
          $ref: '#/components/responses/BadGateway'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
        default:
          $ref: '#/components/responses/Error'
  /v1/mcp/providers/{provider_id}/oauth:
    delete:
      tags:
        - mcp
      operationId: revokeMcpOAuth
      x-person-action: record
      summary: Revoke MCP OAuth tokens (Sign out)
      parameters:
        - name: provider_id
          in: path
          required: true
          schema:
            type: string
            pattern: ^[a-zA-Z0-9_.-]+$
        - name: project_id
          in: query
          schema:
            type: string
            format: uuid
          description: Optional project scope for OAuth flow
      responses:
        '204':
          description: OAuth tokens revoked
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '502':
          $ref: '#/components/responses/BadGateway'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
        default:
          $ref: '#/components/responses/Error'
  /v1/mcp/providers/check:
    post:
      tags:
        - mcp
      operationId: checkMcpProviders
      x-person-action: none
      summary: Check MCP provider connections
      parameters:
        - name: project_id
          in: query
          schema:
            type: string
            format: uuid
          description: When set, evaluate providers with project overlay enablement.
      responses:
        '200':
          description: Connection check results
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/McpCheckResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/providers:
    get:
      tags:
        - providers
      operationId: listProviders
      summary: List live model-provider instances
      description: |
        Returns AI providers from providers.local.yaml (plus credential status from the encrypted credential vault). Ship providers.yaml kinds are Add-picker templates via GET /v1/provider-kinds — not listed here until the user adds an instance. Host platform filtering may omit unsupported instances.
      responses:
        '200':
          description: Providers
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProviderListResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        default:
          $ref: '#/components/responses/Error'
    post:
      tags:
        - providers
      operationId: createProvider
      x-person-action: record
      summary: Add a provider instance under a chosen id
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateProviderRequest'
      responses:
        '201':
          description: Created provider
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProviderMeta'
        '400':
          $ref: '#/components/responses/Error'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '409':
          $ref: '#/components/responses/Error'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/provider-kinds:
    get:
      tags:
        - providers
      operationId: listProviderKinds
      summary: List ship-catalog provider kinds for the Add AI provider picker
      responses:
        '200':
          description: Provider kind templates
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProviderKindListResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        default:
          $ref: '#/components/responses/Error'
  /v1/providers/{provider_id}:
    patch:
      tags:
        - providers
      operationId: updateProvider
      x-person-action: record
      summary: Update a provider instance
      parameters:
        - name: provider_id
          in: path
          required: true
          schema:
            type: string
            pattern: ^[a-zA-Z0-9_.-]+$
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateProviderRequest'
      responses:
        '200':
          description: Updated provider
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProviderMeta'
        '400':
          $ref: '#/components/responses/Error'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/Error'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
    delete:
      tags:
        - providers
      operationId: deleteProvider
      x-person-action: record
      summary: Delete a provider instance
      parameters:
        - name: provider_id
          in: path
          required: true
          schema:
            type: string
            pattern: ^[a-zA-Z0-9_.-]+$
      responses:
        '204':
          description: Provider deleted
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/providers/{provider_id}/credential:
    put:
      tags:
        - providers
      operationId: replaceProviderCredential
      x-person-action: record
      summary: Store provider API key (write-only)
      parameters:
        - name: provider_id
          in: path
          required: true
          schema:
            type: string
            pattern: ^[a-zA-Z0-9_.-]+$
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SetProviderCredentialRequest'
      responses:
        '200':
          description: Credential stored
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProviderMeta'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
    delete:
      tags:
        - providers
      operationId: deleteProviderCredential
      x-person-action: record
      summary: Remove stored provider API key
      parameters:
        - name: provider_id
          in: path
          required: true
          schema:
            type: string
            pattern: ^[a-zA-Z0-9_.-]+$
      responses:
        '204':
          description: Credential removed
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/providers/{provider_id}/test:
    post:
      tags:
        - providers
      operationId: testProvider
      x-person-action: none
      summary: Test provider credentials via live model discovery and refresh the model list
      description: |
        Probes the provider the same way Refresh does — live model listing with stored credentials — and returns the probe result.
      parameters:
        - name: provider_id
          in: path
          required: true
          schema:
            type: string
            pattern: ^[a-zA-Z0-9_.-]+$
      responses:
        '200':
          description: Test result
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProviderProbeResult'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/providers/{provider_id}/refresh:
    post:
      tags:
        - providers
      operationId: refreshProviderModels
      x-person-action: none
      summary: Drop cached model discovery and return the freshly discovered provider
      parameters:
        - name: provider_id
          in: path
          required: true
          schema:
            type: string
            pattern: ^[a-zA-Z0-9_.-]+$
      responses:
        '200':
          description: Provider with freshly discovered models
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProviderMeta'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '502':
          $ref: '#/components/responses/BadGateway'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
        default:
          $ref: '#/components/responses/Error'
  /v1/settings/web-research:
    get:
      tags:
        - web-research
      operationId: getWebResearchSettings
      summary: Web research mode and enablement preferences
      responses:
        '200':
          description: Web research settings
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WebResearchSettings'
        '401':
          $ref: '#/components/responses/Unauthorized'
        default:
          $ref: '#/components/responses/Error'
    patch:
      tags:
        - web-research
      operationId: updateWebResearchSettings
      x-person-action: record
      summary: Update web research mode and enablement preferences
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateWebResearchSettingsRequest'
      responses:
        '200':
          description: Updated web research settings
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WebResearchSettings'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/web-research/providers:
    get:
      tags:
        - web-research
      operationId: getWebResearchProviders
      summary: Web research direct status and catalog provider rows
      responses:
        '200':
          description: Provider status
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WebResearchProvidersResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        default:
          $ref: '#/components/responses/Error'
  /v1/web-research/index:
    get:
      tags:
        - web-research
      operationId: getWebResearchIndex
      summary: Local web index snapshot and recent background-warming activity
      responses:
        '200':
          description: Index status
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WebResearchIndexStatus'
        '401':
          $ref: '#/components/responses/Unauthorized'
        default:
          $ref: '#/components/responses/Error'
  /v1/web-research/providers/{provider_id}:
    patch:
      tags:
        - web-research
      operationId: updateWebResearchProvider
      x-person-action: record
      summary: Store non-secret provider config (endpoint, cx, …)
      parameters:
        - name: provider_id
          in: path
          required: true
          schema:
            type: string
            pattern: ^[a-zA-Z0-9_.-]+$
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateWebResearchProviderRequest'
      responses:
        '200':
          description: Updated provider meta
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WebResearchProviderMeta'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/web-research/providers/{provider_id}/credential:
    put:
      tags:
        - web-research
      operationId: replaceWebResearchCredential
      x-person-action: record
      summary: Store web research API key (write-only)
      parameters:
        - name: provider_id
          in: path
          required: true
          schema:
            type: string
            pattern: ^[a-zA-Z0-9_.-]+$
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SetProviderCredentialRequest'
      responses:
        '200':
          description: Credential stored
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WebResearchProviderMeta'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
    delete:
      tags:
        - web-research
      operationId: deleteWebResearchCredential
      x-person-action: record
      summary: Remove stored web research API key
      parameters:
        - name: provider_id
          in: path
          required: true
          schema:
            type: string
            pattern: ^[a-zA-Z0-9_.-]+$
      responses:
        '204':
          description: Credential removed
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/web-research/providers/{provider_id}/test:
    post:
      tags:
        - web-research
      operationId: testWebResearchProvider
      x-person-action: none
      summary: Run a canned search against one web research provider
      parameters:
        - name: provider_id
          in: path
          required: true
          schema:
            type: string
            pattern: ^[a-zA-Z0-9_.-]+$
      responses:
        '200':
          description: Provider probe result
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProviderProbeResult'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/settings/model-policy:
    get:
      tags:
        - settings
      operationId: getModelPolicySettings
      summary: Get stored model policy overlay (empty slots inherit parent / Settings)
      parameters:
        - name: project_id
          in: query
          schema:
            type: string
            format: uuid
        - name: effective
          in: query
          description: When true, return the effective merged policy (bundled, then global, then the project_id project's overlay) with every slot resolved, instead of the stored overlay layer. Read-only projection for status surfaces; the editor keeps using the overlay form.
          schema:
            type: boolean
            default: false
      responses:
        '200':
          description: Model policy
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ModelPolicy'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        default:
          $ref: '#/components/responses/Error'
    patch:
      tags:
        - settings
      operationId: updateModelPolicySettings
      x-person-action: record
      summary: Apply model-policy slot assignments
      description: |
        Omitted slots stay stored. A present key, including an empty ref, is the assignment.
      parameters:
        - name: project_id
          in: query
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ModelPolicyPatch'
      responses:
        '200':
          description: Updated model policy
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ModelPolicy'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '503':
          description: Provider discovery failed while validating the model assignment; provider_catalog_unavailable is retryable and no assignment is applied
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        default:
          $ref: '#/components/responses/Error'
  /v1/settings/approvals:
    get:
      tags:
        - settings
      operationId: getApprovalsSettings
      summary: Get effective approval rules for scope
      parameters:
        - name: project_id
          in: query
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Approval config
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApprovalConfigResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        default:
          $ref: '#/components/responses/Error'
    patch:
      tags:
        - settings
      operationId: updateApprovalsSettings
      x-person-action: record
      summary: Update approval rules for global or project scope
      parameters:
        - name: project_id
          in: query
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateApprovalsSettingsRequest'
      responses:
        '200':
          description: Updated approval config
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApprovalConfigResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/approval-grants:
    get:
      tags:
        - settings
      operationId: listApprovalGrants
      summary: List active revocable approval leases
      description: |
        Host-minted chat, project, and device leases — including exact local-service (socket_path) grants. Built for review and revocation; search and sort client-side on title, scope, category, pattern, paths, and granted_at. Unqualified, the list includes chat-scoped leases across every chat, each carrying chat_session_id and session_title for grouping. Pass session_id to narrow chat-scoped rows to one chat.
      parameters:
        - name: session_id
          in: query
          schema:
            type: string
            format: uuid
          description: Narrow chat-scoped leases to this chat.
      responses:
        '200':
          description: Approval grants
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApprovalGrantsResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        default:
          $ref: '#/components/responses/Error'
    post:
      tags:
        - settings
      operationId: createApprovalGrant
      x-person-action: record
      summary: Create one durable grant from Settings
      description: |
        Deliberate Settings-only creation of one project or device grant: an exact local-service (socket_path) grant after host resolution of a single absolute AF_UNIX socket, or a network host family (host) lease over a registrable site, optionally bound to one tunnel port. Execution cards mint their own ladders; models cannot call this method.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateApprovalGrantRequest'
      responses:
        '201':
          description: Grant created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApprovalGrant'
        '400':
          $ref: '#/components/responses/Error'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/approval-grants/resolve-socket:
    post:
      tags:
        - settings
      operationId: resolveSocketGrant
      x-person-action: none
      summary: Resolve one absolute AF_UNIX socket for Settings confirmation
      description: |
        Validates type and resolves approved/target paths without writing a grant. Used by the deliberate durable creation confirmation step.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ResolveSocketGrantRequest'
      responses:
        '200':
          description: Resolved socket pair and authority warning
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ResolveSocketGrantResponse'
        '400':
          $ref: '#/components/responses/Error'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/approval-grants/revoke:
    post:
      tags:
        - settings
      operationId: revokeApprovalGrants
      x-person-action: record
      summary: Revoke approval leases in bulk
      description: |
        One call for the Saved approvals group actions. Each id resolves independently and reports its own outcome; socket grants update the next-process boundary only.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/RevokeApprovalGrantsRequest'
      responses:
        '200':
          description: Per-id revoke results
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RevokeApprovalGrantsResponse'
        '400':
          $ref: '#/components/responses/Error'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/approval-asks:
    get:
      tags:
        - settings
      operationId: listApprovalAsks
      summary: Approval asks by reason over a recent window
      description: |
        A local, on-device view of which reasons have been asking and how they were answered, read from the authorization ledger across every session. Presentation only: nothing here changes whether an ask occurs. A reason a person allowed every time is a reason worth quieting, and the row says where that control is.
      parameters:
        - name: days
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 90
            default: 7
          description: Window length in days, ending now.
        - name: project_id
          in: query
          schema:
            type: string
            format: uuid
          description: Narrow to one project's sessions.
      responses:
        '200':
          description: Recent asks by reason
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApprovalRecentAsksResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        default:
          $ref: '#/components/responses/Error'
  /v1/detection-packs:
    get:
      tags:
        - settings
      operationId: listDetectionPacks
      summary: List detection packs
      description: |
        Reads detection packs from disk for the host or filtered by project lens.
      parameters:
        - name: project_id
          in: query
          required: false
          schema:
            type: string
            format: uuid
          description: |
            When set, apply that project's enable-only detection-packs.yaml overlay to the returned enabled flags. Ignored unless the project_settings trust surface applies to the project.
      responses:
        '200':
          description: Detection packs
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DetectionPackList'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        default:
          $ref: '#/components/responses/Error'
    post:
      tags:
        - settings
      operationId: importDetectionPack
      x-person-action: record
      summary: Import a detection pack from a folder
      description: |
        Validates and optionally copies an allowlisted pack folder into the device detection-packs directory. dry_run previews without writing.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DetectionPackImportRequest'
      responses:
        '201':
          description: Import preview or committed result
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DetectionPackImportResult'
        '400':
          $ref: '#/components/responses/Error'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Error'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/detection-packs/{pack_id}:
    patch:
      tags:
        - settings
      operationId: updateDetectionPack
      x-person-action: record
      summary: Enable or disable a detection pack
      parameters:
        - name: pack_id
          in: path
          required: true
          schema:
            type: string
            pattern: ^[a-z0-9][a-z0-9-]{1,39}$
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateDetectionPackRequest'
      responses:
        '200':
          description: Updated pack
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DetectionPack'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/Error'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
    delete:
      tags:
        - settings
      operationId: deleteDetectionPack
      x-person-action: record
      summary: Remove a device detection pack
      parameters:
        - name: pack_id
          in: path
          required: true
          schema:
            type: string
            pattern: ^[a-z0-9][a-z0-9-]{1,39}$
      responses:
        '204':
          description: Pack removed
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/Error'
        '409':
          $ref: '#/components/responses/Error'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/settings/limits:
    get:
      tags:
        - settings
      operationId: getLimitsSettings
      summary: Get effective session and budget limits for scope
      parameters:
        - name: project_id
          in: query
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Settings limits
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SettingsLimitsResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        default:
          $ref: '#/components/responses/Error'
    patch:
      tags:
        - settings
      operationId: updateLimitsSettings
      x-person-action: record
      summary: Update session and budget limits for global or project scope
      parameters:
        - name: project_id
          in: query
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SettingsLimitsPatch'
      responses:
        '200':
          description: Updated settings limits
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SettingsLimitsResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/settings/review:
    get:
      tags:
        - settings
      operationId: getReviewSettings
      summary: Get effective edit review settings for scope
      parameters:
        - name: project_id
          in: query
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Edit review settings
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ReviewSettingsResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        default:
          $ref: '#/components/responses/Error'
    patch:
      tags:
        - settings
      operationId: updateReviewSettings
      x-person-action: record
      summary: Update edit review settings for global or project scope
      parameters:
        - name: project_id
          in: query
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateReviewSettingsRequest'
      responses:
        '200':
          description: Updated edit review settings
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ReviewSettingsResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/settings/file-summaries:
    get:
      tags:
        - settings
      operationId: getFileSummariesSettings
      summary: Get the device-wide File summaries setting
      responses:
        '200':
          description: File summaries setting
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FileSummariesSettingsResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        default:
          $ref: '#/components/responses/Error'
    patch:
      tags:
        - settings
      operationId: updateFileSummariesSettings
      x-person-action: record
      summary: Enable or fully disable File summaries
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateFileSummariesSettingsRequest'
      responses:
        '200':
          description: Updated File summaries setting
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FileSummariesSettingsResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/settings/power:
    get:
      tags:
        - settings
      operationId: getPowerSettings
      summary: Get the device power preference and live assertion status
      responses:
        '200':
          description: Power setting and live status
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PowerSettingsResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        default:
          $ref: '#/components/responses/Error'
    patch:
      tags:
        - settings
      operationId: updatePowerSettings
      x-person-action: record
      summary: Set whether active sessions prevent macOS idle sleep
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdatePowerSettingsRequest'
      responses:
        '200':
          description: Updated power setting and live status
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PowerSettingsResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/settings/verify:
    get:
      tags:
        - settings
      operationId: getVerifySettings
      summary: Get effective verify (test command) settings for scope
      parameters:
        - name: project_id
          in: query
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Verify settings
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/VerifySettingsResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        default:
          $ref: '#/components/responses/Error'
    patch:
      tags:
        - settings
      operationId: updateVerifySettings
      x-person-action: record
      summary: Update project verify settings
      parameters:
        - name: project_id
          in: query
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateVerifySettingsRequest'
      responses:
        '200':
          description: Updated verify settings
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/VerifySettingsResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/settings/verify/dismiss:
    post:
      tags:
        - settings
      operationId: dismissVerifySettings
      x-person-action: record
      summary: Dismiss (or un-dismiss) the project verify-command suggestion
      parameters:
        - name: project_id
          in: query
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DismissVerifySettingsRequest'
      responses:
        '200':
          description: Updated verify settings
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/VerifySettingsResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/settings/security-scanners:
    get:
      tags:
        - settings
      operationId: getSecurityScannersSettings
      summary: Get global Security scanners settings
      responses:
        '200':
          description: Security scanners settings
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SecurityScannersSettingsResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        default:
          $ref: '#/components/responses/Error'
    patch:
      tags:
        - settings
      operationId: updateSecurityScannersSettings
      x-person-action: record
      summary: Update global Security scanners settings
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateSecurityScannersSettingsRequest'
      responses:
        '200':
          description: Updated Security scanners settings
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SecurityScannersSettingsResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/settings/pricing:
    get:
      tags:
        - settings
      operationId: getPricingSettings
      summary: Get global cost pricing settings
      responses:
        '200':
          description: Cost pricing settings
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SettingsPricingResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        default:
          $ref: '#/components/responses/Error'
    patch:
      tags:
        - settings
      operationId: updatePricingSettings
      x-person-action: record
      summary: Update global cost pricing settings
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SettingsPricing'
      responses:
        '200':
          description: Updated cost pricing settings
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SettingsPricingResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/settings/pricing/sources/{source_id}/refresh:
    post:
      tags:
        - settings
      operationId: refreshPricingSource
      x-person-action: none
      summary: Force refresh one pricing feed source
      parameters:
        - name: source_id
          in: path
          required: true
          schema:
            type: string
            pattern: ^[a-z0-9_-]+$
      responses:
        '200':
          description: Updated source meta
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PricingSourceMeta'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '502':
          $ref: '#/components/responses/BadGateway'
        default:
          $ref: '#/components/responses/Error'
  /v1/settings/project-trust:
    get:
      tags:
        - rules
      operationId: getTrustSettings
      summary: Get device-wide off switches for what projects may supply
      responses:
        '200':
          description: Device trust settings
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TrustSettingsResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        default:
          $ref: '#/components/responses/Error'
    patch:
      tags:
        - rules
      operationId: updateTrustSettings
      x-person-action: record
      summary: Update device-wide off switches for what projects may supply
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateTrustSettingsRequest'
      responses:
        '200':
          description: Updated device trust settings
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TrustSettingsResponse'
        '400':
          $ref: '#/components/responses/Error'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects/{id}/git/repos:
    get:
      tags:
        - git
      operationId: listGitRepos
      summary: Ordered repository set for a project
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: Canonical project UUID.
        - name: session_id
          in: query
          required: false
          schema:
            type: string
            format: uuid
          description: Optional session whose active root selects active_repo_id.
      responses:
        '200':
          description: Repository set with status summaries
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GitReposView'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        default:
          $ref: '#/components/responses/Error'
    post:
      tags:
        - git
      operationId: createGitRepo
      x-person-action: record
      summary: Initialize a new git repository in a named project root
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: Canonical project UUID.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/GitInitRequest'
      responses:
        '201':
          description: Initialized git repository
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GitRepoEntry'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '422':
          $ref: '#/components/responses/UnprocessableContent'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects/{id}/git/repos/{repo_id}/status:
    get:
      tags:
        - git
      operationId: getGitStatus
      summary: Working-tree status for the Den Git tab
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: Canonical project UUID.
        - name: repo_id
          in: path
          required: true
          schema:
            type: string
            pattern: ^[a-zA-Z0-9_-]+$
          description: Repository to report status for.
        - name: session_id
          in: query
          required: false
          schema:
            type: string
            format: uuid
          description: Optional session whose checkout supplies the status.
      responses:
        '200':
          description: Git status snapshot
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GitStatusSummary'
        '202':
          description: First status generation is refreshing off the request path
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GitStatusSummary'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects/{id}/git/repos/{repo_id}/changes:
    get:
      tags:
        - git
      operationId: listGitChanges
      summary: Page changed paths from a cached status revision
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: Canonical project UUID.
        - name: repo_id
          in: path
          required: true
          schema:
            type: string
            pattern: ^[a-zA-Z0-9_-]+$
          description: Repository to page changes for.
        - name: session_id
          in: query
          required: false
          schema:
            type: string
            format: uuid
          description: Optional session whose checkout supplies the changes.
        - name: revision
          in: query
          required: false
          schema:
            type: integer
            format: uint64
        - name: cursor
          in: query
          required: false
          schema:
            type: string
        - name: limit
          in: query
          required: false
          schema:
            type: integer
            minimum: 1
            maximum: 500
            default: 200
      responses:
        '200':
          description: Changed path page from the newest available generation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GitChangesPage'
        '202':
          description: First status generation is refreshing
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GitChangesPage'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects/{id}/git/repos/{repo_id}/branches:
    get:
      tags:
        - git
      operationId: listGitBranches
      summary: List local branches for the Git tab picker
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: Canonical project UUID.
        - name: repo_id
          in: path
          required: true
          schema:
            type: string
            pattern: ^[a-zA-Z0-9_-]+$
          description: Repository to list branches for.
        - name: session_id
          in: query
          required: false
          schema:
            type: string
            format: uuid
          description: Optional session whose checkout scopes the repository.
      responses:
        '200':
          description: Local branches
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GitBranchesView'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects/{id}/git/repos/{repo_id}/checkout:
    post:
      tags:
        - git
      operationId: checkoutGit
      x-person-action: record
      summary: Switch to a branch (optionally creating it first)
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: Canonical project UUID.
        - name: repo_id
          in: path
          required: true
          schema:
            type: string
            pattern: ^[a-zA-Z0-9_-]+$
          description: Repository to act on.
        - name: session_id
          in: query
          required: false
          schema:
            type: string
            format: uuid
          description: Optional session whose checkout scopes the operation.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/GitCheckoutRequest'
      responses:
        '200':
          description: Fresh git status after checkout
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GitMutationResult'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '422':
          $ref: '#/components/responses/UnprocessableContent'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects/{id}/git/repos/{repo_id}/discard:
    post:
      tags:
        - git
      operationId: discardGit
      x-person-action: record
      summary: Revert the working tree to a clean state
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: Canonical project UUID.
        - name: repo_id
          in: path
          required: true
          schema:
            type: string
            pattern: ^[a-zA-Z0-9_-]+$
          description: Repository to act on.
        - name: session_id
          in: query
          required: false
          schema:
            type: string
            format: uuid
          description: Optional session whose checkout scopes the operation.
      responses:
        '200':
          description: Fresh git status after discard
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GitMutationResult'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '422':
          $ref: '#/components/responses/UnprocessableContent'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects/{id}/git/repos/{repo_id}/push:
    post:
      tags:
        - git
      operationId: pushGit
      x-person-action: record
      summary: Publish the current branch to its upstream
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: Canonical project UUID.
        - name: repo_id
          in: path
          required: true
          schema:
            type: string
            pattern: ^[a-zA-Z0-9_-]+$
          description: Repository to act on.
        - name: session_id
          in: query
          required: false
          schema:
            type: string
            format: uuid
          description: Optional session whose checkout scopes the operation.
      responses:
        '200':
          description: Fresh git status after push
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GitMutationResult'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '422':
          $ref: '#/components/responses/UnprocessableContent'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects/{id}/git/repos/{repo_id}/pull:
    post:
      tags:
        - git
      operationId: pullGit
      x-person-action: record
      summary: Fast-forward the current branch from its upstream
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: Canonical project UUID.
        - name: repo_id
          in: path
          required: true
          schema:
            type: string
            pattern: ^[a-zA-Z0-9_-]+$
          description: Repository to act on.
        - name: session_id
          in: query
          required: false
          schema:
            type: string
            format: uuid
          description: Optional session whose checkout scopes the operation.
      responses:
        '200':
          description: Fresh git status after pull
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GitMutationResult'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '422':
          $ref: '#/components/responses/UnprocessableContent'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects/{id}/git/repos/{repo_id}/commit:
    post:
      tags:
        - git
      operationId: commitGit
      x-person-action: record
      summary: Stage and commit working-tree changes
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: Canonical project UUID.
        - name: repo_id
          in: path
          required: true
          schema:
            type: string
            pattern: ^[a-zA-Z0-9_-]+$
          description: Repository to act on.
        - name: session_id
          in: query
          required: false
          schema:
            type: string
            format: uuid
          description: Optional session whose checkout scopes the operation.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/GitCommitRequest'
      responses:
        '200':
          description: Fresh git status after the commit
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GitMutationResult'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '422':
          $ref: '#/components/responses/UnprocessableContent'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects/{id}/git/repos/{repo_id}/stash:
    post:
      tags:
        - git
      operationId: stashGit
      x-person-action: record
      summary: Stash working-tree changes (including untracked files)
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: Canonical project UUID.
        - name: repo_id
          in: path
          required: true
          schema:
            type: string
            pattern: ^[a-zA-Z0-9_-]+$
          description: Repository to act on.
        - name: session_id
          in: query
          required: false
          schema:
            type: string
            format: uuid
          description: Optional session whose checkout scopes the operation.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/GitStashRequest'
      responses:
        '200':
          description: Fresh git status after the stash
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GitMutationResult'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '422':
          $ref: '#/components/responses/UnprocessableContent'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects/{id}/git/repos/{repo_id}/commit-message:
    post:
      tags:
        - git
      operationId: draftGitCommitMessage
      x-person-action: none
      summary: Draft a commit message from the current diff via the lite model
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: Canonical project UUID.
        - name: repo_id
          in: path
          required: true
          schema:
            type: string
            pattern: ^[a-zA-Z0-9_-]+$
          description: Repository to act on.
        - name: session_id
          in: query
          required: false
          schema:
            type: string
            format: uuid
          description: Optional session whose checkout scopes the operation.
      responses:
        '200':
          description: Drafted commit message
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GitCommitMessageResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '422':
          $ref: '#/components/responses/UnprocessableContent'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
        default:
          $ref: '#/components/responses/Error'
  /v1/sessions/{id}/git-worktree:
    get:
      tags:
        - git
      operationId: getGitWorktree
      summary: View a chat's worktree binding
      description: |
        Returns the worktree binding for a chat. An unbound chat returns bound false with zero counts. A missing worktree directory returns state stale without probing the path.
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: Canonical session UUID.
      responses:
        '200':
          description: Worktree view
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GitWorktreeView'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        default:
          $ref: '#/components/responses/Error'
    put:
      tags:
        - git
      operationId: bindGitWorktree
      x-person-action: record
      summary: Create a worktree for a chat
      description: |
        Creates a separate checkout of one project repository on a new branch, outside the project folder. Refuses when the chat is busy, already bound, the branch exists, the repository HEAD is detached, or the target directory exists. The repository's shared git config is proven unchanged.
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: Canonical session UUID.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/GitWorktreeBindRequest'
      responses:
        '200':
          description: Bound worktree view
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GitWorktreeView'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
    delete:
      tags:
        - git
      operationId: deleteGitWorktree
      x-person-action: record
      summary: Remove a chat's worktree checkout
      description: |
        Removes the separate checkout and deletes the binding row. The branch and its commits are kept. A dirty worktree is refused. A missing worktree directory is always removable — that is the repair for a stale binding.
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: Canonical session UUID.
      responses:
        '204':
          description: Worktree removed
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/sessions/{id}/git-worktree/land:
    post:
      tags:
        - git
      operationId: landGitWorktree
      x-person-action: record
      summary: Merge a chat's branch into its base branch
      description: |
        Merges the session branch into the recorded base branch in the project folder with an explicit merge commit. Runs only when the chat is idle, both trees are clean, and the project folder is on the base branch. A conflicting merge is aborted; nothing is left half-applied. Landing does not unbind.
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: Canonical session UUID.
      responses:
        '200':
          description: Land result (including nothing_to_land)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GitWorktreeLandResult'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/local-data:
    get:
      tags:
        - local-data
      operationId: getLocalData
      summary: List rebuildable local-data bucket presence and sizes
      responses:
        '200':
          description: Bucket status catalog
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/LocalDataStatus'
        '401':
          $ref: '#/components/responses/Unauthorized'
        default:
          $ref: '#/components/responses/Error'
  /v1/history-storage:
    get:
      operationId: getHistoryStorage
      tags:
        - local-data
      summary: Inspect retained history storage
      parameters:
        - name: project_id
          in: query
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: History storage operation result
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HistoryStorageStatus'
        '400':
          $ref: '#/components/responses/Error'
          description: Stored history retention policy is invalid
        '401':
          $ref: '#/components/responses/Unauthorized'
        '409':
          $ref: '#/components/responses/Error'
          description: A restore is staged; restart before making another request
        '500':
          $ref: '#/components/responses/Error'
          description: History storage operation failed
        '503':
          $ref: '#/components/responses/Error'
          description: History storage is unavailable
        default:
          $ref: '#/components/responses/Error'
    patch:
      operationId: updateHistoryStorage
      x-person-action: record
      tags:
        - local-data
      summary: Apply a reviewed history retention policy
      parameters:
        - name: project_id
          in: query
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/HistoryRetentionRequest'
      responses:
        '200':
          description: History storage operation result
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HistoryRetentionPolicy'
        '400':
          $ref: '#/components/responses/Error'
          description: Invalid JSON or retention policy
        '401':
          $ref: '#/components/responses/Unauthorized'
        '409':
          $ref: '#/components/responses/Error'
          description: The reviewed preview is missing, expired, or invalidated, or a restore is staged
        '413':
          $ref: '#/components/responses/Error'
          description: Request body exceeds the JSON size limit
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/Error'
          description: History storage operation failed
        '503':
          $ref: '#/components/responses/Error'
          description: History storage is unavailable
        default:
          $ref: '#/components/responses/Error'
  /v1/history-storage/preview:
    post:
      operationId: previewHistoryRetention
      x-person-action: none
      tags:
        - local-data
      summary: Preview eligible historical bodies
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/HistoryRetentionRequest'
      responses:
        '200':
          description: History storage operation result
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HistoryRetentionPreview'
        '400':
          $ref: '#/components/responses/Error'
          description: Invalid JSON or retention policy
        '401':
          $ref: '#/components/responses/Unauthorized'
        '409':
          $ref: '#/components/responses/Error'
          description: History or policy changed during preview, or a restore is staged
        '413':
          $ref: '#/components/responses/Error'
          description: Request body exceeds the JSON size limit
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/Error'
          description: History storage operation failed
        '503':
          $ref: '#/components/responses/Error'
          description: History storage is unavailable
        default:
          $ref: '#/components/responses/Error'
  /v1/history-storage/prune:
    post:
      operationId: pruneHistory
      x-person-action: record
      tags:
        - local-data
      summary: Prune one reviewed batch of historical bodies
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/HistoryRetentionRequest'
      responses:
        '200':
          description: History storage operation result
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HistoryPruneResult'
        '400':
          $ref: '#/components/responses/Error'
          description: Invalid JSON or retention policy
        '401':
          $ref: '#/components/responses/Unauthorized'
        '409':
          $ref: '#/components/responses/Error'
          description: The reviewed preview is missing, expired, or invalidated, or a restore is staged
        '413':
          $ref: '#/components/responses/Error'
          description: Request body exceeds the JSON size limit
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/Error'
          description: History storage operation failed
        '503':
          $ref: '#/components/responses/Error'
          description: History storage is unavailable
        default:
          $ref: '#/components/responses/Error'
  /v1/history-storage/protections:
    post:
      operationId: createHistoryProtection
      x-person-action: record
      tags:
        - local-data
      summary: Protect a project or session from pruning
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/HistoryProtection'
      responses:
        '201':
          description: History protection created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HistoryProtection'
        '400':
          $ref: '#/components/responses/Error'
          description: Invalid JSON, protection scope, or owner
        '401':
          $ref: '#/components/responses/Unauthorized'
        '409':
          $ref: '#/components/responses/Error'
          description: A restore is staged; restart before making another request
        '413':
          $ref: '#/components/responses/Error'
          description: Request body exceeds the JSON size limit
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/Error'
          description: History storage operation failed
        '503':
          $ref: '#/components/responses/Error'
          description: History storage is unavailable
        default:
          $ref: '#/components/responses/Error'
  /v1/history-storage/protections/{protection_id}:
    delete:
      operationId: deleteHistoryProtection
      x-person-action: record
      tags:
        - local-data
      summary: Remove a history protection
      parameters:
        - name: protection_id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '204':
          description: History protection removed
        '400':
          $ref: '#/components/responses/Error'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/Error'
        '409':
          $ref: '#/components/responses/Error'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/Error'
        '503':
          $ref: '#/components/responses/Error'
        default:
          $ref: '#/components/responses/Error'
  /v1/local-data/clear:
    post:
      tags:
        - local-data
      operationId: clearLocalData
      x-person-action: record
      summary: Clear one or more rebuildable local-data buckets
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/LocalDataClearRequest'
      responses:
        '200':
          description: Per-bucket clear results (may include individual failures)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/LocalDataClearResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/backup:
    get:
      tags:
        - backup
      operationId: exportBackup
      summary: Download a local durable-install backup archive
      description: Streams a format-1 zip of the restorable durable config set plus a online SQLite snapshot of store.db and a manifest.json (replacement scope, size, and sha256 per file). Live-process and behavior-installing control-plane files, including MCP connections and extension setup, are excluded, as are managed credential stores. Conversation and retained project content is preserved without redaction and can contain secrets supplied by the user. A new device needs credentials and integrations configured again; an in-place restore preserves device credentials. Distinct from the scrubbed diagnostics bundle. Local only — nothing is uploaded.
      responses:
        '200':
          description: Zip archive
          headers:
            X-Backup-SHA256:
              description: SHA-256 of the complete compressed archive; native saves verify it before replacement.
              schema:
                type: string
                pattern: ^[0-9a-f]{64}$
          content:
            application/zip:
              schema:
                type: string
                format: binary
        '401':
          $ref: '#/components/responses/Unauthorized'
        '409':
          description: Retained files could not be captured; retry after active changes finish.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
        default:
          $ref: '#/components/responses/Error'
  /v1/host:
    get:
      tags:
        - host
      operationId: getHost
      summary: Identify the host and the caller
      description: Returns the host identity, the contract version it serves, the authenticated person, and the capabilities offered on this connection.
      responses:
        '200':
          description: Host identity and the caller's standing on it
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HostInfo'
        '401':
          $ref: '#/components/responses/Unauthorized'
        default:
          $ref: '#/components/responses/Error'
  /v1/backup/capabilities:
    get:
      tags:
        - backup
      operationId: getBackupCapabilities
      summary: Read archive format and transfer limits
      description: Available during normal operation and store recovery.
      responses:
        '200':
          description: Limits enforced by this engine
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BackupCapabilities'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
        default:
          $ref: '#/components/responses/Error'
  /v1/backup/restore:
    post:
      tags:
        - backup
      operationId: restoreBackup
      x-person-action: record
      summary: Stage a backup archive for apply on next launch
      description: Applies compressed and expanded size limits, rejects unknown entries, validates the archive manifest and per-file sizes and sha256 hashes, and requires a known baseline with a registered upgrade path to this binary. An older supported store is upgraded in staging, then checked for schema, SQLite integrity, foreign keys, and retained object references. Imported retention policies are suspended until explicitly reviewed. Every affected live file is copied to a transaction-scoped .restore-recovery-<uuid>, present files are fsynced under .restore-staging-<uuid>, and explicit deletions are staged in an exclusively published restore.pending.json. A second stage is rejected while that marker exists. The live store is not swapped until the next launch preflights the complete staged read set and applies the marker before db.Open. The desktop client restarts its managed backend immediately after staging, before normal work can resume. Restore replaces; it never merges.
      requestBody:
        required: true
        content:
          application/zip:
            schema:
              type: string
              format: binary
              maxLength: 68719476736
              description: Compressed archive; its size bound is the backup archive limit.
      responses:
        '200':
          description: Restore staged; restart required
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BackupRestoreResult'
        '400':
          description: Invalid or corrupt archive
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '409':
          description: Archive schema does not match this app, or another restore already holds the pending marker
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '413':
          $ref: '#/components/responses/Error'
          description: Archive exceeds maximum allowed upload size
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
        default:
          $ref: '#/components/responses/Error'
  /v1/backup/restore/recovery-snapshot:
    post:
      tags:
        - backup
      operationId: restoreRecoverySnapshot
      x-person-action: record
      summary: Stage the latest compatible upgrade recovery snapshot
      description: Selects a self-contained snapshot directory in upgrade-recovery with a known schema baseline and upgrade route, validates its complete payload, and publishes the same restore transaction used by uploaded backups. Local recovery uses copy-on-write files where supported and does not inherit public ZIP upload limits. Recovery includes the store and retained files. The live store is not swapped until the next launch. Available on a healthy server and in recovery mode.
      responses:
        '200':
          description: Restore staged; restart required
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BackupRestoreResult'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '409':
          description: Recovery snapshot missing, invalid, incompatible with this app, or another restore already holds the pending marker
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
        default:
          $ref: '#/components/responses/Error'
  /v1/store/reset:
    post:
      tags:
        - backup
      operationId: resetStore
      x-person-action: record
      summary: Stage a clean current store for apply on next launch
      description: Preserves the refused store, its SQLite sidecars, rolling snapshot, identity-coupled project/session files, rebuildable store projections, and the first-run onboarding latch in a transaction-scoped .restore-recovery-<uuid>. It then stages a freshly initialized current schema baseline and resets the onboarding latch for the next launch. Provider definitions, credentials, approvals, and other device configuration are not replaced. Available on a healthy server and in recovery mode. The desktop client requires explicit confirmation and restarts its managed backend immediately after staging.
      responses:
        '200':
          description: Fresh store staged; restart required
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BackupRestoreResult'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '409':
          description: Another restore or reset already holds the pending marker
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
        default:
          $ref: '#/components/responses/Error'
  /v1/contributions:
    get:
      tags:
        - contributions
      operationId: getContributions
      summary: One complete projection of the captured contribution frame
      description: |
        Captures the current device catalog and MCP resource generations once, then
        projects the compiled contribution set with its frame_revision. The
        response is a one-way projection: it has no independent persistence,
        resolver, merge rules, cache key, or fallback source.

        The frame is device-scoped. A project may suggest extensions; it never
        contributes them. Appearance is device configuration, so the theme list
        resolves at this scope.
      parameters:
        - name: If-None-Match
          in: header
          required: false
          description: Quoted frame_revision held by the client.
          schema:
            type: string
      responses:
        '200':
          description: Complete contribution frame projection
          headers:
            ETag:
              description: Quoted frame_revision for conditional refreshes.
              schema:
                type: string
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ContributionFrameResponse'
        '304':
          description: The contribution frame matches If-None-Match.
          headers:
            ETag:
              description: Quoted frame_revision.
              schema:
                type: string
        '401':
          $ref: '#/components/responses/Unauthorized'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects/{id}/commands/{command_id}/invoke:
    post:
      tags:
        - contributions
      operationId: invokeProjectCommand
      x-person-action: record
      summary: Invoke a project-bound contribution command
      description: |
        Project-bound kinds: composer_prefill, navigate, external_link. The
        host captures the current frame, compares the caller's frame_revision
        (an identical compiled subgraph proceeds), revalidates observables and
        authority, and returns a closed typed ui_effect. No session is minted.
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
        - name: command_id
          in: path
          required: true
          schema:
            type: string
            pattern: ^[a-zA-Z0-9_-][a-zA-Z0-9_.-]*(/[a-zA-Z0-9_-][a-zA-Z0-9_.-]*)+:[a-z0-9]+(-[a-z0-9]+)*$
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CommandInvokeRequest'
      responses:
        '200':
          description: Completed invocation with its UI effect
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CommandInvokeResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '422':
          $ref: '#/components/responses/UnprocessableContent'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '502':
          $ref: '#/components/responses/BadGateway'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
        default:
          $ref: '#/components/responses/Error'
  /v1/sessions/{id}/commands/{command_id}/invoke:
    post:
      tags:
        - contributions
      operationId: invokeSessionCommand
      x-person-action: record
      summary: Invoke a session-bound contribution command
      description: |
        Session-bound kinds: editor_action, workflow_start, and mcp_tool.
        The host captures the current frame, compares the
        caller's frame_revision with subgraph equality, resolves the command
        from the captured frame, revalidates observables and authority, and
        executes against that same frame.
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
        - name: command_id
          in: path
          required: true
          schema:
            type: string
            pattern: ^[a-zA-Z0-9_-][a-zA-Z0-9_.-]*(/[a-zA-Z0-9_-][a-zA-Z0-9_.-]*)+:[a-z0-9]+(-[a-z0-9]+)*$
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CommandInvokeRequest'
      responses:
        '200':
          description: Completed synchronous invocation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CommandInvokeResponse'
        '202':
          description: Accepted asynchronous invocation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CommandInvokeResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '422':
          $ref: '#/components/responses/UnprocessableContent'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '502':
          $ref: '#/components/responses/BadGateway'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects/{id}/commands/{command_id}/choices/{step_id}:
    post:
      tags:
        - contributions
      operationId: resolveContributionChoices
      x-person-action: none
      summary: Load one explicitly activated dynamic interaction choice source
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
        - name: command_id
          in: path
          required: true
          schema:
            type: string
            pattern: ^[a-zA-Z0-9_-][a-zA-Z0-9_.-]*(/[a-zA-Z0-9_-][a-zA-Z0-9_.-]*)+:[a-z0-9]+(-[a-z0-9]+)*$
        - name: step_id
          in: path
          required: true
          schema:
            type: string
            pattern: ^[a-zA-Z0-9_.-]+$
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ContributionChoiceRequest'
      responses:
        '200':
          description: Bounded provider-attributed choices
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ContributionChoiceResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '502':
          $ref: '#/components/responses/BadGateway'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects/{id}/contribution-search/{source_id}:
    post:
      tags:
        - contributions
      operationId: searchContributionSource
      x-person-action: none
      summary: Search one explicitly selected extension source
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
        - name: source_id
          in: path
          required: true
          schema:
            type: string
            pattern: ^[a-zA-Z0-9_-][a-zA-Z0-9_.-]*(/[a-zA-Z0-9_-][a-zA-Z0-9_.-]*)+:[a-z0-9]+(-[a-z0-9]+)*$
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ContributionSearchRequest'
      responses:
        '200':
          description: Ephemeral provider-attributed results
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ContributionSearchResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '502':
          $ref: '#/components/responses/BadGateway'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
        default:
          $ref: '#/components/responses/Error'
  /v1/extensions:
    get:
      tags:
        - extensions
      operationId: getExtensionsCatalog
      summary: The catalog generation in force — packs, units, diagnostics, and desired state
      description: |
        One resolve, projected whole: pack summaries with blocked_reason, the units
        they resolved to, the diagnostics against them, and the desired state that
        selected them. A pack summary and a diagnostic about the same pack come from
        the same generation, so they cannot disagree.

        MCP dependency is per contribution, not per pack: see the contribution
        frame's requirement readiness. Does not install or enable MCP providers.
      parameters:
        - $ref: '#/components/parameters/ExtensionsProjectIdQuery'
      responses:
        '200':
          description: The catalog generation in force
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ExtensionsCatalogView'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          description: Extension state changed (extension_state_changed)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        default:
          $ref: '#/components/responses/Error'
  /v1/extensions/units/{unit_id}:
    get:
      tags:
        - extensions
      operationId: getExtensionUnit
      summary: Get one effective unit (providers + bodies)
      parameters:
        - $ref: '#/components/parameters/ExtensionUnitId'
        - $ref: '#/components/parameters/ExtensionsProjectIdQuery'
      responses:
        '200':
          description: Unit detail
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ExtensionUnitDetail'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        default:
          $ref: '#/components/responses/Error'
    patch:
      tags:
        - extensions
      operationId: updateExtensionUnit
      x-person-action: record
      summary: Update unit enablement or collision owner in desired state
      parameters:
        - $ref: '#/components/parameters/ExtensionUnitId'
        - $ref: '#/components/parameters/ExtensionsProjectIdQuery'
        - $ref: '#/components/parameters/ExtensionsScopeQuery'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateExtensionUnitRequest'
      responses:
        '200':
          description: Updated desired state
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ExtensionMutationResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          description: Extension state changed (extension_state_changed)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/extensions/packs:
    post:
      tags:
        - extensions
      operationId: installExtensionPack
      x-person-action: record
      summary: Resolve and install a versioned pack graph
      description: Selects the newest release satisfying version, or the exact ref pin; resolves transitive dependencies; and atomically publishes desired state plus an exact integrity lock. Does not apply profiles or install MCP. Network resolution is cancelable and bounded.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ExtensionInstallRequest'
      responses:
        '201':
          description: Install result (pack installed or refreshed from source)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ExtensionInstallResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          description: Extension state changed (extension_state_changed)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/extensions/packs/{pack_id}:
    patch:
      tags:
        - extensions
      operationId: updateExtensionPack
      x-person-action: record
      summary: Enable or disable a pack in desired state
      parameters:
        - $ref: '#/components/parameters/ExtensionPackId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateExtensionPackRequest'
      responses:
        '200':
          description: Updated desired state
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ExtensionMutationResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          description: Extension state changed (extension_state_changed)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
    delete:
      tags:
        - extensions
      operationId: deleteExtensionPack
      x-person-action: record
      summary: Remove a cached pack from desired state (stock packs rejected)
      parameters:
        - $ref: '#/components/parameters/ExtensionPackId'
        - name: expected_revision
          in: query
          required: true
          description: Extension-state revision the caller last observed
          schema:
            type: string
      responses:
        '204':
          description: Removed — updated desired state
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          description: Extension state changed (extension_state_changed)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/extensions/configuration:
    patch:
      tags:
        - extensions
      operationId: updateExtensionConfiguration
      x-person-action: record
      summary: Atomically replace configuration values for one or more packs
      description: Each pack value is its complete replacement block; an empty map removes that pack's configuration. Values are non-secret scalars or string lists and every pack commits under one extension-state revision.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ExtensionConfigurationRequest'
      responses:
        '200':
          description: Updated desired state
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ExtensionMutationResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          description: Extension state changed (extension_state_changed)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/extensions/packs/{pack_id}/update:
    get:
      tags:
        - extensions
      operationId: getExtensionPackUpdate
      summary: Resolve and compare the complete candidate package graph
      description: |
        Stock packs update with the app. Development packs reload from disk. Release
        constraints re-resolve against SemVer tags; exact pins re-resolve only their
        recorded ref. The response reports every added, removed, upgraded, downgraded,
        or revised transitive package.
      parameters:
        - $ref: '#/components/parameters/ExtensionPackId'
      responses:
        '200':
          description: Update availability
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ExtensionPackUpdateStatus'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        default:
          $ref: '#/components/responses/Error'
    post:
      tags:
        - extensions
      operationId: applyExtensionPackUpdate
      x-person-action: record
      summary: Resolve and atomically publish the candidate package graph
      description: Fetches from the remote, so it can run for minutes; abandoning the request cancels the fetch, and a remote that never responds fails the request rather than hanging.
      parameters:
        - $ref: '#/components/parameters/ExtensionPackId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ExtensionRevisionRequest'
      responses:
        '200':
          description: Pack updated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ExtensionPackActionResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          description: Extension state changed (extension_state_changed)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/extensions/packs/{pack_id}/reload:
    post:
      tags:
        - extensions
      operationId: reloadExtensionPack
      x-person-action: record
      summary: Re-read a linked folder pack from disk and refresh the catalog
      parameters:
        - $ref: '#/components/parameters/ExtensionPackId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ExtensionRevisionRequest'
      responses:
        '200':
          description: Pack reloaded
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ExtensionPackActionResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          description: Extension state changed (extension_state_changed)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/extensions/packs/{pack_id}/profiles/{profile_name}/apply:
    post:
      tags:
        - extensions
      operationId: applyExtensionPackProfile
      x-person-action: record
      summary: Apply a pack profile into desired state
      parameters:
        - $ref: '#/components/parameters/ExtensionPackId'
        - $ref: '#/components/parameters/ExtensionProfileName'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ExtensionRevisionRequest'
      responses:
        '200':
          description: Profile applied
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ExtensionMutationResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          description: Extension state changed (extension_state_changed)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/extensions/meta-packs:
    post:
      tags:
        - extensions
      operationId: installExtensionMetaPack
      x-person-action: record
      summary: Resolve a versioned suite and install all member packages in one graph
      description: Clones from the source, so it can run for minutes; abandoning the request cancels the clone and installs nothing, and a remote that never responds fails the request rather than hanging.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ExtensionMetaPackInstallRequest'
      responses:
        '201':
          description: Suite installed; returns refreshed extensions list
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ExtensionMutationResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          description: Extension state changed (extension_state_changed)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/extensions/meta-packs/{meta_pack_id}:
    patch:
      tags:
        - extensions
      operationId: updateExtensionMetaPack
      x-person-action: record
      summary: Enable or disable every present member pack in a meta-pack suite
      parameters:
        - $ref: '#/components/parameters/ExtensionMetaPackId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateExtensionMetaPackRequest'
      responses:
        '200':
          description: Suite updated; returns refreshed extensions list
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ExtensionMutationResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          description: Extension state changed (extension_state_changed)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
    delete:
      tags:
        - extensions
      operationId: deleteExtensionMetaPack
      x-person-action: record
      summary: Remove a community meta-pack cache entry
      parameters:
        - $ref: '#/components/parameters/ExtensionMetaPackId'
        - name: expected_revision
          in: query
          required: true
          description: Extension-state revision the caller last observed
          schema:
            type: string
      responses:
        '204':
          description: Suite removed
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          description: Extension state changed (extension_state_changed)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/extensions/meta-packs/{meta_pack_id}/apply:
    post:
      tags:
        - extensions
      operationId: applyExtensionMetaPack
      x-person-action: record
      summary: Enable every member pack in a meta-pack suite
      parameters:
        - $ref: '#/components/parameters/ExtensionMetaPackId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ExtensionRevisionRequest'
      responses:
        '200':
          description: Suite enabled; returns refreshed extensions list
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ExtensionMutationResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          description: Extension state changed (extension_state_changed)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/extensions/suggestions:
    get:
      tags:
        - extensions
      operationId: getExtensionSuggestions
      summary: The install proposals this project lists, never applied
      description: |
        Reads the project's extensions.yaml as a suggestion manifest. Packs listed
        there are proposals to install on the device. Suggest rows do not contribute
        until a human accepts them; unit disables are separate project settings.
      parameters:
        - name: project_id
          in: query
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Suggestion rows for project contribution review and extension settings
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ExtensionSuggestionsResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          description: The project's extension suggestion file is invalid (extension_suggestion_invalid)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        default:
          $ref: '#/components/responses/Error'
  /v1/extensions/suggestions/accept:
    post:
      tags:
        - extensions
      operationId: acceptExtensionSuggestions
      x-person-action: record
      summary: Install suggested packs on the device
      parameters:
        - name: project_id
          in: query
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ExtensionSuggestionsAcceptRequest'
      responses:
        '200':
          description: Catalog after the device install
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ExtensionsCatalogView'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          description: The project's extension suggestion file is invalid (extension_suggestion_invalid)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/host-resources:
    get:
      operationId: getHostResources
      summary: List catalog host resources and current machine-state availability
      tags:
        - host-resources
      parameters:
        - name: project_id
          in: query
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Coherent host-resource snapshot
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HostResourcesResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        default:
          $ref: '#/components/responses/Error'
  /v1/host-resources/refresh:
    post:
      operationId: refreshHostResources
      x-person-action: none
      summary: Refresh all host-resource probes and return the new snapshot
      tags:
        - host-resources
      responses:
        '200':
          description: Refreshed host-resource snapshot
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HostResourcesResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/host-resources/{resource_id}:
    patch:
      operationId: updateHostResource
      x-person-action: record
      summary: Set or inherit the exact host-resource access rule for a scope
      tags:
        - host-resources
      parameters:
        - name: resource_id
          in: path
          required: true
          schema:
            type: string
            pattern: ^[a-z0-9]+([.-][a-z0-9]+)*$
        - name: project_id
          in: query
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateHostResourceRequest'
      responses:
        '200':
          description: Effective host-resource snapshot after the lossless policy mutation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HostResourcesResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/debug/den-perf:
    post:
      tags:
        - debug
      operationId: recordDenPerfEvents
      x-person-action: none
      summary: Ingest Den main-thread perf/stall events into the debug capture
      description: |
        Fire-and-forget sink for Den freeze attribution (longtask / loop-stall).
        When LYCAON_DEN_PERF_DEBUG or LYCAON_DEBUG_ALL is on, events append to
        den-perf.jsonl. When capture is off the handler still returns 204.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DenPerfEventsRequest'
      responses:
        '204':
          description: Accepted (written or capture disabled)
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects/{id}/source/views:
    post:
      tags:
        - projects
      operationId: createSourceView
      x-person-action: none
      summary: Create a source view
      parameters:
        - $ref: '#/components/parameters/ProjectId'
      description: 'Accept a tree or comparison view without waiting for complete preparation. operation_id is idempotent within the authenticated person, project, and client identity. Agent capture exclusions never restrict this human presentation. The host validates the selected source and workspace; client_id grants no authority. On every view, presentation, and interest route, a handle the host does not retain for the caller (never issued, expired, released, evicted, or issued before a restart) answers 404 source_view_not_found; reopen with the same intent. A view addressed by a deleted chat ends with it, and creating one answers session_not_found. An address in a folder that is not attached answers root_not_found. '
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SourceViewCreate'
      responses:
        '201':
          description: Create a source view
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SourceView'
          headers:
            Location:
              description: The retained resource URL.
              schema:
                type: string
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          description: Session or root not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '409':
          $ref: '#/components/responses/Conflict'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '422':
          $ref: '#/components/responses/UnprocessableContent'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects/{id}/source/comparison-digests:
    post:
      tags:
        - projects
      operationId: digestSourceComparisons
      x-person-action: none
      summary: Measure comparisons without opening views
      parameters:
        - $ref: '#/components/parameters/ProjectId'
      description: Answers each comparison's line counts and changes-presentation size, in request order, without opening views. Measuring neither decorates nor retains a document. A failed source answers its own failure; the others still answer.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SourceComparisonDigestRequest'
      responses:
        '200':
          description: Measure comparisons without opening views
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SourceComparisonDigests'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '422':
          $ref: '#/components/responses/UnprocessableContent'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects/{id}/source/views/{view_id}:
    get:
      tags:
        - projects
      operationId: getSourceView
      summary: Read source view state and revisions
      parameters:
        - $ref: '#/components/parameters/ProjectId'
        - name: view_id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Read source view state and revisions
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SourceView'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          description: View not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '409':
          $ref: '#/components/responses/Conflict'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '422':
          $ref: '#/components/responses/UnprocessableContent'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
        default:
          $ref: '#/components/responses/Error'
    delete:
      tags:
        - projects
      operationId: releaseSourceView
      x-person-action: none
      summary: Release a source view and its work interests
      parameters:
        - $ref: '#/components/parameters/ProjectId'
        - name: view_id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      description: A stage detach does not release the view. Releasing a view cannot invalidate shared resources pinned by other views or active reads. Release needs no live chat. A view the host does not retain answers source_view_not_found; a client treats that as already released.
      responses:
        '204':
          description: Release a source view and its work interests
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects/{id}/source/views/{view_id}/apply:
    post:
      tags:
        - projects
      operationId: applySourceViewIntent
      x-person-action: none
      summary: Change source presentation intent
      parameters:
        - $ref: '#/components/parameters/ProjectId'
        - name: view_id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      description: Deduplicate operation_id before checking expected_intent_revision. A different body with the same operation_id is rejected. Tree reveal and disclosure are explicit mutations; frame and locate reads never expand folders.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SourceViewUpdate'
      responses:
        '200':
          description: Change source presentation intent
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SourceView'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          description: View not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '409':
          description: Source view revision changed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '422':
          $ref: '#/components/responses/UnprocessableContent'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects/{id}/source/views/{view_id}/interests/{interest_id}:
    put:
      parameters:
        - $ref: '#/components/parameters/ProjectId'
        - name: view_id
          in: path
          required: true
          schema:
            type: string
            format: uuid
        - name: interest_id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      tags:
        - projects
      operationId: replaceSourceViewportInterest
      x-person-action: none
      summary: Replace source viewport demand
      description: Newer sequences supersede previous demand for this consumer. Preparation is bounded and never changes presentation coordinates. Reordered older requests are ignored. Interests expire after thirty seconds without replacement. A presentation_id this view no longer presents answers source_view_revision_changed, as a navigation basis does.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SourceViewportInterest'
      responses:
        '204':
          description: Viewport demand accepted
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          description: Presentation or view not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '409':
          description: Source view revision changed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '422':
          $ref: '#/components/responses/UnprocessableContent'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
        default:
          $ref: '#/components/responses/Error'
    delete:
      parameters:
        - $ref: '#/components/parameters/ProjectId'
        - name: view_id
          in: path
          required: true
          schema:
            type: string
            format: uuid
        - name: interest_id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      tags:
        - projects
      operationId: releaseSourceViewportInterest
      x-person-action: none
      summary: Release source viewport demand
      responses:
        '204':
          description: Viewport demand released
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects/{id}/source/views/{view_id}/presentations/{presentation_id}/rows:
    get:
      tags:
        - projects
      operationId: getSourceViewRows
      x-positional-window: true
      summary: Read a bounded coherent source frame
      parameters:
        - name: presentation_id
          in: path
          required: true
          schema:
            type: string
            format: uuid
        - $ref: '#/components/parameters/ProjectId'
        - name: view_id
          in: path
          required: true
          schema:
            type: string
            format: uuid
        - name: offset
          in: query
          schema:
            type: integer
            minimum: 0
            format: int64
          description: Display row offset in this presentation, or relative to the resolved anchor. Anchored reads clamp to the last row.
        - name: limit
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 200
            default: 200
        - name: anchor
          in: query
          schema:
            type: string
            maxLength: 131072
          description: Base64url-encoded JSON SourceTreeAddress or SourceComparisonAnchor. This is a coordinate, not an authorization credential.
        - name: context_before
          in: query
          schema:
            type: integer
            minimum: 0
            maximum: 199
            default: 0
          description: Include up to this many display rows before the resolved anchor plus offset. Requires an anchor and must be less than limit. The returned span and anchor describe the first returned row.
        - name: retain
          in: query
          schema:
            type: string
            maxLength: 8192
          description: For tree frames, a JSON array of at most 32 SourceTreeFramePrefix proofs from retained frames. The response validates the largest unchanged prefix in the returned snapshot.
      description: At most 200 rows and 256 KiB including ancestor and layout context, read from the immutable presentation. Reads never discover directories, advance revisions, or change the extent. Endpoint bodies and full child arrays are not returned.
      responses:
        '200':
          description: Read a bounded coherent source frame
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SourceViewFrame'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          description: Presentation or view not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '409':
          description: Source view revision changed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '422':
          $ref: '#/components/responses/UnprocessableContent'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects/{id}/source/views/{view_id}/presentations/{presentation_id}/locate:
    get:
      tags:
        - projects
      operationId: locateSourceView
      summary: Locate an address without changing disclosure
      parameters:
        - name: presentation_id
          in: path
          required: true
          schema:
            type: string
            format: uuid
        - $ref: '#/components/parameters/ProjectId'
        - name: view_id
          in: path
          required: true
          schema:
            type: string
            format: uuid
        - name: anchor
          in: query
          schema:
            type: string
            maxLength: 131072
          description: Base64url-encoded JSON SourceTreeAddress or SourceComparisonAnchor. Supply either anchor, or line and side for a comparison.
        - name: line
          in: query
          schema:
            type: integer
            minimum: 1
          description: One-based source line; comparison views only.
        - name: side
          in: query
          schema:
            type: string
            enum:
              - before
              - after
          description: Required with line.
      responses:
        '200':
          description: Locate an address without changing disclosure
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SourceViewLocation'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          description: Presentation or view not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '409':
          $ref: '#/components/responses/Conflict'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '422':
          $ref: '#/components/responses/UnprocessableContent'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects/{id}/source/views/{view_id}/presentations/{presentation_id}/search:
    get:
      tags:
        - projects
      operationId: searchSourceView
      summary: Find within one source presentation
      parameters:
        - name: presentation_id
          in: path
          required: true
          schema:
            type: string
            format: uuid
        - $ref: '#/components/parameters/ProjectId'
        - name: view_id
          in: path
          required: true
          schema:
            type: string
            format: uuid
        - name: q
          in: query
          schema:
            type: string
            minLength: 1
            maxLength: 4096
          required: true
        - name: case_sensitive
          in: query
          schema:
            type: boolean
        - name: cursor
          in: query
          schema:
            type: string
        - name: limit
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 200
            default: 200
      description: Continuations are opaque and bound to the query and projection revision. Comparison search reads pinned source text; tree search reads labels in the current projection. This does not replace repository-wide source search.
      responses:
        '200':
          description: Find within one source presentation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SourceViewSearchPage'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          description: Presentation or view not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '409':
          $ref: '#/components/responses/Conflict'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '422':
          $ref: '#/components/responses/UnprocessableContent'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects/{id}/source/views/{view_id}/presentations:
    post:
      tags:
        - projects
      operationId: createSourcePresentation
      x-person-action: none
      summary: Acquire an immutable source presentation
      description: Retain complete coordinates for the requested intent revision. Preparation returns source_view_preparing without acquiring a resource. Retrying operation_id returns the same retained resource. Progress events cannot replace this presentation.
      parameters:
        - $ref: '#/components/parameters/ProjectId'
        - name: view_id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SourcePresentationCreate'
      responses:
        '201':
          description: Retained presentation with exact extent
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SourcePresentation'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          description: View not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '409':
          $ref: '#/components/responses/Conflict'
        '413':
          $ref: '#/components/responses/ContentTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '422':
          $ref: '#/components/responses/UnprocessableContent'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
        default:
          $ref: '#/components/responses/Error'
  /v1/projects/{id}/source/views/{view_id}/presentations/{presentation_id}:
    delete:
      tags:
        - projects
      operationId: releaseSourcePresentation
      x-person-action: none
      summary: Release a source presentation
      description: Release is idempotent. A presentation belongs to one view; under any other view it is not retained and is already released.
      parameters:
        - $ref: '#/components/parameters/ProjectId'
        - name: view_id
          in: path
          required: true
          schema:
            type: string
            format: uuid
        - name: presentation_id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '204':
          description: Presentation released after its last active read
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        default:
          $ref: '#/components/responses/Error'
components:
  parameters:
    SessionId:
      name: id
      in: path
      required: true
      schema:
        type: string
        format: uuid
    DraftSlotId:
      name: slot_id
      in: path
      required: true
      schema:
        type: string
        format: uuid
      description: Stable coordinator draft message id for the turn
    ProjectId:
      name: id
      in: path
      required: true
      schema:
        type: string
        format: uuid
    SourceSessionIdQuery:
      name: session_id
      in: query
      required: false
      schema:
        type: string
        format: uuid
      description: Optional session whose bound checkout supplies the project roots
    RootId:
      name: root_id
      in: path
      required: true
      schema:
        type: string
        format: uuid
    SourceRootIdQuery:
      name: root_id
      in: query
      required: false
      schema:
        type: string
        format: uuid
      description: Attached root to browse; defaults to the primary root when omitted
    SourceDirQuery:
      name: dir
      in: query
      required: false
      schema:
        type: string
      description: Root-relative, slash-separated directory; "." or omitted means the root folder
    BlueprintId:
      name: blueprint_id
      in: path
      required: true
      schema:
        type: string
        format: uuid
    DelegationId:
      name: id
      in: path
      required: true
      schema:
        type: string
        format: uuid
    ProjectIDQuery:
      name: project_id
      in: query
      required: true
      schema:
        type: string
        format: uuid
      description: Canonical project UUID from the project registry
    WorkerId:
      name: id
      in: path
      required: true
      schema:
        type: string
        format: uuid
    EventsProjectIDQuery:
      name: project_id
      in: query
      required: false
      schema:
        type: string
        format: uuid
      description: Canonical project UUID from the project registry. Omit it to subscribe device-wide.
    EventsAfterQuery:
      name: after
      in: query
      required: false
      description: Opaque cursor; replay continues after it before live delivery
      schema:
        type: string
    EventsClientIDQuery:
      name: client_id
      in: query
      required: false
      description: The client this stream belongs to, as supplied to editor document requests. Editing leases follow it — the host releases the leases a client holds once its last stream has been gone for the lease grace, so a closed or crashed window stops holding a document. Omit it for a stream that holds no leases.
      schema:
        type: string
        pattern: ^[a-zA-Z0-9:._-]+$
    ExtensionsProjectIdQuery:
      name: project_id
      in: query
      required: false
      schema:
        type: string
        format: uuid
      description: Project UUID for project desired state; omit for device-only view/mutations
    ExtensionUnitId:
      name: unit_id
      in: path
      required: true
      description: |
        URL-encoded unit id. Unit ids contain slashes (workflows/plan,
        workflows/_templates/hotfix), so each id is sent as one encoded segment:
        workflows%2F_templates%2Fhotfix.
      schema:
        type: string
        pattern: ^[a-zA-Z0-9_./%:-]+$
    ExtensionsScopeQuery:
      name: scope
      in: query
      required: false
      schema:
        $ref: '#/components/schemas/ExtensionsDesiredScope'
        default: project
      description: Desired-state file to write; the project scope requires project_id
    ExtensionPackId:
      name: pack_id
      in: path
      required: true
      description: |
        URL-encoded pack id. Pack ids are namespace/name, so they are sent as one
        encoded segment: painted-wolf%2Fplatform.
      schema:
        type: string
        pattern: ^[a-zA-Z0-9_-][a-zA-Z0-9_.-]*(/[a-zA-Z0-9_-][a-zA-Z0-9_.-]*)*$
    ExtensionProfileName:
      name: profile_name
      in: path
      required: true
      description: Profile name declared by the pack manifest
      schema:
        type: string
        pattern: ^[a-zA-Z0-9_-][a-zA-Z0-9_.-]*$
    ExtensionMetaPackId:
      name: meta_pack_id
      in: path
      required: true
      description: |
        URL-encoded meta-pack id. Meta-pack ids are namespace/name, so they are sent as one
        encoded segment.
      schema:
        type: string
        pattern: ^[a-zA-Z0-9_-][a-zA-Z0-9_.-]*(/[a-zA-Z0-9_-][a-zA-Z0-9_.-]*)*$
  responses:
    MethodNotAllowed:
      description: Route exists but does not support the request method
      headers:
        Allow:
          description: Supported methods, sorted and de-duplicated.
          schema:
            type: string
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    InternalError:
      description: The host failed unexpectedly; the failure is logged.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    Error:
      description: Error response
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    Unauthorized:
      description: Missing or invalid sidecar bearer token
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    BadRequest:
      description: The request is malformed or names invalid values; `code` says which.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    NotFound:
      description: A resource the request addresses does not exist; `code` names the resource.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    Conflict:
      description: The request conflicts with current host state; `code` says which.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    ContentTooLarge:
      description: The request body or a named input exceeds its bound.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    UnsupportedMediaType:
      description: Request body Content-Type is absent, malformed, or unsupported
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    TooManyRequests:
      description: Request rate limit exceeded
      headers:
        Retry-After:
          description: Whole seconds until the same request may be retried.
          required: true
          schema:
            type: integer
            minimum: 1
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    Forbidden:
      description: Policy or confinement refuses the operation; `code` says which.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    UnprocessableContent:
      description: The request is well formed but its content cannot be processed; `code` says why.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    ServiceUnavailable:
      description: A subsystem the operation needs is not available yet; `code` names it.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    BadGateway:
      description: An upstream provider or process answered with a failure; `code` says which.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    WorkflowValidationFailed:
      description: Workflow manifest failed validation (`workflow_validation_failed`); `details.errors` lists each diagnostic.
      content:
        application/json:
          schema:
            allOf:
              - $ref: '#/components/schemas/Error'
              - type: object
                required:
                  - details
                properties:
                  details:
                    $ref: '#/components/schemas/ComposeValidationDetails'
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: |
        API client token from `LYCAON_API_TOKEN` at serve time or `~/.config/paintedwolf/api.token` (mode 0600).
        Not stored in `daemon.json`. Required on all `/v1/*` routes.
  schemas:
    Person:
      x-go-generate: true
      type: object
      description: A human who acts on this host. Durable authorship names a person by id; the window or tab they used is a separate client id.
      required:
        - id
        - role
      additionalProperties: false
      properties:
        id:
          type: string
          format: uuid
        role:
          $ref: '#/components/schemas/PersonRole'
    PersonRole:
      type: string
      description: The authority a person holds on this host.
      enum:
        - owner
    HostInfo:
      x-go-generate: true
      type: object
      description: The host a client is connected to, the contract it serves, and who the client is on it. A client reads this before relying on any other route.
      required:
        - host_id
        - host_public_key
        - product_version
        - contract_version
        - caller
        - capabilities
      additionalProperties: false
      properties:
        host_id:
          type: string
          format: uuid
          description: Stable identity of this install, derived from its public key. It survives restarts and store resets, and changes only when the host key is replaced.
        host_public_key:
          type: string
          description: Ed25519 public key of this host, standard base64 of the 32 key bytes.
        product_version:
          type: string
          description: Host product semver.
        contract_version:
          type: string
          description: Host API contract version (MAJOR.MINOR.PATCH). A client built for a different major version must not use the host.
        caller:
          $ref: '#/components/schemas/Person'
        capabilities:
          type: array
          description: Facilities the host offers this client, sorted and unique.
          items:
            $ref: '#/components/schemas/HostCapability'
    HostCapability:
      type: string
      description: A facility the host offers the client on this connection.
      enum:
        - shared_device
    HealthResponse:
      x-go-generate: true
      type: object
      required:
        - status
        - version
        - store_revision
        - schema_version
        - recovery_snapshot_available
      additionalProperties: false
      properties:
        status:
          type: string
          enum:
            - ok
            - recovery
          description: ok when the store is open and the server is serving; recovery when this build must not open the store and only restore routes are available.
        version:
          type: string
          description: Host/desktop semver (sidecar binary).
        store_revision:
          type: integer
          format: int64
          minimum: 0
          description: Monotonic sidecar boot counter; Den reconciles cache when this changes.
        schema_version:
          type: integer
          minimum: 0
          description: SQLite baseline marker used by this binary.
        min_den_version:
          type: string
          description: Optional semver the sidecar expects from Den; omit or empty skips skew UX.
        previous_app_version:
          type: string
          description: App version that last opened this store before the current boot. Omitted when unchanged (ordinary boot).
        store_schema_version:
          type: integer
          minimum: 0
          description: PRAGMA user_version read from the live store when status is recovery. Omitted on ordinary health.
        recovery_reason:
          type: string
          enum:
            - integrity_failed
            - schema_mismatch
          description: Closed reason the store cannot be served. Present when status is recovery.
        recovery_detail:
          type: string
          description: Human-readable sentence matching the recovery reason. Present when status is recovery.
        recovery_snapshot_available:
          type: boolean
          description: Whether a local recovery snapshot has valid metadata and a supported schema upgrade route. Restore verifies all payload hashes before use. Always false on ordinary health.
        recovery_snapshot_at:
          type: string
          format: date-time
          description: RFC 3339 UTC creation time of the recovery snapshot when available. Omitted when recovery_snapshot_available is false.
    HostResourceStatus:
      type: string
      enum:
        - available
        - unavailable
        - unknown
    HostResourceHostSupport:
      type: string
      enum:
        - supported
        - unsupported
        - not_applicable
    HostResourceExecutionSurface:
      type: string
      enum:
        - process_exec
    HostResourceConnectionMode:
      type: string
      enum:
        - none
        - proxy
        - socks
        - baseline_loopback
        - local_service
        - direct_ip
        - dynamic
    HostResourceLocalServiceTransport:
      type: string
      enum:
        - unix_socket
        - named_pipe
    HostResourceConnection:
      x-go-generate: true
      type: object
      required:
        - mode
      properties:
        mode:
          $ref: '#/components/schemas/HostResourceConnectionMode'
        transport:
          $ref: '#/components/schemas/HostResourceLocalServiceTransport'
        target:
          type: string
      additionalProperties: false
    HostResource:
      x-go-generate: true
      type: object
      required:
        - id
        - family
        - label
        - category
        - description
        - origin
        - status
        - host_support
        - access
        - access_setting
        - prompt
        - surfaces
        - connections
        - checked_at
      properties:
        id:
          type: string
          pattern: ^[a-z0-9]+([.-][a-z0-9]+)*$
        family:
          type: string
          pattern: ^[a-z0-9]+([.-][a-z0-9]+)*$
        label:
          type: string
        category:
          type: string
        description:
          type: string
        docs_url:
          type: string
          format: uri
        origin:
          type: string
          enum:
            - built-in
            - user
        status:
          $ref: '#/components/schemas/HostResourceStatus'
        host_support:
          $ref: '#/components/schemas/HostResourceHostSupport'
        access:
          $ref: '#/components/schemas/HostResourceAccess'
        access_setting:
          $ref: '#/components/schemas/HostResourceAccessSetting'
        prompt:
          $ref: '#/components/schemas/HostResourcePromptMode'
        reason:
          type: string
        surfaces:
          type: array
          items:
            $ref: '#/components/schemas/HostResourceExecutionSurface'
        connections:
          type: array
          items:
            $ref: '#/components/schemas/HostResourceConnection'
        checked_at:
          x-go-type: string
          type: string
          format: date-time
      additionalProperties: false
    HostResourceDiagnostic:
      x-go-generate: true
      type: object
      required:
        - code
        - message
      properties:
        code:
          type: string
        message:
          type: string
      additionalProperties: false
    HostResourcesResponse:
      x-go-generate: true
      type: object
      required:
        - version
        - resources
        - diagnostics
        - user_catalog_path
        - checked_at
        - fingerprint
      properties:
        version:
          type: integer
          minimum: 1
        resources:
          type: array
          items:
            $ref: '#/components/schemas/HostResource'
        diagnostics:
          type: array
          items:
            $ref: '#/components/schemas/HostResourceDiagnostic'
        user_catalog_path:
          type: string
        checked_at:
          x-go-type: string
          type: string
          format: date-time
        fingerprint:
          type: string
      additionalProperties: false
    BoardDelegationEntry:
      x-go-generate: true
      type: object
      required:
        - id
        - phase
      additionalProperties: false
      properties:
        id:
          type: string
          format: uuid
        phase:
          $ref: '#/components/schemas/DelegationPhase'
        task:
          type: string
    BoardDetailLevel:
      type: string
      enum:
        - status
        - compact
        - full
        - forensic
    BoardForensicWorkers:
      x-go-generate: true
      type: object
      description: Present only when detail_level is forensic. Full worker job records for host debugging — not for routine coordination.
      required:
        - notice
        - tasks
      additionalProperties: false
      properties:
        notice:
          type: string
        tasks:
          type: array
          items:
            $ref: '#/components/schemas/WorkerTask'
    BoardGitRepoLine:
      x-go-generate: true
      type: object
      required:
        - repo_id
        - label
        - dirty
        - staged_count
        - unstaged_count
      additionalProperties: false
      properties:
        repo_id:
          type: string
          format: uuid
        label:
          type: string
        branch:
          type: string
        dirty:
          type: boolean
        staged_count:
          type: integer
          minimum: 0
        unstaged_count:
          type: integer
          minimum: 0
    BoardGitSlice:
      x-go-generate: true
      type: object
      required:
        - available
        - repo_id
        - label
        - dirty
        - staged_count
        - unstaged_count
        - others
        - others_truncated
      additionalProperties: false
      properties:
        available:
          type: boolean
        repo_id:
          type: string
          format: uuid
        label:
          type: string
        branch:
          type: string
        head_short:
          type: string
        dirty:
          type: boolean
        staged_count:
          type: integer
          minimum: 0
        unstaged_count:
          type: integer
          minimum: 0
        recent_commits:
          type: array
          items:
            type: string
        others:
          type: array
          items:
            $ref: '#/components/schemas/BoardGitRepoLine'
        others_truncated:
          type: integer
          minimum: 0
        worktree:
          x-go-pointer: true
          $ref: '#/components/schemas/BoardGitWorktree'
    BoardGitWorktree:
      x-go-generate: true
      type: object
      additionalProperties: false
      description: |
        The session's own checkout when it has one. Absent for a session working directly in the project's folders.
      properties:
        branch:
          type: string
        base_branch:
          type: string
        ahead_of_base:
          type: integer
        behind_base:
          type: integer
        dirty:
          type: boolean
      required:
        - branch
        - base_branch
        - ahead_of_base
        - behind_base
        - dirty
    BoardScansSlice:
      x-go-generate: true
      type: object
      additionalProperties: false
      properties:
        current_assessment:
          x-go-pointer: true
          $ref: '#/components/schemas/BoardScanSummary'
        latest_attempt:
          x-go-pointer: true
          $ref: '#/components/schemas/BoardScanSummary'
        compare:
          x-go-pointer: true
          $ref: '#/components/schemas/BoardScanCompareSlice'
    BoardScanCompareSlice:
      x-go-generate: true
      type: object
      required:
        - scanners_compared
        - new_count
        - resolved_count
      additionalProperties: false
      properties:
        baseline_assessment_id:
          type: string
          format: uuid
          description: Previous complete assessment used for auto-compare.
        scanners_compared:
          type: integer
          minimum: 0
        new_count:
          type: integer
          minimum: 0
        resolved_count:
          type: integer
          minimum: 0
        new_by_level:
          type: object
          additionalProperties:
            type: integer
            minimum: 0
        top_new:
          type: array
          maxItems: 2
          items:
            $ref: '#/components/schemas/BoardScanCompareStub'
    BoardScanCompareStub:
      x-go-generate: true
      type: object
      required:
        - file
        - rule_id
      additionalProperties: false
      properties:
        file:
          type: string
        rule_id:
          type: string
          pattern: ^[a-zA-Z0-9_.:/-]+$
    BoardScanSummary:
      x-go-generate: true
      x-go-field-order:
        - assessment_id
        - source_snapshot_id
        - coverage_status
        - scanner_count
        - status
        - categories
        - findings_count
        - head_short
        - findings_by_level
        - findings_by_kind
        - unmapped_count
        - top_locations
        - warning_summary
        - error
        - trigger
        - created_at
        - completed_at
        - started_at
        - long_running
      type: object
      required:
        - assessment_id
        - scanner_count
        - status
        - findings_count
        - created_at
        - long_running
      additionalProperties: false
      properties:
        assessment_id:
          type: string
          format: uuid
        source_snapshot_id:
          type: string
          pattern: ^[a-zA-Z0-9_.:/-]+$
        coverage_status:
          $ref: '#/components/schemas/ScanCoverageStatus'
        scanner_count:
          type: integer
          minimum: 0
        status:
          $ref: '#/components/schemas/CodeScanStatus'
        categories:
          type: array
          items:
            $ref: '#/components/schemas/ScanCategory'
        findings_count:
          type: integer
          minimum: 0
        warning_summary:
          type: array
          items:
            $ref: '#/components/schemas/ScanWarningSummary'
        error:
          type: string
        trigger:
          $ref: '#/components/schemas/ScanTrigger'
        created_at:
          type: string
          format: date-time
        completed_at:
          x-go-pointer: true
          type: string
          format: date-time
        started_at:
          x-go-pointer: true
          type: string
          format: date-time
        long_running:
          type: boolean
        head_short:
          type: string
        findings_by_level:
          type: object
          additionalProperties:
            type: integer
            minimum: 0
        findings_by_kind:
          type: object
          additionalProperties:
            type: integer
            minimum: 0
        unmapped_count:
          type: integer
          minimum: 0
          description: SAST rows still mapped to SCAN_FINDING_UNMAPPED.
        top_locations:
          type: array
          items:
            $ref: '#/components/schemas/BoardScanTopLocation'
    BoardView:
      x-go-generate: true
      type: object
      description: Slim pack board view. Use detail_level=forensic only when you need full worker job records including workspace baselines.
      required:
        - summary
        - repo
        - cost
        - pack_content_hash
        - detail_level
        - board
        - board_chars
        - truncated
        - generated_at
        - now_line
      additionalProperties: false
      properties:
        session_id:
          type: string
          format: uuid
          description: Coordinator session this board snapshot belongs to.
        summary:
          type: string
          description: One-line coordination headline (worker counts, merge status, conflicts).
        repo:
          $ref: '#/components/schemas/RepoBrief'
        git:
          x-go-pointer: true
          $ref: '#/components/schemas/BoardGitSlice'
        scans:
          x-go-pointer: true
          $ref: '#/components/schemas/BoardScansSlice'
        roster:
          type: array
          items:
            $ref: '#/components/schemas/BoardWorkerRosterEntry'
        promote_paths:
          type: array
          items:
            $ref: '#/components/schemas/WorkerPromoteJobPathStatus'
        overlay_merge_plan:
          x-go-pointer: true
          $ref: '#/components/schemas/OverlayMergePlan'
        delegation:
          type: array
          items:
            $ref: '#/components/schemas/BoardDelegationEntry'
        active_workflow_run:
          x-go-pointer: true
          $ref: '#/components/schemas/WorkflowRun'
        forensic_workers:
          x-go-pointer: true
          $ref: '#/components/schemas/BoardForensicWorkers'
        cost:
          x-go-type: '*CostSummary'
          anyOf:
            - $ref: '#/components/schemas/CostSummary'
            - type: 'null'
        pack_content_hash:
          type: string
        detail_level:
          $ref: '#/components/schemas/BoardDetailLevel'
        board:
          type: string
        board_chars:
          type: integer
          minimum: 0
        truncated:
          type: boolean
        truncated_sections:
          type: array
          items:
            type: string
        generated_at:
          x-go-type: string
          type: string
          format: date-time
        now_line:
          type: string
    BoardWorkerRosterEntry:
      x-go-generate: true
      type: object
      required:
        - worker_id
        - agent_type
        - status
      additionalProperties: false
      properties:
        worker_id:
          type: string
          format: uuid
        agent_type:
          type: string
        status:
          $ref: '#/components/schemas/WorkerStatus'
        merge_status:
          $ref: '#/components/schemas/WorkerMergeStatus'
        overlay_id:
          type: string
          pattern: ^[a-zA-Z0-9_.:/-]+$
        scope_summary:
          type: string
        changed_paths:
          type: array
          items:
            type: string
        touched_paths:
          type: array
          items:
            type: string
        brief:
          type: string
          description: Worker assignment goal included in full board detail.
        error:
          type: string
        max_tool_loops:
          type: integer
          minimum: 2
          maximum: 120
          description: Effective worker tool-round ceiling when known (host default 20, maximum 120)
        budget_request:
          x-go-pointer: true
          $ref: '#/components/schemas/WorkerBudgetRequest'
          description: The worker's unanswered request for more tool rounds; grant it with extend_worker_budget
        tool_loops_used:
          type: integer
          minimum: 0
          description: Completed tool rounds this worker run
        reservations:
          type: array
          items:
            type: string
          description: Repo-relative paths held by handoff_reserve for this worker
        blocked_on_approval:
          type: boolean
          description: True when checkpoint SSE reports a pending human checkpoint for this worker child session
    RepoBrief:
      x-go-generate: true
      x-go-field-order:
        - languages
        - file_count
        - layout
        - generated_at
        - refreshing
        - incomplete
      type: object
      description: Project orientation summary — languages detected by content via enry, listed in dominant-bytes order.
      required:
        - languages
        - file_count
        - generated_at
      additionalProperties: false
      properties:
        refreshing:
          type: boolean
          description: The orientation is warming or belongs to an older observed source generation.
        incomplete:
          type: boolean
          description: The observed file population has incomplete coverage, including settled budget omissions or failures. Zero files cannot establish an empty repository when this is true.
        languages:
          type: array
          items:
            type: string
        file_count:
          type: integer
          minimum: 0
        layout:
          $ref: '#/components/schemas/RepoLayout'
        generated_at:
          type: string
          format: date-time
    RepoLayout:
      x-go-generate: true
      type: object
      description: Orientation-tier layout sample — Files for tiny repos, TopLevel for small/medium.
      additionalProperties: false
      properties:
        files:
          type: array
          items:
            type: string
          description: Tiny tier — full repo-relative file paths.
        top_level:
          type: array
          items:
            type: string
          description: Small/medium tier — top-level dirs (trailing /) and promoted root files.
    ReadOutlineResponse:
      x-go-generate: true
      x-tool-output:
        - read
      type: object
      required:
        - path
        - mode
        - total_lines
      properties:
        path:
          type: string
        mode:
          type: string
        outline_kind:
          $ref: '#/components/schemas/OutlineKind'
        outline_source:
          type: string
        total_lines:
          type: integer
        log_digest:
          x-go-pointer: true
          $ref: '#/components/schemas/LogDigest'
        truncation_banner:
          type: string
    Error:
      x-go-generate: true
      x-go-name: ErrorResponse
      description: Every API error body. `code` identifies the condition and maps to exactly one HTTP status; clients branch on it, never on `message` or the status alone. The presentation fields are rendered from the host notice catalog.
      type: object
      required:
        - code
        - message
      additionalProperties: false
      properties:
        code:
          $ref: '#/components/schemas/ApiErrorCode'
          description: Machine-readable error code
        message:
          type: string
          description: User-facing message for this occurrence (rendered server-side)
        details:
          type: object
          additionalProperties: true
          description: Structured context for the code, such as `field` for `invalid_request`, `param` for `invalid_query`, or `errors` for `workflow_validation_failed`.
        title:
          type: string
          description: User-facing notice title (rendered server-side)
        retryable:
          type: boolean
          description: Catalog signal that the same operation may succeed when retried.
        suggested_action:
          type: string
          description: Hint for agent or user recovery
        actions:
          type: array
          items:
            $ref: '#/components/schemas/NoticeAction'
          description: Structured in-app destinations for the remedy; clients never infer them from copy.
        tier:
          $ref: '#/components/schemas/NoticeTier'
          description: Host-declared tier for the underlying notice; always `non_catastrophic` on an HTTP error body, which requires a request the user was able to make.
        scope:
          $ref: '#/components/schemas/NoticeScope'
          description: Host-declared scope for the underlying notice, so a failed request surfaces where it belongs rather than wherever the user happens to be.
        resolution:
          type: string
          description: Which declared resolution produced `tier`/`scope`.
    NoticeCopy:
      x-go-generate: true
      type: object
      required:
        - title
        - message
      additionalProperties: false
      description: Rendered user-notice copy for the accompanying catalog code.
      properties:
        title:
          type: string
        message:
          type: string
        suggested_action:
          type: string
        actions:
          type: array
          items:
            $ref: '#/components/schemas/NoticeAction'
    EventTopic:
      type: string
      enum:
        - session
        - message
        - worker
        - checkpoint
        - board
        - delegation
        - cost
        - llm
        - activity
        - oar
        - scan
        - providers
        - model_policy
        - settings
        - workflow
        - grounding
        - findings
        - progress
        - queue
        - project
        - process
        - preview
        - attention
        - cli_open
        - source_changed
        - source_operation
        - file_briefing
        - artifact
        - agent_presence
        - editor_document
        - preflight
        - turn_clock
        - turn_load
        - source_view
    EventEnvelope:
      description: Topic-discriminated EventHub SSE envelope.
      oneOf:
        - $ref: '#/components/schemas/EventEnvelopeSession'
        - $ref: '#/components/schemas/EventEnvelopeMessage'
        - $ref: '#/components/schemas/EventEnvelopeWorker'
        - $ref: '#/components/schemas/EventEnvelopeCheckpoint'
        - $ref: '#/components/schemas/EventEnvelopeBoard'
        - $ref: '#/components/schemas/EventEnvelopeDelegation'
        - $ref: '#/components/schemas/EventEnvelopeCost'
        - $ref: '#/components/schemas/EventEnvelopeLlm'
        - $ref: '#/components/schemas/EventEnvelopeActivity'
        - $ref: '#/components/schemas/EventEnvelopeOar'
        - $ref: '#/components/schemas/EventEnvelopeScan'
        - $ref: '#/components/schemas/EventEnvelopeProviders'
        - $ref: '#/components/schemas/EventEnvelopeModelPolicy'
        - $ref: '#/components/schemas/EventEnvelopeSettings'
        - $ref: '#/components/schemas/EventEnvelopeWorkflow'
        - $ref: '#/components/schemas/EventEnvelopeGrounding'
        - $ref: '#/components/schemas/EventEnvelopeFindings'
        - $ref: '#/components/schemas/EventEnvelopeProgress'
        - $ref: '#/components/schemas/EventEnvelopeQueue'
        - $ref: '#/components/schemas/EventEnvelopeProject'
        - $ref: '#/components/schemas/EventEnvelopeProcess'
        - $ref: '#/components/schemas/EventEnvelopePreview'
        - $ref: '#/components/schemas/EventEnvelopeAttention'
        - $ref: '#/components/schemas/EventEnvelopeCliOpen'
        - $ref: '#/components/schemas/EventEnvelopeSourceChanged'
        - $ref: '#/components/schemas/EventEnvelopeSourceOperation'
        - $ref: '#/components/schemas/EventEnvelopeFileBriefing'
        - $ref: '#/components/schemas/EventEnvelopeArtifact'
        - $ref: '#/components/schemas/EventEnvelopeAgentPresence'
        - $ref: '#/components/schemas/EventEnvelopeEditorDocument'
        - $ref: '#/components/schemas/EventEnvelopePreflight'
        - $ref: '#/components/schemas/EventEnvelopeTurnClock'
        - $ref: '#/components/schemas/EventEnvelopeTurnLoad'
        - $ref: '#/components/schemas/EventEnvelopeSourceView'
      discriminator:
        propertyName: topic
        mapping:
          session: '#/components/schemas/EventEnvelopeSession'
          message: '#/components/schemas/EventEnvelopeMessage'
          worker: '#/components/schemas/EventEnvelopeWorker'
          checkpoint: '#/components/schemas/EventEnvelopeCheckpoint'
          board: '#/components/schemas/EventEnvelopeBoard'
          delegation: '#/components/schemas/EventEnvelopeDelegation'
          cost: '#/components/schemas/EventEnvelopeCost'
          llm: '#/components/schemas/EventEnvelopeLlm'
          activity: '#/components/schemas/EventEnvelopeActivity'
          oar: '#/components/schemas/EventEnvelopeOar'
          scan: '#/components/schemas/EventEnvelopeScan'
          providers: '#/components/schemas/EventEnvelopeProviders'
          model_policy: '#/components/schemas/EventEnvelopeModelPolicy'
          settings: '#/components/schemas/EventEnvelopeSettings'
          workflow: '#/components/schemas/EventEnvelopeWorkflow'
          grounding: '#/components/schemas/EventEnvelopeGrounding'
          findings: '#/components/schemas/EventEnvelopeFindings'
          progress: '#/components/schemas/EventEnvelopeProgress'
          queue: '#/components/schemas/EventEnvelopeQueue'
          project: '#/components/schemas/EventEnvelopeProject'
          process: '#/components/schemas/EventEnvelopeProcess'
          preview: '#/components/schemas/EventEnvelopePreview'
          attention: '#/components/schemas/EventEnvelopeAttention'
          cli_open: '#/components/schemas/EventEnvelopeCliOpen'
          source_changed: '#/components/schemas/EventEnvelopeSourceChanged'
          source_operation: '#/components/schemas/EventEnvelopeSourceOperation'
          file_briefing: '#/components/schemas/EventEnvelopeFileBriefing'
          artifact: '#/components/schemas/EventEnvelopeArtifact'
          agent_presence: '#/components/schemas/EventEnvelopeAgentPresence'
          editor_document: '#/components/schemas/EventEnvelopeEditorDocument'
          preflight: '#/components/schemas/EventEnvelopePreflight'
          turn_clock: '#/components/schemas/EventEnvelopeTurnClock'
          turn_load: '#/components/schemas/EventEnvelopeTurnLoad'
          source_view: '#/components/schemas/EventEnvelopeSourceView'
    MessageEvent:
      x-go-generate: true
      type: object
      description: 'One transcript row change. message is always the full post-write row: apply it as a whole-row snapshot ordered by seq, never a field merge onto a cached row — an absent field means the row no longer has it.'
      required:
        - session_id
        - op
        - message
      additionalProperties: false
      properties:
        session_id:
          type: string
          format: uuid
        op:
          $ref: '#/components/schemas/MessageChangeOp'
        seq:
          type: integer
          format: int64
        message:
          $ref: '#/components/schemas/Message'
    WorkerEvent:
      x-go-generate: true
      type: object
      required:
        - worker_id
        - status
      additionalProperties: false
      properties:
        worker_id:
          type: string
          format: uuid
        status:
          $ref: '#/components/schemas/WorkerStatus'
        agent_type:
          type: string
        child_session_id:
          type: string
          format: uuid
        parent_session_id:
          type: string
          format: uuid
        brief:
          type: string
          description: Worker assignment goal used for card titles.
        merge_status:
          $ref: '#/components/schemas/WorkerMergeStatus'
        max_tool_loops:
          type: integer
          minimum: 0
        budget_request:
          x-go-pointer: true
          $ref: '#/components/schemas/WorkerBudgetRequest'
          description: The worker's unanswered request for more tool rounds; absent once granted
        tool_loops_used:
          type: integer
          minimum: 0
        tool_calls_used:
          type: integer
          minimum: 0
        turn_tool_calls:
          type: integer
          minimum: 0
        turn_tools_done:
          type: integer
          minimum: 0
        context_usage:
          x-go-pointer: true
          $ref: '#/components/schemas/WorkerContextUsage'
        workspace_preparation:
          x-go-pointer: true
          $ref: '#/components/schemas/WorkspacePreparation'
        result:
          x-go-pointer: true
          $ref: '#/components/schemas/WorkerResult'
        failure:
          x-go-pointer: true
          $ref: '#/components/schemas/WorkerFailure'
        error:
          type: string
    BoardEvent:
      x-go-generate: true
      type: object
      required:
        - project_id
        - snapshot
        - detail_level
      additionalProperties: false
      properties:
        project_id:
          type: string
          format: uuid
        session_id:
          type: string
          format: uuid
        snapshot:
          $ref: '#/components/schemas/BoardView'
        detail_level:
          $ref: '#/components/schemas/BoardDetailLevel'
    DelegationEvent:
      x-go-generate: true
      type: object
      required:
        - delegation_id
        - status
        - leg_id
        - phase
      additionalProperties: false
      properties:
        delegation_id:
          type: string
          format: uuid
        status:
          $ref: '#/components/schemas/DelegationStatus'
        leg_id:
          type: string
          format: uuid
        phase:
          $ref: '#/components/schemas/DelegationPhase'
    LLMTokenCounts:
      x-go-generate: true
      type: object
      additionalProperties: false
      properties:
        prompt:
          type: integer
          minimum: 0
        completion:
          type: integer
          minimum: 0
        total:
          type: integer
          minimum: 0
        context_window:
          type: integer
          minimum: 0
        compaction_threshold:
          type: integer
          minimum: 0
    CoordinatorLoopProgress:
      x-go-generate: true
      type: object
      additionalProperties: false
      properties:
        host_turn:
          type: boolean
        iteration:
          type: integer
          minimum: 0
          description: 1-based index of the model turn now running.
        max_iterations:
          type: integer
          minimum: 0
        prose_finish:
          type: boolean
          description: Tools-omitted closeout turn — reserved last iteration or early closeout (blocked loop, cancel, spend, timeout).
        surface:
          type: string
        activity_label:
          type: string
          description: Host-projected activity label from the coordinator surface catalog.
        guarded:
          type: boolean
        provisional_hidden:
          type: boolean
    LLMCallEvent:
      x-go-generate: true
      type: object
      required:
        - call_id
        - session_id
        - provider
        - model
        - status
        - tokens
      additionalProperties: false
      properties:
        call_id:
          type: string
          format: uuid
        session_id:
          description: Session whose coordinator turn this call belongs to. The event stream is project-scoped, so clients must attribute activity by this id rather than by whichever session they currently display.
          type: string
          format: uuid
        provider:
          type: string
        model:
          type: string
        status:
          $ref: '#/components/schemas/LLMCallStatus'
        tokens:
          $ref: '#/components/schemas/LLMTokenCounts'
        coordinator_loop:
          x-go-pointer: true
          $ref: '#/components/schemas/CoordinatorLoopProgress'
        attempt:
          type: integer
          description: 1-based index of the attempt now running. Present only while the call is being retried, so a rate-limited provider is distinguishable from a slow one; the first attempt omits it. Republished on the same call_id with status active.
        max_attempts:
          type: integer
          description: The initial request plus every retry the provider policy allows.
        retry_wait_ms:
          type: integer
          format: int64
          description: Backoff slept before this attempt, in milliseconds.
        retry_reason:
          $ref: '#/components/schemas/LLMRetryReason'
        silence_ms:
          type: integer
          format: int64
          description: 'How long the previous attempt went unanswered after its request was delivered, in milliseconds. Present only with retry_reason silent. This is the number that separates a slow model from a stalled one: both look identical while a turn is open, and only this says which.'
    ActivityEvent:
      x-go-generate: true
      type: object
      required:
        - activity_id
        - session_id
        - kind
        - status
        - started_at
      additionalProperties: false
      description: 'One session-scoped host activity lease. The active edge opens the lease and the done edge with the same activity_id closes it. Further active edges replace measured progress on that lease; arguments remain on the canonical transcript tool call. A lease outlives the turn that opened it: an awaiting_wake lease spans an idle session, so a client closes leases on their done edge or an authoritative session activity snapshot, never on a session status change.'
      properties:
        activity_id:
          type: string
          format: uuid
        session_id:
          type: string
          format: uuid
        kind:
          $ref: '#/components/schemas/ActivityKind'
        status:
          $ref: '#/components/schemas/ActivityStatus'
        started_at:
          type: string
          format: date-time
        tool_name:
          type: string
        tool_call_id:
          type: string
          pattern: ^[A-Za-z0-9_.:-]+$
        progress:
          x-go-pointer: true
          $ref: '#/components/schemas/ToolProgress'
        wait_triggers:
          type: array
          items:
            type: string
          description: Subscriptions that will end an awaiting_wake lease, in the host's own vocabulary (timer, next_worker_done, process_done, …). Present only on awaiting_wake, and what the client names rather than inferring a subject from the sleep reason.
        decision_trigger:
          $ref: '#/components/schemas/TurnLoadTrigger'
          description: What asked the local decision engine. Present only on deciding, which the host opens only while an available engine is answering; the answer arrives as a turn_load event.
    BackgroundProcessEvent:
      x-go-generate: true
      type: object
      required:
        - process_id
        - session_id
        - stream
        - end_offset
        - running
      additionalProperties: false
      properties:
        process_id:
          type: string
          pattern: ^[A-Za-z0-9_-]+$
        session_id:
          type: string
          format: uuid
        stream:
          type: string
        text:
          type: string
        end_offset:
          type: integer
          format: int64
          minimum: 0
          description: Byte offset just past the process output this event reflects; 0 for a held call, which has no byte stream.
        running:
          type: boolean
        exit_code:
          x-go-pointer: true
          type: integer
        truncated:
          type: boolean
        reset:
          type: boolean
          description: Replace previously projected text instead of appending it.
    BackgroundProcess:
      x-go-generate: true
      type: object
      required:
        - process_id
        - running
      additionalProperties: false
      properties:
        output:
          x-go-pointer: true
          $ref: '#/components/schemas/BackgroundProcessOutput'
        process_id:
          type: string
          pattern: ^[A-Za-z0-9_-]+$
        running:
          type: boolean
        stages:
          type: array
          items:
            $ref: '#/components/schemas/BackgroundProcessStage'
        exit_code:
          x-go-pointer: true
          type: integer
    BackgroundProcessStage:
      x-go-generate: true
      type: object
      required:
        - command
      additionalProperties: false
      properties:
        command:
          type: string
        exit_code:
          x-go-pointer: true
          type: integer
          description: Absent while the stage has not exited, and for a stage a sequencing operator skipped. A skipped stage reached no status, so reporting 0 would read as success.
        skipped:
          type: boolean
          description: 'The stage never ran: the preceding group''s exit status did not satisfy the connector joining them.'
        connector:
          type: string
          enum:
            - '|'
            - '&&'
            - '||'
            - ;
          description: Operator joining this stage to the previous one. Absent on the first stage. `|` wires the previous stage's stdout into this one; the rest gate on the previous group's exit status.
    BackgroundProcessChunk:
      x-go-generate: true
      type: object
      required:
        - offset
        - stream
        - text
      additionalProperties: false
      properties:
        offset:
          type: integer
          format: int64
          minimum: 0
          description: Byte offset of the first process output byte this chunk covers.
        stream:
          type: string
        text:
          type: string
    BackgroundProcessOutput:
      x-go-generate: true
      type: object
      description: 'The screened tail of one process''s retained output, bounded by the host. It is a snapshot, not a page: live output continues on the process event topic.'
      required:
        - process_id
        - chunks
        - running
      additionalProperties: false
      properties:
        process_id:
          type: string
          pattern: ^[A-Za-z0-9_-]+$
        chunks:
          type: array
          items:
            $ref: '#/components/schemas/BackgroundProcessChunk'
        running:
          type: boolean
        truncated:
          type: boolean
          description: Earlier output was evicted from the host buffer or cut from this snapshot.
        exit_code:
          x-go-pointer: true
          type: integer
    PreviewEvent:
      x-go-generate: true
      type: object
      required:
        - op
        - session_id
        - page_id
        - assistant_message_id
        - tool_call_id
        - seq
      additionalProperties: false
      description: Read-only live page mirror (CDP screencast frames + driven-action overlay). Frames ride the authenticated host SSE channel; Den never forwards input.
      properties:
        op:
          $ref: '#/components/schemas/PreviewEventOp'
        session_id:
          type: string
          format: uuid
        page_id:
          type: string
          pattern: ^[A-Za-z0-9_.:-]+$
        assistant_message_id:
          type: string
          format: uuid
          description: Assistant transcript row that originated this preview surface.
        tool_call_id:
          type: string
          pattern: ^[A-Za-z0-9_.:-]+$
          description: Tool invocation within the containing assistant row.
        parent_session_id:
          type: string
          format: uuid
        seq:
          type: integer
          format: int64
        mime:
          type: string
        jpeg_b64:
          type: string
        width:
          type: integer
        height:
          type: integer
        url:
          type: string
        title:
          type: string
        idle:
          x-go-pointer: true
          type: boolean
        action:
          x-go-pointer: true
          $ref: '#/components/schemas/PreviewActionOverlay'
    PreviewWatchRequest:
      x-go-generate: true
      type: object
      required:
        - watching
        - page_id
      additionalProperties: false
      properties:
        watching:
          type: boolean
        page_id:
          type: string
          pattern: ^[A-Za-z0-9_.:-]+$
    PreviewWatchResult:
      x-go-generate: true
      type: object
      required:
        - watching
        - page_id
      additionalProperties: false
      properties:
        watching:
          type: boolean
        page_id:
          type: string
          pattern: ^[A-Za-z0-9_.:-]+$
    BackgroundProcessStopResult:
      x-go-generate: true
      type: object
      required:
        - process_id
        - stop_requested
        - running
      additionalProperties: false
      properties:
        process_id:
          type: string
          pattern: ^[A-Za-z0-9_-]+$
        stop_requested:
          type: boolean
          description: The host requested termination; this does not assert process exit.
        running:
          type: boolean
          description: True until the host has observed terminal process state. Wait for the process event instead of repeating stop.
        exit_code:
          x-go-pointer: true
          type: integer
    ProviderEvent:
      x-go-generate: true
      type: object
      required:
        - provider_id
        - action
      additionalProperties: false
      properties:
        provider_id:
          type: string
          pattern: ^[a-zA-Z0-9_.-]+$
        action:
          type: string
    ModelPolicyEvent:
      x-go-generate: true
      type: object
      required:
        - scope
        - action
      additionalProperties: false
      properties:
        scope:
          type: string
        action:
          type: string
    SettingsEvent:
      x-go-generate: true
      type: object
      required:
        - area
        - scope
        - action
      additionalProperties: false
      properties:
        area:
          $ref: '#/components/schemas/SettingsArea'
        scope:
          $ref: '#/components/schemas/SettingsScope'
        action:
          type: string
    ArtifactEvent:
      x-go-generate: true
      type: object
      required:
        - artifact_id
        - project_id
        - op
      additionalProperties: false
      description: 'A durable visual artifact was written or deleted. Identity only: clients re-read the artifact list so one tile''s metadata, reference counts, and bytes all render from one place.'
      properties:
        artifact_id:
          type: string
          format: uuid
        project_id:
          type: string
          format: uuid
        session_id:
          type: string
          format: uuid
          description: Producing (holder) session, when one is known.
        root_session_id:
          type: string
          format: uuid
          description: Session tree that resolves the event, when one is known.
        op:
          $ref: '#/components/schemas/ArtifactChangeOp'
    ArtifactChangeOp:
      type: string
      enum:
        - written
        - deleted
    SourceChange:
      x-go-generate: true
      type: object
      required:
        - root_id
        - path
        - op
        - origin
        - changed_at
      additionalProperties: false
      description: One host-authoritative or external filesystem transition. Rename carries from_path; agent/user changes may carry attribution and after_sha256.
      properties:
        root_id:
          type: string
          format: uuid
        path:
          type: string
          description: Root-relative path after the change (destination for rename).
        from_path:
          type: string
          description: Root-relative path before a rename; omit for other ops.
        op:
          $ref: '#/components/schemas/SourceChangeOp'
        origin:
          $ref: '#/components/schemas/SourceChangeOrigin'
        is_dir:
          x-go-pointer: true
          type: boolean
          description: Entry kind after an upsert; omitted when unknown or deleted.
        session_id:
          type: string
          format: uuid
          description: Producing session attribution; never event-delivery scope.
        worker_id:
          type: string
          format: uuid
        turn:
          type: integer
          minimum: 0
        tool_call_id:
          type: string
          pattern: ^[A-Za-z0-9_.:-]+$
        after_sha256:
          type: string
          description: Hex SHA-256 of content after a host write; omit for external and delete.
        changed_at:
          type: string
          format: date-time
    SourceChangesEvent:
      x-go-generate: true
      type: object
      required:
        - project_id
        - workspace_id
        - workspace_kind
        - changes
        - resync
      additionalProperties: false
      description: A bounded set of filesystem transitions for one physical source workspace. Resync means the path set overflowed or exact transitions are unavailable; clients rehydrate their loaded projection.
      properties:
        project_id:
          type: string
          format: uuid
        workspace_id:
          type: string
          pattern: ^ws_[0-9a-f]{32}$
          minLength: 1
        workspace_kind:
          $ref: '#/components/schemas/SourceWorkspaceKind'
        changes:
          type: array
          maxItems: 512
          items:
            $ref: '#/components/schemas/SourceChange'
        resync:
          type: boolean
        git_changed:
          type: boolean
          description: A git ref surface moved in this window — possibly with no file changes at all (a commit). Consumers refresh git-derived comparisons even when changes is empty.
    EditorDocumentEvent:
      x-go-generate: true
      type: object
      additionalProperties: false
      description: Document invalidation and presentation metadata. Reconcile through state-vector synchronization; events never transport document text.
      required:
        - id
        - project_id
        - workspace_id
        - file_id
        - root_id
        - path
        - base_sha256
        - encoding
        - size_bytes
        - eol
        - base_eol
        - mixed_eol
        - base_mixed_eol
        - revision
        - dirty
        - diverged
        - secret_screen_status
        - content_changed
      properties:
        epoch:
          type: integer
          format: int64
          minimum: 1
        published_revision:
          type: integer
          format: int64
          minimum: 1
        participants:
          type: array
          items:
            $ref: '#/components/schemas/EditorParticipant'
        id:
          type: string
          format: uuid
        project_id:
          type: string
          format: uuid
        workspace_id:
          type: string
          pattern: ^ws_[0-9a-f]{32}$
        file_id:
          type: string
          format: uuid
        root_id:
          type: string
          format: uuid
        path:
          type: string
        base_sha256:
          type: string
        encoding:
          $ref: '#/components/schemas/SourceEncoding'
        size_bytes:
          type: integer
          format: int64
          minimum: 0
        eol:
          type: string
          enum:
            - lf
            - crlf
        base_eol:
          type: string
          enum:
            - lf
            - crlf
        mixed_eol:
          type: boolean
        base_mixed_eol:
          type: boolean
        revision:
          type: integer
          format: int64
          minimum: 1
        dirty:
          type: boolean
        diverged:
          type: boolean
        held_agent_version_id:
          x-go-pointer: true
          type: string
          format: uuid
          description: The retained state holding an agent edit this draft carries and disk does not. Omitted once a save settles the hold.
        secret_screen_status:
          $ref: '#/components/schemas/SecretScreenStatus'
        content_changed:
          type: boolean
          description: Whether the accepted text changed during this transition.
        secret_screen:
          x-go-pointer: true
          $ref: '#/components/schemas/SecretScreen'
    CLIOpenEvent:
      x-go-generate: true
      type: object
      required:
        - action
        - path
      additionalProperties: false
      description: 'A `lycaon open` invocation that the host resolved but did not enact. Device-scoped, so it reaches the window whatever project it is showing. `open` names a project that already exists and may be switched to directly — the human typing the command is the explicit action. `create` only proposes a root: minting a project is a durable act that needs trust, so Den confirms rather than the CLI deciding.'
      properties:
        action:
          $ref: '#/components/schemas/CLIOpenAction'
        project_id:
          type:
            - string
            - 'null'
          format: uuid
          description: Resolved project for `open`; null for `create`.
        path:
          type: string
          description: Absolute canonical path the CLI resolved. The proposed root for `create`; for `open`, the path that matched, which may be a subdirectory of the project root rather than the root itself.
    PreflightEvent:
      x-go-generate: true
      type: object
      required:
        - probe_id
      additionalProperties: false
      description: |-
        Host readiness changed while the app was running. Device-scoped: the report is about the machine, not one project, even when a probe's result names the projects it is about.
        Carries no verdict. A probe's copy, tier, and scope resolve together from the user-notice catalog when `GET /v1/preflight` runs, so re-reading is what keeps one condition described one way.
      properties:
        probe_id:
          type: string
          pattern: ^[a-z0-9_-]+$
          description: Probe whose verdict may have moved, e.g. `provider_configured`. A hint for clients that only care about some of them; the report is still the authority on every probe.
    CLIOpenAction:
      type: string
      enum:
        - open
        - create
    SourceChangeOp:
      type: string
      enum:
        - write
        - create
        - rename
        - delete
    SourceChangeOrigin:
      type: string
      enum:
        - agent
        - user
        - external
    SourceWorkspaceKind:
      type: string
      enum:
        - project
        - worker
    CostBreakdown:
      x-go-generate: true
      type: object
      required:
        - estimated_nano_usd
        - token_totals
      additionalProperties: false
      properties:
        cache_savings:
          x-go-pointer: true
          $ref: '#/components/schemas/CacheSavings'
        estimated_nano_usd:
          x-go-type: int64
          type: integer
        token_totals:
          $ref: '#/components/schemas/TokenTotals'
        task_count:
          type: integer
          minimum: 0
    CostEvent:
      x-go-generate: true
      type: object
      required:
        - scope
        - estimated_nano_usd
        - token_totals
        - coordinator
        - workers
        - summarizer
      additionalProperties: false
      properties:
        scope:
          $ref: '#/components/schemas/CostScope'
        session_id:
          type: string
          format: uuid
        estimated_nano_usd:
          x-go-type: int64
          type: integer
        token_totals:
          $ref: '#/components/schemas/TokenTotals'
        coordinator:
          $ref: '#/components/schemas/CostBreakdown'
        workers:
          $ref: '#/components/schemas/CostBreakdown'
        summarizer:
          $ref: '#/components/schemas/CostBreakdown'
          description: Summarizer utility calls (compaction, curate, commit draft, and similar).
    CostScope:
      type: string
      enum:
        - session
        - project
    CostSummary:
      x-go-generate: true
      type: object
      required:
        - scope
        - estimated_nano_usd
        - token_totals
        - coordinator
        - workers
        - summarizer
        - estimate_coverage
        - pricing_provenance
      additionalProperties: false
      properties:
        cache_savings:
          x-go-pointer: true
          $ref: '#/components/schemas/CacheSavings'
        scope:
          $ref: '#/components/schemas/CostScope'
        session_id:
          type: string
          format: uuid
        project_id:
          type: string
          format: uuid
        estimated_nano_usd:
          x-go-type: int64
          type: integer
          description: Estimated nano USD over the priced usage. Read it with estimate_coverage, which says whether it is the whole estimate, a lower bound, or nothing.
        token_totals:
          $ref: '#/components/schemas/TokenTotals'
        coordinator:
          $ref: '#/components/schemas/CostBreakdown'
        workers:
          $ref: '#/components/schemas/CostBreakdown'
        summarizer:
          $ref: '#/components/schemas/CostBreakdown'
          description: Summarizer utility calls (compaction, curate, commit draft, and similar).
        estimate_coverage:
          $ref: '#/components/schemas/CostEstimateCoverage'
        pricing_provenance:
          type: array
          description: Every pricing source represented in the estimate, sorted by source id.
          items:
            $ref: '#/components/schemas/CostPricingProvenance'
        unpriced_tokens:
          type: integer
          minimum: 0
          description: Prompt + completion tokens whose individual price was unavailable. Known buckets from the same call remain in the USD subtotal. One of the two causes of a lower_bound coverage; under unpriced coverage it is every incurred token.
        unknown_calls:
          type: integer
          minimum: 0
          description: Provider calls with missing usage that the host could not measure, or with an incomplete provider usage report. Calls that never reached generation are voided and never counted; calls that produced content without a usage report are measured by the host instead (see host_measured_tokens). Non-zero means the token counts are incomplete.
        unknown_charged_calls:
          type: integer
          minimum: 0
          description: The unknown_calls subset on a provider that can bill for tokens. The other cause of a lower_bound coverage; the remainder ran on local inference, where an unreported call costs tokens off the counts but no money.
        host_measured_tokens:
          type: integer
          minimum: 0
          description: 'Prompt + completion tokens the host counted itself for calls that ended without a provider usage report, the usual cause being a user interrupt mid-stream. Included in the totals and in estimated_nano_usd, and approximate: host tokenization, and no cache attribution.'
    ProjectCostReport:
      x-go-generate: true
      type: object
      required:
        - summary
        - project_utilities
        - retired_sessions
        - sessions
        - total
        - session_count
        - archived_session_count
        - worker_task_count
        - max_session_nano_usd
        - max_session_tokens
      additionalProperties: false
      properties:
        total:
          type: integer
          minimum: 0
        session_count:
          type: integer
          minimum: 0
        archived_session_count:
          type: integer
          minimum: 0
        worker_task_count:
          type: integer
          minimum: 0
        max_session_nano_usd:
          x-go-type: int64
          type: integer
          minimum: 0
        max_session_tokens:
          type: integer
          minimum: 0
        next_cursor:
          type: string
        summary:
          $ref: '#/components/schemas/CostSummary'
        project_utilities:
          x-go-type: CostSummary
          allOf:
            - $ref: '#/components/schemas/CostSummary'
          description: LLM utility usage attributed to the project but not to a session.
        retired_sessions:
          x-go-type: CostSummary
          allOf:
            - $ref: '#/components/schemas/CostSummary'
          description: Usage retained after its session was deleted or expired.
        sessions:
          type: array
          description: One ordered page of top-level project sessions, including archived sessions. Totals and maxima cover the full project; total is the number matching the search.
          items:
            $ref: '#/components/schemas/ProjectCostSession'
    ProjectCostSession:
      x-go-generate: true
      type: object
      required:
        - session
        - cost
      additionalProperties: false
      properties:
        session:
          $ref: '#/components/schemas/SessionSummary'
        cost:
          $ref: '#/components/schemas/CostSummary'
    DenPerfEvent:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - event
      properties:
        observed_at:
          x-go-type: string
          type: string
          format: date-time
          description: Den-side observation time (RFC 3339)
        channel:
          type: string
          description: Debug channel (always "perf" today)
        event:
          type: string
          minLength: 1
          description: Event name (loop-stall, longtask, …)
        detail:
          type: object
          additionalProperties: {}
          description: Optional fields (lag_ms, recent, dur_ms, …)
    DenPerfEventsRequest:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - events
      properties:
        events:
          type: array
          minItems: 1
          maxItems: 32
          items:
            $ref: '#/components/schemas/DenPerfEvent'
    SummarizeAnchor:
      type: object
      required:
        - path
        - line
        - excerpt
      properties:
        handle:
          type: string
          description: Minted summarize#N handle after tool completion
        path:
          type: string
        line:
          type: integer
        excerpt:
          type: string
          description: Verbatim line the anchor cites
    SummarizeNextAction:
      type: object
      required:
        - tool
        - args
        - kind
        - reason
      properties:
        tool:
          type: string
        args:
          type: object
          description: Arguments accepted directly by the named tool
          properties:
            path:
              type: string
            paths:
              type: array
              items:
                type: string
            task:
              type: string
            pattern:
              type: string
            cursor:
              type: string
            offset:
              type: integer
            limit:
              type: integer
        kind:
          type: string
          enum:
            - inspect
            - continue
        reason:
          type: string
    SummarizeResponse:
      x-tool-output:
        - summarize
      type: object
      required:
        - task
        - coverage
        - pack
      properties:
        task:
          type: string
        coverage:
          $ref: '#/components/schemas/SummarizeCoverage'
        pack:
          $ref: '#/components/schemas/SummarizePack'
        anchors:
          type: array
          items:
            $ref: '#/components/schemas/SummarizeAnchor'
        next_actions:
          type: array
          description: Concrete follow-up tool calls — what to do next
          items:
            $ref: '#/components/schemas/SummarizeNextAction'
        sources_touched:
          type: array
          items:
            type: string
        gather:
          type: object
          description: Gather-phase report (operator visibility)
          properties:
            mode:
              type: string
              description: inline | repo | hybrid
            path:
              type: string
            paths:
              type: array
              items:
                type: string
            pattern:
              type: string
            candidates:
              type: integer
            bytes:
              type: integer
            match_count:
              type: integer
              description: Pattern-matching lines examined when pattern is set
            sample_matches:
              type: array
              description: First grep hit lines when pattern is set — verbatim path/line/content so agents can verify RE2 matches before trusting the pack.
              items:
                type: object
                required:
                  - path
                  - line
                  - content
                properties:
                  path:
                    type: string
                  line:
                    type: integer
                  content:
                    type: string
                    description: Verbatim matched line (may be truncated when very long)
    TokenTotals:
      x-go-generate: true
      type: object
      required:
        - prompt
        - completion
      additionalProperties: false
      properties:
        cache_read:
          type: integer
          minimum: 0
          description: Reported cache-read input tokens, included in prompt.
        cache_write:
          type: integer
          minimum: 0
          description: Reported cache-write input tokens, included in prompt.
        prompt:
          type: integer
          minimum: 0
        completion:
          type: integer
          minimum: 0
    AbortDelegationRequest:
      x-go-generate: true
      type: object
      additionalProperties: false
      properties:
        reason:
          type: string
    CreateDelegationRequest:
      x-go-generate: true
      type: object
      required:
        - operation_id
        - project_id
        - task
      additionalProperties: false
      properties:
        operation_id:
          type: string
          format: uuid
          description: Idempotency key. Repeating a create with the same key and body answers the delegation the first create made; the same key with a different body is an idempotency_conflict.
        project_id:
          type: string
          format: uuid
        task:
          type: string
        strategy:
          $ref: '#/components/schemas/HuntStrategy'
          description: Decomposition strategy. The host uses file-based when the request omits it.
        inspect_mode:
          $ref: '#/components/schemas/InspectMode'
        workflow_id:
          type: string
          pattern: ^[a-z0-9_-]+$
        workflow_version:
          type: string
        workflow_run_id:
          type: string
          format: uuid
        blueprint_path:
          type: string
          description: Project-relative path under the overlay blueprints/ directory
    Delegation:
      x-go-generate: true
      type: object
      required:
        - id
        - project_id
        - task
        - strategy
        - status
        - created_at
      additionalProperties: false
      properties:
        id:
          type: string
          format: uuid
        project_id:
          type: string
          format: uuid
        workspace_root_id:
          type: string
          format: uuid
        workspace_path:
          type: string
        coordinator_session_id:
          type: string
          format: uuid
          description: Coordinator session created with the delegation (for prompts and grounding)
        task:
          type: string
        strategy:
          $ref: '#/components/schemas/HuntStrategy'
        inspect_mode:
          $ref: '#/components/schemas/InspectMode'
        workflow_id:
          type: string
          pattern: ^[a-z0-9_-]+$
        workflow_version:
          type: string
        workflow_run_id:
          type: string
          format: uuid
        blueprint_path:
          type: string
          description: Project-relative path under the overlay blueprints/ directory
        base_head_sha:
          type: string
        status:
          $ref: '#/components/schemas/DelegationStatus'
        reason:
          type: string
        phase:
          $ref: '#/components/schemas/DelegationPhase'
        legs:
          type: array
          items:
            $ref: '#/components/schemas/Leg'
        created_at:
          type: string
          format: date-time
    DelegationPhase:
      type: string
      enum:
        - setup
        - worker
        - closeout
        - done
    DispatchRequest:
      x-go-generate: true
      type: object
      additionalProperties: false
      properties:
        leg_id:
          type: string
          format: uuid
    HuntStrategy:
      type: string
      enum:
        - file-based
        - feature-based
        - risk-based
        - research-based
    InspectMode:
      type: string
      enum:
        - standard
        - turbo
        - full
    Leg:
      type: object
      required:
        - id
        - delegation_id
        - title
        - status
        - created_at
      additionalProperties: false
      properties:
        id:
          type: string
          format: uuid
        delegation_id:
          type: string
          format: uuid
        title:
          type: string
        agent_type:
          type: string
          description: Functional worker agent id stamped at dispatch
        status:
          $ref: '#/components/schemas/LegStatus'
        worker_id:
          type: string
          format: uuid
        parent_id:
          type: string
          format: uuid
        depends_on:
          type: array
          description: Leg ids that must complete before this leg can dispatch.
          items:
            type: string
            format: uuid
        files:
          type: array
          items:
            type: string
        prompt:
          type: string
        completion_criteria:
          type: array
          items:
            type: string
        workspace_id:
          type: string
          pattern: ^[a-zA-Z0-9_.:/-]+$
          description: Stable workspace binding id for scoped evidence
        result:
          $ref: '#/components/schemas/WorkerResult'
        created_at:
          type: string
          format: date-time
        started_at:
          type: string
          format: date-time
        completed_at:
          type: string
          format: date-time
    LegStatus:
      type: string
      enum:
        - pending
        - dispatched
        - running
        - retry_pending
        - complete
        - failed
        - held
        - canceled
    DelegationStatus:
      type: string
      enum:
        - active
        - done
        - failed
        - aborted
        - canceled
    LogDigest:
      x-go-generate: true
      type: object
      required:
        - format
        - record_count
        - parsed_count
        - truncated
      properties:
        format:
          $ref: '#/components/schemas/LogFormat'
        record_count:
          type: integer
        parsed_count:
          type: integer
        time_span:
          x-go-pointer: true
          $ref: '#/components/schemas/LogDigestTimeSpan'
        fields:
          type: array
          items:
            $ref: '#/components/schemas/LogDigestFieldStat'
        facets:
          type: array
          items:
            $ref: '#/components/schemas/LogDigestFacet'
        clusters:
          type: array
          items:
            $ref: '#/components/schemas/LogDigestCluster'
        truncated:
          type: boolean
    LogDigestCluster:
      x-go-generate: true
      type: object
      required:
        - template
        - count
        - first_line
        - last_line
      properties:
        template:
          type: string
        count:
          type: integer
        severity:
          type: string
        first_line:
          type: integer
        last_line:
          type: integer
    LogDigestFacet:
      x-go-generate: true
      type: object
      required:
        - key
        - values
      properties:
        key:
          type: string
        values:
          type: array
          items:
            $ref: '#/components/schemas/LogDigestFacetValue'
    LogDigestFacetValue:
      x-go-generate: true
      type: object
      required:
        - value
        - count
      properties:
        value:
          type: string
        count:
          type: integer
    LogDigestFieldStat:
      x-go-generate: true
      type: object
      required:
        - key
        - coverage_pct
      properties:
        key:
          type: string
        coverage_pct:
          x-go-name: Coverage
          type: integer
    LogDigestTimeSpan:
      x-go-generate: true
      type: object
      required:
        - start_at
        - end_at
      properties:
        start_at:
          x-go-type: string
          type: string
          format: date-time
        end_at:
          x-go-type: string
          type: string
          format: date-time
    LogFormat:
      type: string
      enum:
        - json_lines
        - logfmt
        - syslog_rfc5424
        - syslog_rfc3164
        - cef
        - leef
        - clf
        - combined
    LLMCallStatus:
      type: string
      enum:
        - active
        - ok
        - error
    LLMRetryReason:
      type: string
      description: 'Why a provider request is being reissued: status (throttle / transient 5xx), capacity (503/529), hold (turn-level wait), cooldown (slot), unreachable (the request never reached the provider), or silent (the provider took the request and sent no response headers), or empty_completion (the provider completed without assistant text or a tool call). The last three carry no HTTP status, so a client must not expect one alongside them.'
      enum:
        - status
        - capacity
        - hold
        - cooldown
        - unreachable
        - silent
        - empty_completion
    MessageChangeOp:
      type: string
      description: append adds a new transcript row; patch replaces the row with the same message id.
      enum:
        - append
        - patch
    AgentPool:
      type: object
      required:
        - selection
        - models
      additionalProperties: false
      properties:
        selection:
          $ref: '#/components/schemas/PoolSelection'
        models:
          type: array
          items:
            $ref: '#/components/schemas/ModelRef'
    PoolSelection:
      type: string
      enum:
        - round_robin
        - random
        - first
    CreateBlueprintRequest:
      x-go-generate: true
      type: object
      required:
        - title
      additionalProperties: false
      properties:
        title:
          type: string
        path:
          type: string
          description: Optional project-relative path under the overlay blueprints/ directory
        source_workflow_id:
          type: string
          pattern: ^[a-z0-9_-]+$
    Blueprint:
      x-go-generate: true
      type: object
      required:
        - id
        - project_id
        - title
        - path
        - content
        - status
        - version
        - updated_at
      additionalProperties: false
      properties:
        id:
          type: string
          format: uuid
        project_id:
          type: string
          format: uuid
        title:
          type: string
        path:
          type: string
          description: Project-relative path under the overlay blueprints/ directory
        source_workflow_id:
          type: string
          pattern: ^[a-z0-9_-]+$
        content:
          type: string
        status:
          $ref: '#/components/schemas/BlueprintStatus'
        version:
          type: integer
        compatible_workflows:
          type: array
          items:
            type: string
        updated_at:
          type: string
          format: date-time
          description: When the blueprint file was last written. A file carries no creation time.
    BlueprintApproveRequest:
      x-go-generate: true
      type: object
      required:
        - workflow_run_id
        - expected_revision
        - content_digest
      additionalProperties: false
      properties:
        note:
          type: string
        workflow_run_id:
          type: string
          format: uuid
        expected_revision:
          type: integer
          format: int64
          minimum: 1
        content_digest:
          type: string
          pattern: ^[a-f0-9]{64}$
    LaunchBlueprintRequest:
      x-go-generate: true
      type: object
      additionalProperties: false
      properties:
        target_workflow_id:
          type: string
          pattern: ^[a-z0-9_-]+$
        defer_start:
          type: boolean
          description: |
            When true, copy the source blueprint into a fresh draft and open a session without starting the workflow run. Den arms the catalog recipe; the next composer send starts the run bound to the seed.
    LaunchBlueprintResponse:
      x-go-generate: true
      type: object
      required:
        - session_id
        - blueprint_id
      additionalProperties: false
      properties:
        session_id:
          type: string
          format: uuid
        workflow_run_id:
          type: string
          format: uuid
          description: Omitted when defer_start seeded without starting a run.
        blueprint_id:
          type: string
          format: uuid
          description: UUID of the seeded blueprint copy.
    BlueprintStatus:
      type: string
      enum:
        - draft
        - approved
        - implementing
        - done
    BlueprintSummary:
      x-go-generate: true
      type: object
      required:
        - id
        - title
        - path
        - status
        - version
        - updated_at
      additionalProperties: false
      properties:
        id:
          type: string
          format: uuid
        title:
          type: string
        path:
          type: string
          description: Project-relative path under the overlay blueprints/ directory
        source_workflow_id:
          type: string
          pattern: ^[a-z0-9_-]+$
        status:
          $ref: '#/components/schemas/BlueprintStatus'
        version:
          type: integer
        compatible_workflows:
          type: array
          items:
            type: string
        updated_at:
          type: string
          format: date-time
    UpdateBlueprintRequest:
      x-go-generate: true
      type: object
      description: |
        Partial update — supply content and/or title (at least one). Title writes frontmatter. A provisional mint retargets onto the declared title once; `path` on the response is identity after that write.
      minProperties: 1
      additionalProperties: false
      properties:
        content:
          x-go-pointer: true
          type: string
        title:
          x-go-pointer: true
          type: string
          minLength: 1
          maxLength: 120
          description: Display title written to frontmatter; may retarget a provisional path once
    ProjectRoot:
      x-go-generate: true
      type: object
      required:
        - id
        - path
        - label
        - is_primary
        - added_at
        - kind
      additionalProperties: false
      properties:
        id:
          type: string
          format: uuid
        path:
          type: string
        label:
          type: string
          minLength: 1
          maxLength: 64
          description: Canonical display label and case-insensitive @label addressing token. Draft roots use the temporary label Draft; attached roots default to the folder basename.
        is_primary:
          type: boolean
        git_remote_hash:
          type:
            - string
            - 'null'
        added_at:
          type: string
          format: date-time
        kind:
          type: string
          enum:
            - draft
            - attached
          description: Engine-created draft workspace or user-attached folder.
    CreateProjectRootInput:
      x-go-generate: true
      type: object
      required:
        - path
      additionalProperties: false
      properties:
        path:
          type: string
        label:
          type: string
          maxLength: 64
          description: Display label doubling as the @label addressing token; unique per project (case-insensitive). Blank = derived from the folder basename.
        is_primary:
          x-go-pointer: true
          type: boolean
    CreateProjectRequest:
      x-go-generate: true
      type: object
      additionalProperties: false
      properties:
        name:
          x-go-pointer: true
          type: string
        roots:
          type: array
          items:
            $ref: '#/components/schemas/CreateProjectRootInput'
        draft:
          type: boolean
          description: Materialize as an unsaved draft until the user promotes it.
    UpdateProjectRequest:
      x-go-generate: true
      type: object
      additionalProperties: false
      properties:
        name:
          x-go-pointer: true
          type: string
          minLength: 1
        starred:
          x-go-pointer: true
          type: boolean
    AttachProjectRootRequest:
      x-go-generate: true
      type: object
      required:
        - path
      additionalProperties: false
      properties:
        path:
          type: string
        label:
          type: string
          maxLength: 64
          description: Display label doubling as the @label addressing token; unique per project (case-insensitive). Blank = derived from the folder basename.
        is_primary:
          x-go-pointer: true
          type: boolean
    ProjectEvent:
      x-go-generate: true
      type: object
      required:
        - id
        - action
      additionalProperties: false
      properties:
        id:
          type: string
          format: uuid
        action:
          $ref: '#/components/schemas/ProjectEventAction'
        project:
          x-go-pointer: true
          $ref: '#/components/schemas/Project'
    ProjectEventAction:
      type: string
      enum:
        - created
        - updated
        - deleted
    OutlineKind:
      type: string
      enum:
        - symbols
        - log_digest
    Project:
      x-go-generate: true
      x-go-field-order:
        - id
        - name
        - roots
        - roots_generation
        - session_count
        - starred
        - is_draft
        - promotion
        - cover_artifact_id
        - cover_root_session_id
        - cover_source
        - cover_updated_at
        - last_activity_at
        - last_opened_at
        - created_at
      type: object
      required:
        - id
        - name
        - roots
        - roots_generation
        - session_count
        - starred
        - is_draft
        - promotion
        - last_opened_at
        - created_at
      additionalProperties: false
      properties:
        id:
          type: string
          format: uuid
        name:
          type:
            - string
            - 'null'
        roots:
          type: array
          items:
            $ref: '#/components/schemas/ProjectRoot'
        roots_generation:
          type: integer
          minimum: 0
        session_count:
          type: integer
          minimum: 0
          description: Number of retained root chats, including archived chats and excluding worker sessions.
        starred:
          type: boolean
        is_draft:
          type: boolean
          description: Computed from the project having exactly one primary draft root.
        promotion:
          x-go-type: '*ProjectPromotion'
          oneOf:
            - $ref: '#/components/schemas/ProjectPromotion'
            - type: 'null'
          description: Durable save-to-folder progress, or null when no transition is active.
        last_activity_at:
          type:
            - string
            - 'null'
          format: date-time
          description: Newest activity_at among the project's sessions; null when it has none.
        last_opened_at:
          type: string
          format: date-time
          description: 'When the person last opened the project: created a chat in it, or had one of its chats on screen (markSessionSeen). Fetching a session does not open its project.'
        created_at:
          type: string
          format: date-time
        cover_artifact_id:
          type:
            - string
            - 'null'
          format: uuid
          description: Product-render VisualArtifact id for the home-page project card (capture or render).
        cover_root_session_id:
          type:
            - string
            - 'null'
          format: uuid
          description: Root session id for fetching cover bytes via GET /v1/sessions/{id}/artifacts/{artifact_id}.
        cover_source:
          type:
            - string
            - 'null'
          enum:
            - capture
            - render
            - null
          description: Producer face of the designated cover artifact.
        cover_updated_at:
          type:
            - string
            - 'null'
          format: date-time
          description: When the product-render cover was last designated.
    FileBriefingRequest:
      x-go-generate: true
      type: object
      required:
        - root_id
        - path
        - presentation
        - trigger
      additionalProperties: false
      description: Exact file revision to brief; presentation fields are mutually exclusive.
      properties:
        root_id:
          type: string
          format: uuid
        path:
          type: string
        presentation:
          type: string
          enum:
            - current
            - document
            - version
        trigger:
          type: string
          enum:
            - automatic
            - manual
        worker_id:
          type: string
          format: uuid
        document_id:
          type: string
          format: uuid
        document_revision:
          type: integer
          minimum: 1
        version_id:
          type: string
          format: uuid
          description: Required for version; the host validates the project, root, and path association.
    FileBriefingPreview:
      x-go-generate: true
      type: object
      required:
        - line_count
      additionalProperties: false
      description: Deterministic file facts available before AI generation completes.
      properties:
        language:
          type: string
        line_count:
          type: integer
          minimum: 0
    FileBriefingLocation:
      x-go-generate: true
      type: object
      required:
        - line
        - name
      additionalProperties: false
      description: Host-derived declaration location used for exact inline code navigation.
      properties:
        line:
          type: integer
          minimum: 1
        name:
          type: string
          minLength: 1
        kind:
          type: string
    FileBriefingSection:
      x-go-generate: true
      type: object
      required:
        - kind
        - text
      additionalProperties: false
      properties:
        kind:
          type: string
          enum:
            - purpose
            - structure
            - key_behavior
        text:
          type: string
          maxLength: 8192
    FileBriefingResponse:
      x-go-generate: true
      type: object
      required:
        - target_key
        - attempt_id
        - root_id
        - path
        - presentation
        - source_sha256
        - status
        - preview
        - locations
        - sections
        - fallback_text
        - truncated
        - updated_at
      additionalProperties: false
      properties:
        target_key:
          type: string
        attempt_id:
          type: string
          format: uuid
        root_id:
          type: string
          format: uuid
        path:
          type: string
        presentation:
          type: string
          enum:
            - current
            - document
            - version
        source_sha256:
          type: string
        status:
          type: string
          enum:
            - preview
            - pending
            - complete
            - failed
        preview:
          $ref: '#/components/schemas/FileBriefingPreview'
        locations:
          type: array
          description: Bounded declaration inventory supplied to the summarizer; exact unique names may navigate inline.
          items:
            $ref: '#/components/schemas/FileBriefingLocation'
        sections:
          type: array
          items:
            $ref: '#/components/schemas/FileBriefingSection'
        fallback_text:
          type: string
          maxLength: 8192
          description: Bounded terminal explanation when structured sections are unavailable.
        truncated:
          type: boolean
        error:
          type: string
        updated_at:
          x-go-type: string
          type: string
          format: date-time
    FileBriefingEvent:
      x-go-generate: true
      type: object
      required:
        - project_id
        - target_key
        - attempt_id
        - root_id
        - path
        - presentation
        - source_sha256
        - status
        - fallback_text
        - truncated
        - updated_at
      additionalProperties: false
      description: Revision-keyed briefing update with delta, result, or error.
      properties:
        project_id:
          type: string
          format: uuid
        target_key:
          type: string
        attempt_id:
          type: string
          format: uuid
        root_id:
          type: string
          format: uuid
        path:
          type: string
        presentation:
          type: string
          enum:
            - current
            - document
            - version
        source_sha256:
          type: string
        status:
          type: string
          enum:
            - preview
            - pending
            - streaming
            - complete
            - failed
        preview:
          x-go-pointer: true
          $ref: '#/components/schemas/FileBriefingPreview'
        locations:
          type: array
          items:
            $ref: '#/components/schemas/FileBriefingLocation'
        sections:
          type: array
          items:
            $ref: '#/components/schemas/FileBriefingSection'
        fallback_text:
          type: string
          maxLength: 8192
        delta:
          type: string
          maxLength: 8192
        truncated:
          type: boolean
        error:
          type: string
        updated_at:
          x-go-type: string
          type: string
          format: date-time
    SourcePin:
      x-go-generate: true
      type: object
      required:
        - id
        - project_id
        - created_at
      additionalProperties: false
      description: Named source comparison boundary.
      properties:
        id:
          type: string
          format: uuid
        project_id:
          type: string
          format: uuid
        label:
          type: string
        created_at:
          x-go-name: CreatedAt
          type: string
          format: date-time
        git_heads:
          type: array
          items:
            $ref: '#/components/schemas/SourcePinGitHead'
    SourcePinList:
      x-go-generate: true
      type: object
      required:
        - pins
      additionalProperties: false
      properties:
        pins:
          type: array
          items:
            $ref: '#/components/schemas/SourcePin'
        next_cursor:
          type: string
    SourcePinCreate:
      x-go-generate: true
      type: object
      additionalProperties: false
      properties:
        label:
          type: string
          maxLength: 120
    SourcePinUpdate:
      x-go-generate: true
      type: object
      additionalProperties: false
      properties:
        label:
          type:
            - string
            - 'null'
          maxLength: 120
    CheckpointEvent:
      x-go-generate: true
      type: object
      required:
        - id
        - session_id
        - kind
        - status
        - issued_at
      additionalProperties: false
      properties:
        id:
          type: string
          format: uuid
        session_id:
          type: string
          format: uuid
        kind:
          $ref: '#/components/schemas/CheckpointKind'
        status:
          $ref: '#/components/schemas/CheckpointStatus'
        issued_at:
          type: string
          format: date-time
        tool_approval:
          x-go-pointer: true
          $ref: '#/components/schemas/ToolApprovalPayload'
        content_apply:
          x-go-pointer: true
          $ref: '#/components/schemas/ContentApplyPayload'
    CheckpointKind:
      type: string
      enum:
        - tool_approval
        - content_apply
    CheckpointResponse:
      x-go-generate: true
      type: object
      required:
        - id
        - session_id
        - kind
        - status
      additionalProperties: false
      properties:
        id:
          type: string
          format: uuid
        session_id:
          type: string
          format: uuid
        kind:
          $ref: '#/components/schemas/CheckpointKind'
        status:
          $ref: '#/components/schemas/CheckpointStatus'
        result:
          type: object
          additionalProperties: true
        resolved_at:
          x-go-pointer: true
          type: string
          format: date-time
    CheckpointStatus:
      type: string
      enum:
        - pending
        - approved
        - rejected
        - expired
        - canceled
    ContentApplyDecision:
      type: string
      enum:
        - approve
        - reject
        - approve_partial
      description: |
        approve applies the complete host-authored proposal; approve_partial selects host-authored hunk ids for host composition; reject applies nothing. Clients never submit replacement bytes.
    ApprovalDecisionAction:
      type: string
      enum:
        - approve
        - reject
    ContentApplyHunk:
      x-go-generate: true
      type: object
      required:
        - id
        - path
        - before
        - after
      additionalProperties: false
      properties:
        id:
          type: string
          pattern: ^[A-Za-z0-9_.:-]+$
        path:
          type: string
        before:
          type: string
        after:
          type: string
    ContentApplyPayload:
      x-go-generate: true
      type: object
      required:
        - tool
        - path
        - after
        - hunks
      additionalProperties: false
      properties:
        tool:
          type: string
        tool_call_id:
          type: string
          pattern: ^[A-Za-z0-9_.:-]+$
        path:
          type: string
        before:
          x-go-pointer: true
          type: string
        after:
          type: string
        hunks:
          type: array
          minItems: 1
          description: |
            Immutable host-authored selectable edits. Hunk ids are the only client-selectable input to partial composition.
          items:
            $ref: '#/components/schemas/ContentApplyHunk'
    ContentApplyResolveBody:
      type: object
      required:
        - kind
        - decision
      additionalProperties: false
      properties:
        kind:
          type: string
          const: content_apply
        decision:
          $ref: '#/components/schemas/ContentApplyDecision'
        approved_hunks:
          type: array
          minItems: 1
          uniqueItems: true
          description: |
            Host-authored ids from content_apply.hunks. Required only with approve_partial; the host composes final bytes from the stored plan.
          items:
            type: string
            pattern: ^[A-Za-z0-9_.:-]+$
        guidance:
          type: string
          description: |
            Explicit human direction from the composer describing what the agent should do instead. Only valid with decision reject. This does not authorize an alternative edit; it is returned to the agent with the denial.
        secrets:
          type: array
          maxItems: 32
          description: Managed secrets explicitly attached to rejecting guidance.
          items:
            $ref: '#/components/schemas/PromptSecretReferencePart'
    CodeScan:
      type: object
      required:
        - id
        - categories
        - status
        - findings_count
        - created_at
        - long_running
      additionalProperties: false
      properties:
        id:
          type: string
          format: uuid
        detail_pruned_at:
          type: string
          format: date-time
          description: Historical result bodies were removed by an explicit retention policy at this time.
        canonical_path:
          type: string
        categories:
          type: array
          items:
            $ref: '#/components/schemas/ScanCategory'
        scanner_id:
          type: string
          pattern: ^[a-zA-Z0-9_-]+$
        runtime:
          $ref: '#/components/schemas/ScanRuntimePolicy'
        started_at:
          type: string
          format: date-time
        long_running_at:
          type: string
          format: date-time
        long_running:
          type: boolean
          description: True after the scanner's soft runtime limit elapses; execution continues.
        status:
          $ref: '#/components/schemas/CodeScanStatus'
        assessment_id:
          type: string
          format: uuid
        finding_set_id:
          type: string
          format: uuid
        target_kind:
          $ref: '#/components/schemas/ScanTargetKind'
        target_paths:
          type: array
          items:
            type: string
        deleted_paths:
          type: array
          items:
            type: string
        coverage_status:
          $ref: '#/components/schemas/ScanCoverageStatus'
        failure_code:
          type: string
        execution_fingerprint:
          type: string
        fingerprint_scheme:
          type: string
        execution_manifest:
          $ref: '#/components/schemas/ScanExecutionManifest'
        source_capture_quality:
          type: string
        source_admission_mode:
          type: string
          description: How much of the tree the source generation observed. `scope` means every directory the scope admitted was read; directories the excludes floor, the project's declarations, or an ignore file left out are policy, not cost. `scope_bounded` means a walk budget stopped the observation short of the scope, so coverage is partial and the generation names the unobserved folders.
        progress:
          $ref: '#/components/schemas/ScanProgress'
        delta:
          $ref: '#/components/schemas/ScanDelta'
        findings_count:
          type: integer
          minimum: 0
        guidance:
          type: array
          items:
            $ref: '#/components/schemas/ScanGuidanceSummary'
        findings:
          type: array
          items:
            $ref: '#/components/schemas/SecurityFinding'
        findings_stored:
          type: integer
          minimum: 0
        findings_merged:
          type: integer
          minimum: 0
          description: 'Findings folded into another row at ingest because they share an advisory. With findings_stored and the suppressed list this accounts for every reported finding: count = stored + suppressed + merged.'
        findings_by_level:
          type: object
          additionalProperties:
            type: integer
            minimum: 0
        findings_by_kind:
          type: object
          additionalProperties:
            type: integer
            minimum: 0
        unmapped_count:
          type: integer
          minimum: 0
        top_locations:
          type: array
          maxItems: 5
          items:
            $ref: '#/components/schemas/BoardScanTopLocation'
        agent_budget:
          $ref: '#/components/schemas/ScanAgentBudget'
        scan_scope:
          type: string
        supported_query_filters:
          type: array
          items:
            type: string
        ignored:
          type: array
          items:
            $ref: '#/components/schemas/ScanIgnoredFinding'
        warnings:
          type: array
          items:
            $ref: '#/components/schemas/ScanWarning'
        warning_summary:
          type: array
          description: Counts of distinct diagnostics by kind; individual warnings are available in the full view.
          items:
            $ref: '#/components/schemas/ScanWarningSummary'
        result:
          type: object
          additionalProperties: true
        delegation_id:
          type: string
          format: uuid
        workflow_run_id:
          type: string
          format: uuid
        session_id:
          type: string
          format: uuid
          description: |
            Session that asked for this scan. A scan inside a workflow carries both stamps; each backs the report scope that addresses it.
        head_sha:
          type: string
        source_snapshot_id:
          type: string
          pattern: ^[a-zA-Z0-9_-]+$
          description: |
            Immutable content snapshot scanned by this run. HEAD is provenance only. The sentinel value "warming" means the host is still publishing the snapshot; the row is listable but not claimable by the runner yet.
        replacement_scan_id:
          type: string
          format: uuid
          description: Winning run when this pending scan lost an equivalent enqueue race.
        trigger:
          $ref: '#/components/schemas/ScanTrigger'
        error:
          type: string
        created_at:
          type: string
          format: date-time
        completed_at:
          type: string
          format: date-time
    CodeScanEvent:
      x-go-generate: true
      x-go-field-order:
        - scan_id
        - assessment_id
        - categories
        - status
        - coverage_status
        - failure_code
        - findings_count
        - error
        - guidance
        - runtime
        - progress
        - started_at
        - long_running_at
        - long_running
      type: object
      required:
        - scan_id
        - categories
        - status
        - findings_count
        - long_running
      additionalProperties: false
      properties:
        scan_id:
          type: string
          format: uuid
        categories:
          type: array
          items:
            $ref: '#/components/schemas/ScanCategory'
        status:
          $ref: '#/components/schemas/CodeScanStatus'
        assessment_id:
          type: string
          format: uuid
        coverage_status:
          $ref: '#/components/schemas/ScanCoverageStatus'
        failure_code:
          type: string
        findings_count:
          type: integer
          minimum: 0
        error:
          type: string
        guidance:
          type: array
          items:
            $ref: '#/components/schemas/ScanGuidanceSummary'
        runtime:
          x-go-pointer: true
          $ref: '#/components/schemas/ScanRuntimePolicy'
        progress:
          x-go-pointer: true
          $ref: '#/components/schemas/ScanProgress'
        started_at:
          x-go-pointer: true
          type: string
          format: date-time
        long_running_at:
          x-go-pointer: true
          type: string
          format: date-time
        long_running:
          type: boolean
    CodeScanStatus:
      type: string
      enum:
        - pending
        - running
        - complete
        - failed
        - timed_out
        - canceled
        - superseded
    ScanCategory:
      type: string
      enum:
        - security
        - sast
        - sca
        - container
        - secret
        - lint
        - types
        - style
        - license
        - custom
    FindingLevel:
      type: string
      enum:
        - critical
        - high
        - medium
        - low
        - info
        - unknown
    FindingKind:
      type: string
      enum:
        - sast
        - secret
        - sca
        - container
        - iac
        - license
        - custom
    FindingDisposition:
      type: string
      enum:
        - fix
        - fp
        - defer
        - accepted_risk
        - human
    SecurityFinding:
      x-go-generate: true
      x-go-field-order:
        - rule_id
        - level
        - message
        - locations
        - dataflow
        - fingerprints
        - tool
        - properties
        - history
      type: object
      required:
        - rule_id
        - level
        - message
        - locations
        - fingerprints
        - tool
      additionalProperties: false
      properties:
        rule_id:
          type: string
          pattern: ^[a-zA-Z0-9_.:/-]+$
        level:
          $ref: '#/components/schemas/FindingLevel'
        message:
          type: string
        locations:
          type: array
          minItems: 1
          items:
            $ref: '#/components/schemas/SecurityFindingLocation'
        fingerprints:
          $ref: '#/components/schemas/SecurityFindingFingerprints'
        dataflow:
          x-go-pointer: true
          $ref: '#/components/schemas/SecurityFindingDataflow'
        tool:
          $ref: '#/components/schemas/ToolDescriptor'
        properties:
          x-go-pointer: true
          $ref: '#/components/schemas/SecurityFindingProperties'
        history:
          x-go-pointer: true
          $ref: '#/components/schemas/SecurityFindingHistory'
    SecurityFindingLocation:
      x-go-generate: true
      type: object
      required:
        - uri
      additionalProperties: false
      properties:
        uri:
          type: string
        start_line:
          type: integer
          minimum: 0
        start_column:
          type: integer
          minimum: 0
        end_line:
          type: integer
          minimum: 0
        end_column:
          type: integer
          minimum: 0
    SecurityFindingFingerprints:
      x-go-generate: true
      type: object
      required:
        - primary
      additionalProperties: false
      properties:
        primary:
          type: string
    ToolDescriptor:
      x-go-generate: true
      type: object
      required:
        - driver_id
        - name
      additionalProperties: false
      properties:
        driver_id:
          type: string
          pattern: ^[a-zA-Z0-9_-]+$
        name:
          type: string
        version:
          type: string
    AdvisoryKind:
      type: string
      enum:
        - vulnerability
        - malicious_package
    AdvisoryRef:
      x-go-generate: true
      type: object
      additionalProperties: false
      description: One vulnerability under every id it is published as. osv_id is the canonical id chosen from the whole alias set (a CVE when one is known, else a GHSA, else the first ecosystem database id); the family lists carry every id, canonical included.
      properties:
        osv_id:
          x-go-name: OSVID
          type: string
          pattern: ^[a-zA-Z0-9_.:/-]+$
        kind:
          $ref: '#/components/schemas/AdvisoryKind'
        cve_ids:
          x-go-name: CVEIDs
          type: array
          items:
            type: string
            pattern: ^[a-zA-Z0-9_.:/-]+$
        ghsa_ids:
          x-go-name: GHSAIDs
          type: array
          items:
            type: string
            pattern: ^[a-zA-Z0-9_.:/-]+$
        aliases:
          description: Ecosystem advisory database ids for the same vulnerability (GO-, PYSEC-, RUSTSEC-, MAL-, ...).
          type: array
          items:
            type: string
        package:
          x-go-pointer: true
          $ref: '#/components/schemas/AdvisoryPackageRef'
        severity_source:
          type: string
        cvss:
          x-go-name: CVSS
          type: array
          items:
            $ref: '#/components/schemas/AdvisoryCVSS'
        fixed_versions:
          type: array
          items:
            type: string
        urls:
          x-go-name: URLs
          type: array
          items:
            type: string
    AdvisoryCVSS:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - type
        - vector
      properties:
        type:
          type: string
        vector:
          type: string
        score:
          x-go-pointer: true
          type: number
          minimum: 0
          maximum: 10
    AdvisoryPackageRef:
      x-go-generate: true
      type: object
      additionalProperties: false
      properties:
        name:
          type: string
        version:
          type: string
        ecosystem:
          type: string
    SecurityFindingLycaonProperties:
      x-go-generate: true
      type: object
      additionalProperties: false
      properties:
        kind:
          $ref: '#/components/schemas/FindingKind'
        categories:
          type: array
          items:
            $ref: '#/components/schemas/ScanCategory'
        advisory:
          x-go-pointer: true
          $ref: '#/components/schemas/AdvisoryRef'
        hint_code:
          type: string
        disposition:
          $ref: '#/components/schemas/FindingDisposition'
        sources:
          type: array
          items:
            type: string
    SecurityFindingProperties:
      x-go-generate: true
      type: object
      additionalProperties: false
      properties:
        lycaon:
          x-go-pointer: true
          $ref: '#/components/schemas/SecurityFindingLycaonProperties'
    ScanAgentBudget:
      x-go-generate: true
      type: object
      additionalProperties: false
      properties:
        min_severity:
          type: string
        max_hints_per_injection:
          type: integer
          minimum: 0
        dedupe_by:
          type: string
    BoardScanTopLocation:
      x-go-generate: true
      type: object
      required:
        - uri
      additionalProperties: false
      properties:
        uri:
          type: string
        start_line:
          type: integer
          minimum: 0
    ScanWarning:
      x-go-generate: true
      type: object
      required:
        - kind
      additionalProperties: false
      properties:
        kind:
          $ref: '#/components/schemas/ScanWarningKind'
        rule_id:
          type: string
          pattern: ^[a-zA-Z0-9_.:/-]+$
        file:
          type: string
        message:
          type: string
        construct:
          type: string
          description: Structured construct code from the engine or source parser; never inferred from diagnostic text.
        start_line:
          type: integer
          minimum: 1
        start_column:
          type: integer
          minimum: 1
    ScanWarningKind:
      type: string
      description: 'Analysis diagnostics. target_unscanned is a coverage gap: the engine failed on a target (timeout, out of memory, an internal error) so that target was never analyzed, and its absence from the findings proves nothing. source_moved is a coverage gap: the file changed while the engine read the tree, so its findings describe neither the generation the scan is bound to nor the tree as it is now.'
      enum:
        - rule_parse_error
        - file_partial_parse
        - file_partial_semantics
        - target_unscanned
        - source_moved
    ScanGuidanceSummary:
      x-go-generate: true
      type: object
      required:
        - code
        - message
      additionalProperties: false
      properties:
        code:
          type: string
        message:
          type: string
        fix:
          type: string
        severity:
          type: string
        rule_id:
          type: string
          pattern: ^[a-zA-Z0-9_.:/-]+$
        file:
          type: string
        line:
          type: integer
        count:
          type: integer
          minimum: 1
    ScanQueryRequest:
      x-go-generate: true
      type: object
      additionalProperties: false
      properties:
        level:
          type: string
          description: Normalized finding level (critical, high, medium, low, info).
        kind:
          type: string
          description: properties.lycaon.kind (sast, sca, secret, container, custom).
        advisory_id:
          type: string
          pattern: ^[a-zA-Z0-9_.:/-]+$
          description: OSV, CVE, or GHSA identifier.
        fingerprint:
          type: string
          description: Exact fingerprints.primary match.
        rule_id:
          type: string
          pattern: ^[a-zA-Z0-9_.:/-]+$
        path:
          type: string
          description: Location uri prefix.
        code:
          type: string
          description: Hint code (properties.lycaon.hint_code).
        cursor:
          type: string
          description: Opaque pagination cursor.
        limit:
          type: integer
          minimum: 1
          maximum: 1000
        dedupe:
          type: boolean
        introduced_since_at:
          x-go-pointer: true
          type: string
          format: date-time
          description: Only findings this scanner series first observed at or after this time.
        fixed_since_at:
          x-go-pointer: true
          type: string
          format: date-time
          description: Also return findings this series recorded as fixed at or after this time, in fixed_findings.
    ScanQueryResponse:
      x-go-generate: true
      type: object
      required:
        - scan_id
        - guidance
        - total_match
      additionalProperties: false
      properties:
        scan_id:
          type: string
          format: uuid
        findings:
          type: array
          items:
            $ref: '#/components/schemas/SecurityFinding'
        guidance:
          type: array
          items:
            $ref: '#/components/schemas/ScanGuidanceSummary'
        total_match:
          type: integer
          minimum: 0
        truncated:
          type: boolean
        next_cursor:
          type: string
        fixed_findings:
          type: array
          description: Findings fixed since the requested time, as they were last observed.
          items:
            $ref: '#/components/schemas/SecurityFinding'
    ScanDelta:
      x-go-generate: true
      type: object
      description: What a path-scoped scan changed relative to the generation its scanner last covered. Each changed file was read as it was in that base and as it is now; findings were matched by identity across the two, so introduced and fixed describe the change itself rather than the file.
      required:
        - base_snapshot_id
        - introduced
        - fixed
        - persisted
      additionalProperties: false
      properties:
        base_snapshot_id:
          type: string
          pattern: ^[a-zA-Z0-9_-]+$
        introduced:
          type: integer
          minimum: 0
        fixed:
          type: integer
          minimum: 0
        persisted:
          type: integer
          minimum: 0
    SecurityFindingHistory:
      x-go-generate: true
      type: object
      description: The generation that first showed this finding to its scanner series. A full pass that re-observes a known finding keeps the history its first observation gave it.
      required:
        - introduced_at
      additionalProperties: false
      properties:
        introduced_at:
          type: string
          format: date-time
        introduced_snapshot_id:
          type: string
          pattern: ^[a-zA-Z0-9_-]+$
        introduced_scan_id:
          type: string
          format: uuid
    FullScanRequest:
      x-go-generate: true
      type: object
      description: A complete pass of every admitted file by the named scanners, or by every selected scanner when none are named. The only way a full pass starts; automatic scanning is incremental.
      additionalProperties: false
      properties:
        kind:
          type: string
          enum:
            - full
        scanner_ids:
          x-go-name: ScannerIDs
          type: array
          items:
            type: string
            pattern: ^[a-zA-Z0-9_-]+$
        session_id:
          type: string
          format: uuid
    FullScanResponse:
      x-go-generate: true
      type: object
      description: The pass each project root owes, as it stands after the request, and the Security stage overview that already includes it. A request joins an unfinished pass that covers its scanners, widens one that has not started, and otherwise records a new pass.
      required:
        - passes
        - overview
      additionalProperties: false
      properties:
        passes:
          type: array
          items:
            $ref: '#/components/schemas/SecurityFullPass'
        overview:
          $ref: '#/components/schemas/SecurityOverview'
    SecurityBaseline:
      x-go-generate: true
      type: object
      description: The generation automatic scanning measures change against.
      required:
        - snapshot_id
        - created_at
        - file_count
        - unobserved_directories
      additionalProperties: false
      properties:
        snapshot_id:
          type: string
          pattern: ^[a-zA-Z0-9_-]+$
        created_at:
          type: string
          format: date-time
        file_count:
          type: integer
          minimum: 0
        unobserved_directories:
          type: integer
          minimum: 0
    SecurityFullPass:
      x-go-generate: true
      type: object
      description: One requested full pass. Every member reads every admitted file of the same source generation, so members start together once each scanner has finished earlier work. The pass is finished when no member is waiting and every started member's scan is terminal.
      required:
        - assessment_id
        - requested_at
        - members
      additionalProperties: false
      properties:
        assessment_id:
          type: string
          format: uuid
          description: The pass id, which is also its security assessment id once it starts.
        requested_at:
          type: string
          format: date-time
        started_at:
          x-go-pointer: true
          type: string
          format: date-time
          description: When the members were dispatched together; absent while any member waits.
        completed_at:
          x-go-pointer: true
          type: string
          format: date-time
        coverage_status:
          $ref: '#/components/schemas/ScanCoverageStatus'
        trigger:
          $ref: '#/components/schemas/ScanTrigger'
        members:
          type: array
          items:
            $ref: '#/components/schemas/SecurityFullPassMember'
    SecurityScannerState:
      x-go-generate: true
      type: object
      required:
        - id
        - label
        - categories
        - available
        - running
        - watching
        - pass_superseded
      additionalProperties: false
      properties:
        id:
          type: string
          pattern: ^[a-zA-Z0-9_-]+$
        label:
          type: string
        categories:
          type: array
          items:
            $ref: '#/components/schemas/ScanCategory'
        available:
          type: boolean
        unavailable_reason:
          x-go-name: Unavailable
          type: string
        last_full_at:
          x-go-pointer: true
          type: string
          format: date-time
        running:
          type: boolean
        watching:
          type: boolean
          description: 'True once the scanner holds a series for this project, so admitted writes arm it. Automatic scanning is change-driven: an empty delta produces no scan record, so an absence of recent runs under a watching scanner means the tree was quiet, not that scanning lapsed.'
        last_completed_at:
          x-go-pointer: true
          type: string
          format: date-time
          description: When this scanner's series last finished a scan of any target.
        pass_superseded:
          type: boolean
          description: 'True when the execution identity the series last covered is not the one this scanner would run now — a rule or engine change since its results were established. Requires positive evidence of drift: a series that has covered nothing has no pass to invalidate, and an identity that cannot be resolved is never reported as a change.'
    SecurityOverview:
      x-go-generate: true
      type: object
      description: 'What the Security stage states above its findings: what change is measured against, whether a full pass exists, and what is running now.'
      required:
        - project_id
        - enabled
        - scanners
        - introduced_since_baseline
        - fixed_since_baseline
      additionalProperties: false
      properties:
        project_id:
          type: string
          format: uuid
        enabled:
          type: boolean
        coverage_status:
          x-go-name: Coverage
          $ref: '#/components/schemas/ScanCoverageStatus'
        baseline:
          x-go-pointer: true
          $ref: '#/components/schemas/SecurityBaseline'
        last_full:
          x-go-pointer: true
          $ref: '#/components/schemas/SecurityFullPass'
        running:
          x-go-pointer: true
          $ref: '#/components/schemas/SecurityFullPass'
        scanners:
          type: array
          items:
            $ref: '#/components/schemas/SecurityScannerState'
        introduced_since_baseline:
          type: integer
          minimum: 0
        fixed_since_baseline:
          type: integer
          minimum: 0
    SearchRequest:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - query
      properties:
        query:
          type: string
        origin_project_id:
          type: string
          format: uuid
        cursor:
          type: string
        limit:
          type: integer
          minimum: 1
          maximum: 500
        budget:
          $ref: '#/components/schemas/SearchBudget'
        regex:
          type: boolean
          description: When true, treat the code pattern as a Go regexp
        case_sensitive:
          type: boolean
        whole_word:
          type: boolean
        include:
          type: array
          items:
            type: string
          description: Root-relative globs that must match (`**/*.go`)
        exclude:
          type: array
          items:
            type: string
          description: Root-relative globs that exclude a path (wins over include)
    SearchResponse:
      x-go-generate: true
      type: object
      required:
        - hits
        - status
        - exhaustive
        - total_hits
        - count_relation
        - facets_exhaustive
      properties:
        hits:
          type: array
          items:
            $ref: '#/components/schemas/SearchHit'
        status:
          $ref: '#/components/schemas/SearchResultStatus'
        exhaustive:
          type: boolean
          description: True only when every executor completed without a limit or error.
        total_hits:
          type: integer
          minimum: 0
          description: Hits available in this generation before transport paging.
        count_relation:
          $ref: '#/components/schemas/SearchCountRelation'
        facets_exhaustive:
          type: boolean
          description: False when facets describe a limited or partial generation.
        facets:
          type: array
          items:
            $ref: '#/components/schemas/SearchFacet'
        histogram:
          type: array
          items:
            $ref: '#/components/schemas/SearchHistogramBucket'
        next_cursor:
          type: string
        interpreted:
          $ref: '#/components/schemas/SearchInterpretation'
        issues:
          type: array
          items:
            $ref: '#/components/schemas/SearchIssue'
    SearchHit:
      x-go-generate: true
      type: object
      required:
        - hit_id
        - hit_kind
        - source
        - project_id
        - title
      properties:
        hit_id:
          type: string
          format: uuid
          description: Stable identity for the same indexed row or source coordinate across pages and refreshed generations.
        hit_kind:
          type: string
        source:
          type: string
        score:
          type: number
        session_id:
          type: string
          format: uuid
        source_ref:
          type: string
        leg_id:
          type: string
          format: uuid
        handle:
          type: string
        parent_session_id:
          type: string
          format: uuid
        worker_id:
          type: string
          format: uuid
        project_id:
          type: string
          format: uuid
        root_id:
          type: string
          format: uuid
          description: Attached root for live file and code hits, when known.
        project_name:
          type: string
        snippet:
          type: string
        title:
          type: string
          minLength: 1
          description: Scannable host-projected label.
        context:
          type: string
          description: Host-projected location or source context.
        path:
          type: string
          description: Repo-relative file path for file-anchored hits.
        line:
          type: integer
          description: 1-based line within path, when known.
        url:
          type: string
          format: uri
          description: External URL for web hits.
        trust:
          type: string
        verified:
          x-go-pointer: true
          type: boolean
        hint_code:
          type: string
        symbol_kind:
          $ref: '#/components/schemas/SourceSymbolKind'
        title_highlights:
          type: array
          description: Code point ranges of `title` the query matched; symbol hits carry them.
          items:
            $ref: '#/components/schemas/SourceSearchHighlight'
        created_at:
          type: string
          format: date-time
    SearchFacet:
      x-go-generate: true
      type: object
      required:
        - key
        - values
      properties:
        key:
          type: string
        values:
          type: array
          items:
            $ref: '#/components/schemas/SearchFacetValue'
    SearchFacetValue:
      x-go-generate: true
      type: object
      required:
        - value
        - count
      properties:
        value:
          type: string
        count:
          type: integer
    SearchHistogramBucket:
      x-go-generate: true
      type: object
      required:
        - key
        - count
      properties:
        key:
          type: string
        count:
          type: integer
    SearchResultStatus:
      type: string
      enum:
        - complete
        - limited
        - partial
    SearchCountRelation:
      type: string
      enum:
        - exact
        - lower_bound
    SearchIssueReason:
      type: string
      description: |
        Why a generation is not exhaustive. `time_budget`: the code leg's
        wall-clock budget ended with files unscanned. `catalog_warming`: `count`
        project roots had no source generation at all in time; their files
        are absent and the build they started continues, so the same query
        can include them when ready. `catalog_incomplete`: `count` project
        roots answered from a generation whose discovery has not reached the
        whole tree; the hits are real and more arrive as it continues.
        `catalog_bounded`: a walk budget refused `count` directories, so their
        files were never indexed and no query reaches them; unlike the warming
        reasons this does not resolve by asking again. `index_warming`: a direct content scan
        reached its time budget while the shared content index continues building.
        `catalog_failed`: count directories could not be discovered.
        `catalog_refresh_failed`: count readable roots failed to refresh.
        `catalog_refreshing`: count roots serve an older generation while updating.
        `symbol_budget`: declaration discovery in `count` projects stopped at
        its line cap, clock, or file budget, so more declarations may match.
      enum:
        - result_limit
        - executor_error
        - files_skipped
        - time_budget
        - catalog_warming
        - catalog_incomplete
        - catalog_bounded
        - catalog_failed
        - catalog_refresh_failed
        - catalog_refreshing
        - index_warming
        - symbol_budget
    SearchIssue:
      x-go-generate: true
      type: object
      required:
        - executor
        - reason
      properties:
        executor:
          type: string
        reason:
          $ref: '#/components/schemas/SearchIssueReason'
        limit:
          type: integer
          minimum: 1
        count:
          type: integer
          minimum: 1
          description: Affected-item count for reasons that carry one (files_skipped, catalog_warming, catalog_incomplete, catalog_bounded, index_warming, symbol_budget, and result_limit from the symbol leg)
        message:
          type: string
          description: Underlying failure for executor_error
    SearchInterpretation:
      x-go-generate: true
      type: object
      required:
        - scope
      properties:
        scope:
          $ref: '#/components/schemas/SearchScopeMode'
        slug:
          type: string
        fts_terms:
          x-go-name: FTSTerms
          type: array
          items:
            type: string
        filters:
          type: array
          items:
            $ref: '#/components/schemas/SearchInterpretedFilter'
        dependency_trees_excluded:
          type: boolean
          description: The content scan skipped dependency and build trees; an explicit path filter or include glob re-admits them.
    SearchScopeMode:
      type: string
      enum:
        - global
        - current
        - slug
    SearchExportFormat:
      type: string
      enum:
        - jsonl
        - csv
        - sarif
    SearchExportRequest:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - query
        - format
      description: Carries the same match flags as SearchRequest so an export reproduces exactly what the search view showed.
      properties:
        query:
          type: string
        origin_project_id:
          type: string
          format: uuid
        format:
          $ref: '#/components/schemas/SearchExportFormat'
        regex:
          type: boolean
        case_sensitive:
          type: boolean
        whole_word:
          type: boolean
        include:
          type: array
          items:
            type: string
        exclude:
          type: array
          items:
            type: string
    ScanIgnoredFinding:
      x-go-generate: true
      type: object
      description: A finding one of the project's ignore entries covered. The finding stays in the scan's set; this says which decision kept it out of the agent's view, and why.
      required:
        - reason
      additionalProperties: false
      properties:
        fingerprint:
          type: string
        rule_id:
          type: string
          pattern: ^[a-zA-Z0-9_.:/-]+$
        file:
          type: string
        entry_id:
          type: string
          pattern: ^[a-zA-Z0-9_-]+$
        matched_on:
          type: string
          description: The predicates the entry decided on, in the ignore file's own vocabulary, so a reader can find the entry without opening it.
        reason:
          type: string
        expires_on:
          type: string
          format: date
    ScanTrigger:
      type: string
      enum:
        - landed_change
        - phase_enter
        - manual
        - scan_pack
        - write_burst
        - authority_refresh
    ScannerSummary:
      x-go-generate: true
      type: object
      required:
        - id
        - driver
        - engine
        - scope_kind
        - categories
        - enabled
        - check_ok
        - catalog_source
        - runtime
      additionalProperties: false
      properties:
        id:
          type: string
          pattern: ^[a-zA-Z0-9_-]+$
        driver:
          type: string
          description: library | bundled | external
        engine:
          type: string
        scope_kind:
          type: string
          enum:
            - source_host_floor
            - source_driver
            - dependencies
            - secrets
            - container
            - custom
        categories:
          type: array
          items:
            $ref: '#/components/schemas/ScanCategory'
        enabled:
          type: boolean
          description: True when this scanner currently occupies its category slot.
        check_ok:
          x-go-name: CheckOK
          type: boolean
        binary_found:
          type: boolean
        requires_binary:
          type: string
          description: Optional display label for the external binary
        output_parser:
          type: string
        catalog_source:
          type: string
          enum:
            - host
            - user
            - project
        issues:
          type: array
          items:
            type: string
        command_summary:
          type: string
          description: Argv joined without expanding secrets; never env values
        label:
          type: string
        description:
          type: string
        mapper_id:
          type: string
          pattern: ^[a-zA-Z0-9_-]+$
        runtime:
          $ref: '#/components/schemas/ScanRuntimePolicy'
    SelectScannerSlotRequest:
      x-go-generate: true
      type: object
      required:
        - scanner_id
      additionalProperties: false
      properties:
        scanner_id:
          type: string
          pattern: ^[a-zA-Z0-9_-]+$
          description: Empty clears the slot
    ScannerCatalogEntry:
      x-go-generate: true
      type: object
      required:
        - id
        - label
        - binary
        - categories
        - added
        - binary_found
      additionalProperties: false
      properties:
        id:
          type: string
          pattern: ^[a-zA-Z0-9_-]+$
        label:
          type: string
        binary:
          type: string
          description: The CLI the user must install; the host ships no scanner binaries
        categories:
          type: array
          items:
            $ref: '#/components/schemas/ScanCategory'
        hint:
          type: string
        install:
          type: string
          description: Command that installs the binary, e.g. brew install trivy
        docs_url:
          type: string
        env:
          type: array
          items:
            type: string
          description: Env var names the tool needs (never values)
        command_summary:
          type: string
          description: Argv joined without expanding tokens
        added:
          type: boolean
          description: Already present in the user scanner catalog
        binary_found:
          type: boolean
          description: PATH hit; the install check is the authoritative probe
    ScannerCatalogResponse:
      x-go-generate: true
      type: object
      required:
        - scanners
      additionalProperties: false
      properties:
        scanners:
          type: array
          items:
            $ref: '#/components/schemas/ScannerCatalogEntry'
    ScannerCheckRow:
      x-go-generate: true
      type: object
      required:
        - scanner_id
        - ok
        - binary_found
      additionalProperties: false
      properties:
        scanner_id:
          type: string
          pattern: ^[a-zA-Z0-9_-]+$
        ok:
          x-go-name: OK
          type: boolean
        detail:
          type: string
          description: Tool version line on success, or the failure reason
        binary_found:
          type: boolean
          description: Separates not-installed from installed-but-broken
    ScannerCheckResponse:
      x-go-generate: true
      type: object
      required:
        - rows
      additionalProperties: false
      properties:
        rows:
          type: array
          items:
            $ref: '#/components/schemas/ScannerCheckRow'
    ScannerListResponse:
      x-go-generate: true
      type: object
      required:
        - scanners
        - rejected
      additionalProperties: false
      properties:
        scanners:
          type: array
          items:
            $ref: '#/components/schemas/ScannerSummary'
        rejected:
          type: object
          additionalProperties:
            $ref: '#/components/schemas/ScannerRejectedRow'
          description: |
            Rows of the user or project scanners.yaml the merge refused, keyed by scanner id; a row that named no id is keyed by the empty string. A refused row changed nothing.
        user_scanners_path:
          type: string
          description: Device path hint for scanners.yaml (no secrets)
    ScannerRejectedRow:
      x-go-generate: true
      type: object
      required:
        - code
      additionalProperties: false
      properties:
        id:
          type: string
          pattern: ^[a-zA-Z0-9_.-]+$
          description: The scanner id the row named. Absent when the row omitted one.
        code:
          type: string
          description: |
            Why the row was refused, from the closed set shared with detection packs. Clients branch on this rather than on detail.
        detail:
          type: string
    CreateCatalogScannerRequest:
      x-go-generate: true
      type: object
      required:
        - source
        - catalog_id
      additionalProperties: false
      properties:
        source:
          type: string
          enum:
            - catalog
        catalog_id:
          type: string
          pattern: ^[a-zA-Z0-9_-]+$
    CreateCustomScannerRequest:
      x-go-generate: true
      type: object
      required:
        - source
        - id
        - engine
        - scope_kind
        - command
        - output_parser
        - categories
        - runtime
      additionalProperties: false
      properties:
        source:
          type: string
          enum:
            - custom
        id:
          type: string
          pattern: ^[a-zA-Z0-9_-]+$
        engine:
          type: string
        scope_kind:
          type: string
          enum:
            - source_host_floor
            - source_driver
            - dependencies
            - secrets
            - container
            - custom
        command:
          type: array
          items:
            type: string
          minItems: 1
        output_parser:
          type: string
        mapper_id:
          type: string
          pattern: ^[a-zA-Z0-9_-]+$
        categories:
          type: array
          items:
            type: string
          minItems: 1
        label:
          type: string
        description:
          type: string
        skip_if_binary_missing:
          x-go-pointer: true
          type: boolean
        runtime:
          x-go-pointer: true
          $ref: '#/components/schemas/ScanRuntimePolicy'
        env:
          type: array
          items:
            type: string
          description: Env var names only (never values)
    CreateScannerRequest:
      oneOf:
        - $ref: '#/components/schemas/CreateCatalogScannerRequest'
        - $ref: '#/components/schemas/CreateCustomScannerRequest'
    UpdateScannerRequest:
      x-go-generate: true
      type: object
      additionalProperties: false
      properties:
        engine:
          type: string
        scope_kind:
          type: string
          enum:
            - source_host_floor
            - source_driver
            - dependencies
            - secrets
            - container
            - custom
        command:
          type: array
          items:
            type: string
        output_parser:
          type: string
        mapper_id:
          type: string
          pattern: ^[a-zA-Z0-9_-]+$
        categories:
          type: array
          items:
            type: string
        label:
          type: string
        description:
          type: string
        skip_if_binary_missing:
          x-go-pointer: true
          type: boolean
        runtime:
          x-go-pointer: true
          $ref: '#/components/schemas/ScanRuntimePolicy'
        env:
          type: array
          items:
            type: string
        driver:
          type: string
          description: When replacing a host row with a full user external, must be external
    UpdateProjectScannerRequest:
      x-go-generate: true
      type: object
      additionalProperties: false
      minProperties: 1
      properties:
        enabled:
          x-go-pointer: true
          type: boolean
    CompactedChunkMeta:
      x-go-generate: true
      type: object
      required:
        - original_tokens
        - compacted_tokens
        - kind
        - strategy
        - compacted_at
      additionalProperties: false
      properties:
        original_tokens:
          type: integer
          minimum: 0
        compacted_tokens:
          type: integer
          minimum: 0
        kind:
          type: string
          enum:
            - user_paste
            - tool_result
            - assistant_output
            - unknown
        strategy:
          type: string
          enum:
            - truncate
            - summarize
        compacted_at:
          type: string
          format: date-time
    CreateSessionRequest:
      x-go-generate: true
      type: object
      required:
        - project_id
        - posture
      additionalProperties: false
      dependentRequired:
        provider_id:
          - model
        model:
          - provider_id
      properties:
        project_id:
          type: string
          format: uuid
        workspace_root_id:
          type: string
          format: uuid
          description: Active root for this session; defaults to primary when omitted
        posture:
          $ref: '#/components/schemas/SessionPosture'
        provider_id:
          type: string
          pattern: ^[a-z0-9_-]{1,64}$
          description: Optional coordinator provider override for this session
        model:
          type: string
          minLength: 1
          description: Optional coordinator model override for this session
    Message:
      type: object
      required:
        - id
        - role
        - content
        - origin
        - authority
        - trust_tier
        - created_at
      additionalProperties: false
      properties:
        id:
          type: string
          format: uuid
        role:
          $ref: '#/components/schemas/MessageRole'
        content:
          type: string
        origin:
          $ref: '#/components/schemas/MessageOrigin'
        authority:
          $ref: '#/components/schemas/ContentAuthority'
        trust_tier:
          $ref: '#/components/schemas/ContentTrustTier'
        author_person_id:
          type: string
          format: uuid
          description: Person who wrote this message. Present on prompts a person sent; absent on host, model, tool, and coordinator-authored rows.
        content_parts:
          type: array
          description: |
            Ordered mixed-origin text parts used for model projection. The content field remains the flattened transcript/search projection.
          items:
            $ref: '#/components/schemas/MessageContentPart'
        host_secret_redaction:
          $ref: '#/components/schemas/HostSecretRedactionMeta'
        kind:
          $ref: '#/components/schemas/MessageKind'
        host_signal_id:
          type: string
          pattern: ^[A-Za-z0-9_.:-]+$
          description: Canonical catalog anchor id or structured guidance code for a host-authored row.
        worker_id:
          type: string
          format: uuid
          description: Immutable worker run associated with this child-session message; child sessions may be reused by later runs.
        workflow_run_id:
          type: string
          format: uuid
        workflow_boundary:
          $ref: '#/components/schemas/WorkflowBoundaryMeta'
        progress_complete:
          $ref: '#/components/schemas/ProgressCompleteMeta'
        progress_update:
          $ref: '#/components/schemas/ProgressUpdateMeta'
        workflow_feedback:
          $ref: '#/components/schemas/WorkflowFeedbackMeta'
        workflow_explain:
          $ref: '#/components/schemas/WorkflowExplainMeta'
        blueprint:
          $ref: '#/components/schemas/BlueprintMeta'
        completion_report:
          $ref: '#/components/schemas/CompletionReportMeta'
        index_warming:
          $ref: '#/components/schemas/IndexWarmingMeta'
        tool_calls:
          type: array
          items:
            $ref: '#/components/schemas/ToolCall'
        tool_result:
          $ref: '#/components/schemas/ToolResult'
        worker_summary:
          $ref: '#/components/schemas/WorkerSummaryMeta'
        grounding:
          $ref: '#/components/schemas/CitationGrounding'
        source_context:
          $ref: '#/components/schemas/SourceContext'
        navigation_refs:
          type: array
          maxItems: 128
          description: |
            Host-validated interaction targets mentioned by this message. Navigation references are independent of citation grounding and never support factual claims.
          items:
            $ref: '#/components/schemas/NavigationReference'
        compacted_chunk:
          $ref: '#/components/schemas/CompactedChunkMeta'
        compaction_checkpoint:
          type: boolean
        visibility:
          $ref: '#/components/schemas/MessageVisibility'
        prompt_cache_breakpoint:
          $ref: '#/components/schemas/PromptCacheTier'
        context_pinned:
          type: boolean
          description: Host-only prompt-assembly mark on a row the session's identity depends on (a worker leg's charter); the deterministic context fit refuses to evict it at any budget. Stamped per turn, never persisted, never accepted as input
        seq:
          type: integer
          format: int64
          description: Per-session transcript mutation clock. Stamped on append and durable UpdateMessage; live stream projections do not advance it. Clients reject stale rows by comparing seq.
        ord:
          type: integer
          format: int64
          description: Per-session immutable creation ordinal stamped once at append and never rewritten by a patch; the transcript renders in ord order. Distinct from seq (mutation clock) and created_at (display timestamp, not monotonic across batches)
        created_at:
          type: string
          format: date-time
        draft_version_count:
          type: integer
          minimum: 0
          description: Sidecar superseded attempts plus the live or committed body
        draft_status:
          $ref: '#/components/schemas/DraftStatus'
        status:
          $ref: '#/components/schemas/MessageLiveStatus'
        generating_tokens:
          type: integer
          description: Live completion-token estimate (visible content + hidden reasoning) for the currently streaming message. Runtime-derived and stamped at SSE time, not persisted; only meaningful while status is streaming.
        artifact_ids:
          type: array
          items:
            type: string
            format: uuid
          description: Coordinator present references into the session-tree visual artifact store (reference-only, never bytes)
        evidence_handles:
          type: array
          items:
            type: string
          description: Evidence handles produced when this tool result was committed (diet-retention exempt)
        diet_stamp:
          type: string
          description: Host diet strategy override stamped at commit (e.g. preserve_structure)
        diet_stamp_source:
          type: string
          enum:
            - overlay_merge
            - worker_envelope
          description: Disambiguates preserve_structure diet stamp producers
    MessageKind:
      type: string
      enum:
        - workflow_boundary
        - progress_complete
        - progress_update
        - workflow_feedback
        - workflow_explain
        - draft
        - index_warming
        - superseded
        - blueprint
        - agent_note
        - completion_report
        - iteration_cap_closeout
        - host_kick
        - coordinator_guidance
        - host_nudge
        - host_loop_wake
        - host_secret_redaction_notice
        - user_continuation
    MessageRole:
      type: string
      enum:
        - user
        - assistant
        - tool
        - system
    MessageLiveStatus:
      type: string
      enum:
        - streaming
        - complete
      description: Runtime-derived streaming affordance stamped at query/SSE time; not persisted
    DraftStatus:
      type: string
      enum:
        - live
        - committed
        - withdrawn
        - rejected
    DraftVersion:
      x-go-generate: true
      type: object
      required:
        - version_index
        - body
        - outcome_code
        - created_at
      additionalProperties: false
      properties:
        version_index:
          type: integer
          minimum: 0
        body:
          type: string
        outcome_code:
          type: string
          description: Structured reject Code for superseded attempts; empty for committed/final snapshots
        outcome_label:
          type: string
          description: Host-resolved short UI label for outcome_code (hint catalog); empty when code is empty — Den may fall back to Superseded
        created_at:
          type: string
          format: date-time
    DraftVersionsResponse:
      x-go-generate: true
      type: object
      required:
        - versions
      additionalProperties: false
      properties:
        versions:
          type: array
          items:
            $ref: '#/components/schemas/DraftVersion'
    PromptRequest:
      x-go-generate: true
      type: object
      required:
        - operation_id
        - text
      additionalProperties: false
      properties:
        operation_id:
          type: string
          format: uuid
          description: Stable client mutation identity. An exact retry replays the original submission; reusing this id with different input is rejected with 409 idempotency_conflict.
        text:
          type: string
          description: User prose; may be empty when attachments or references are present, or when the active workflow accepts an empty request
        attachments:
          type: array
          maxItems: 8
          description: |
            Handles for bodies already streamed to the attachment upload route — text, documents, and rasters alike. No bytes, no URLs, no paths.
          items:
            $ref: '#/components/schemas/PromptAttachmentPart'
        references:
          type: array
          maxItems: 64
          description: |
            On-disk, artifact, and search-hit references; no bytes. Counted separately from uploaded attachments.
          items:
            $ref: '#/components/schemas/PromptReferencePart'
        secrets:
          type: array
          maxItems: 32
          description: Typed managed-secret selections. Raw token syntax in text is rejected.
          items:
            $ref: '#/components/schemas/PromptSecretReferencePart'
    PromptAttachmentPart:
      x-go-generate: true
      type: object
      description: |
        Names an attachment the client already streamed to the upload route. Bodies never ride the prompt envelope, so this carries no bytes: the host re-derives filename and media type from the stored body, and the blob id is the only client-supplied value.
      required:
        - blob_id
      additionalProperties: false
      properties:
        blob_id:
          type: string
          pattern: ^[0-9a-f]{64}$
          description: Stable blob id returned by uploadAttachment
    PromptSecretReferencePart:
      x-go-generate: true
      type: object
      description: A typed selection of a managed-secret reference. It never carries protected bytes.
      required:
        - reference
      additionalProperties: false
      properties:
        reference:
          type: string
          pattern: ^\{\{paintedwolf-secret:[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\}\}$
          description: Value-free managed-secret reference returned by the host.
    CreateComposerSecretRequest:
      x-go-generate: true
      type: object
      description: Protects selected draft bytes without storing them in composer state.
      required:
        - name
        - secret_value
        - operation_id
      additionalProperties: false
      properties:
        name:
          type: string
          minLength: 1
          maxLength: 80
        purpose:
          type: string
          maxLength: 240
        secret_value:
          type: string
          minLength: 4
          writeOnly: true
          description: Protected value redacted from request capture. The floor is the length the outbound screen can recognize; the host refuses a shorter selection rather than hand back a reference that protects nothing.
        operation_id:
          type: string
          format: uuid
    AttachmentUploadResponse:
      x-go-generate: true
      type: object
      description: Receipt for one materialized attachment body.
      required:
        - blob_id
        - filename
        - mime
        - kind
        - bytes
      additionalProperties: false
      properties:
        blob_id:
          type: string
          pattern: ^[0-9a-f]{64}$
          description: Stable sha256 id over the stored body and sanitized filename
        filename:
          type: string
          description: Sanitized leaf name the host stored the body under
        mime:
          type: string
          description: Media type the host detected from the stored bytes
        kind:
          $ref: '#/components/schemas/AttachmentKind'
        bytes:
          type: integer
          format: int64
          description: Materialized byte count after any container was decoded away
        video:
          x-go-pointer: true
          $ref: '#/components/schemas/AttachmentVideoFacts'
    AttachmentKind:
      type: string
      description: Routing family the host detected for a materialized attachment.
      enum:
        - image
        - text
        - document
        - video
    PromptReferenceKind:
      type: string
      description: |
        Attachment-reference family. Path kinds are jailed under an attached project root; artifact re-links an existing VisualArtifact; search-hit re-serves a stored evidence_index snippet. Never carries bytes or a server-opened absolute path.
      enum:
        - path-file
        - path-folder
        - artifact
        - search-hit
    PromptReferencePart:
      description: |
        A pointer to something already on disk (under an attached project root), an existing visual artifact, or a stored search hit. Carries jailed coordinates only — never bytes and never an absolute filesystem path the server opens. The host fences path/folder refs so the agent reads them in place with its own jailed tools.
      oneOf:
        - $ref: '#/components/schemas/PromptReferencePathFile'
        - $ref: '#/components/schemas/PromptReferencePathFolder'
        - $ref: '#/components/schemas/PromptReferenceArtifact'
        - $ref: '#/components/schemas/PromptReferenceSearchHit'
      discriminator:
        propertyName: kind
        mapping:
          path-file: '#/components/schemas/PromptReferencePathFile'
          path-folder: '#/components/schemas/PromptReferencePathFolder'
          artifact: '#/components/schemas/PromptReferenceArtifact'
          search-hit: '#/components/schemas/PromptReferenceSearchHit'
    PromptAcceptedResponse:
      x-go-generate: true
      type: object
      required:
        - status
        - operation_id
        - message_id
        - session_revision
      additionalProperties: false
      properties:
        status:
          type: string
          description: Durable submission state observed at admission or replay. queued means the turn is waiting for session execution; eligible text-only turns may also appear in the editable next-turn draft. canceled means the user removed a waiting draft item before it ran.
          enum:
            - queued
            - running
            - complete
            - failed
            - interrupted
            - canceled
        operation_id:
          type: string
          format: uuid
          description: Durable idempotency identity of this prompt action.
        message_id:
          type: string
          format: uuid
          description: Durable message id of the transcript user row minted for this prompt. Clients correlate a submission with its transcript row by id.
        session_revision:
          type: integer
          format: int64
          minimum: 0
          description: Session event revision minted after the submission was admitted. Every session event with a greater revision reflects this submission, so the sender can treat the prompt as in flight until it applies one. Session events published earlier can still arrive after this response and describe the session before admission. 0 means the host publishes no session events.
    PromptStreamChunk:
      x-go-generate: true
      type: object
      required:
        - token
        - done
      additionalProperties: false
      properties:
        token:
          type: string
          description: Message text chunk
        reset:
          type: boolean
          description: Selects replacement instead of append
        tool_calls:
          type: array
          items:
            $ref: '#/components/schemas/ToolCall'
          description: Replaces the in-flight tool-call list when present
        done:
          type: boolean
          description: True when this chunk terminates the stream
    SessionHostError:
      x-go-generate: true
      type: object
      additionalProperties: false
      properties:
        code:
          $ref: '#/components/schemas/NoticeCode'
        title:
          type: string
        message:
          type: string
        suggested_action:
          type: string
        actions:
          type: array
          items:
            $ref: '#/components/schemas/NoticeAction'
        tier:
          $ref: '#/components/schemas/NoticeTier'
          description: 'Host-declared tier. Always `non_catastrophic` here: a condition that blocks the app cannot be delivered on a session event, because that needs a session the user cannot reach.'
        scope:
          $ref: '#/components/schemas/NoticeScope'
          description: Host-declared scope. Usually `session` — the enclosing event's `id` names which one — but a condition wider than the chat that raised it (an unconfigured provider, say) is declared at its true scope so it renders once rather than in every chat.
        resolution:
          type: string
          description: Which declared resolution produced `tier`/`scope`.
    SessionEvent:
      x-go-generate: true
      x-go-field-order:
        - id
        - project_id
        - action
        - title
        - status
        - last_message
        - untrusted_content
        - current_turn
        - ui
        - host_error
        - idle_disposition
        - prompt_pending
      type: object
      required:
        - id
        - project_id
        - action
        - status
      additionalProperties: false
      properties:
        id:
          type: string
          format: uuid
        project_id:
          type: string
          format: uuid
          description: Associated project; explicit so device-wide subscriptions never infer scope.
        action:
          $ref: '#/components/schemas/SessionEventAction'
        status:
          $ref: '#/components/schemas/SessionStatus'
        last_message:
          type: string
        title:
          type: string
          description: Display title (auto until the user renames); omitted until set
        host_error:
          x-go-pointer: true
          $ref: '#/components/schemas/SessionHostError'
        untrusted_content:
          type: boolean
          description: |
            Server-derived session untrusted-content fact (same semantics as Session.untrusted_content).
        current_turn:
          type: integer
          minimum: 0
          description: |
            Session.current_turn, carried on the event so a client reading a chat's current turn (`baseline=turn:{session_id}`) knows to read it again when a new turn starts, without refetching the session.
        ui:
          x-go-pointer: true
          $ref: '#/components/schemas/SessionUiState'
        idle_disposition:
          $ref: '#/components/schemas/SessionIdleDisposition'
        prompt_pending:
          type: boolean
          description: Session.prompt_pending as of this event, omitted when false. Every session event states it, so a client applying the newest event by revision never shows a session at rest while an admitted prompt waits to start.
    Session:
      x-go-generate: true
      x-go-field-order:
        - id
        - title
        - project_id
        - owner_person_id
        - workspace_root_id
        - workspace_path
        - posture
        - agent_type
        - provider_id
        - model
        - status
        - parent_session_id
        - max_tool_loops
        - compaction_generation
        - untrusted_content
        - current_turn
        - prompt_pending
        - ui
        - archived_at
        - pin_rank
        - seen_at
        - created_at
        - activity_at
        - updated_at
      type: object
      required:
        - id
        - project_id
        - owner_person_id
        - posture
        - status
        - created_at
        - activity_at
        - updated_at
      additionalProperties: false
      properties:
        id:
          type: string
          format: uuid
        project_id:
          type: string
          format: uuid
        owner_person_id:
          type: string
          format: uuid
          description: Person who started the session. Child sessions share their parent's owner. Read-only; ignored on input.
        workspace_root_id:
          type: string
          format: uuid
          description: Active root for this session; omitted when no roots
        workspace_path:
          type: string
          description: Computed on read from workspace_root_id or primary root; omitted when project has no roots
        posture:
          $ref: '#/components/schemas/SessionPosture'
        provider_id:
          type: string
          pattern: ^[a-z0-9_-]{1,64}$
        model:
          type: string
        agent_type:
          type: string
        status:
          $ref: '#/components/schemas/SessionStatus'
        title:
          type: string
          description: Display title (auto until the user renames); omitted until set
        created_at:
          type: string
          format: date-time
        activity_at:
          type: string
          format: date-time
          description: When the transcript last gained a visible message, or the creation time before that. Viewing, pinning, renaming, and other record changes leave it alone. Read-only; ignored on input.
        updated_at:
          type: string
          format: date-time
          description: Last change to the session record, including lifecycle and read state
        parent_session_id:
          type: string
          format: uuid
        max_tool_loops:
          type: integer
          minimum: 2
          maximum: 120
          description: Effective worker tool-loop cap. Omitted task budgets default to 12 for read scope and 20 for write scope; maximum 120. The final iteration is prose-only.
        compaction_generation:
          type: integer
          minimum: 0
        untrusted_content:
          type: boolean
          description: |
            Server-derived: true once this session has ingested untrusted external content (web tools, MCP, or web-researcher worker summary). Read-only; ignored on input.
        current_turn:
          type: integer
          minimum: 0
          description: |
            Server-derived 1-based ordinal of the session's newest user turn, and the same number the source ledger stamps on writes made during it. A client addressing a turn (source `baseline=turn:{session_id},{n}`) cites this value rather than counting transcript messages itself; `baseline=turn:{session_id}` lets the host resolve it. 0 means no user turn has started. Read-only; ignored on input.
        prompt_pending:
          type: boolean
          description: 'Server-derived: true while the host holds an admitted human prompt it will run without further human action and whose turn has not begun. That covers a prompt between admission and its turn, a command that ends without a turn while it runs, and next-turn draft items the host will drain, unless the draft is held for editing and the item is not reserved by Send. The gap between one turn''s idle boundary and the next turn''s busy is therefore never a session at rest. Omitted when false. Read-only; ignored on input.'
        ui:
          x-go-pointer: true
          $ref: '#/components/schemas/SessionUiState'
        archived_at:
          x-go-pointer: true
          type: string
          format: date-time
          description: Set while the session is archived (out of the working set); omitted when active
        pin_rank:
          x-go-pointer: true
          type: integer
          minimum: 1
          description: Order among the project's pinned chats, lowest first; omitted when unpinned. Archiving the session or moving it to another project unpins it. Read-only; ignored on input.
        seen_at:
          x-go-pointer: true
          type: string
          format: date-time
          description: When the person last had this session readable on screen. A turn that completes after this stamp is a result they have not seen, which is what puts the session in the attention view's `finished` class. Omitted until the session has been read. Written by POST /v1/sessions/{id}/seen; ignored on other input.
    SessionBootstrap:
      x-go-generate: true
      type: object
      required:
        - event_cursor
        - session
        - transcript
        - progress
        - turn_clock
        - findings
        - queue
        - coordinator
        - workers
        - checkpoints
        - background_outputs
        - previews
      additionalProperties: false
      properties:
        event_cursor:
          type: string
          description: Replay boundary captured before the snapshot was read
        session:
          $ref: '#/components/schemas/Session'
        activities:
          type: array
          description: Current host work leases for this session. Omitted when no leases remain.
          items:
            $ref: '#/components/schemas/ActivityEvent'
        transcript:
          $ref: '#/components/schemas/SessionTranscriptPage'
        progress:
          $ref: '#/components/schemas/ProgressDigest'
        turn_clock:
          $ref: '#/components/schemas/TurnClock'
        findings:
          $ref: '#/components/schemas/FindingsDigest'
        queue:
          $ref: '#/components/schemas/QueueDraft'
        coordinator:
          $ref: '#/components/schemas/CoordinatorRunContext'
        workers:
          type: array
          items:
            $ref: '#/components/schemas/WorkerTask'
        checkpoints:
          type: array
          items:
            $ref: '#/components/schemas/CheckpointEvent'
        background_outputs:
          type: array
          items:
            $ref: '#/components/schemas/BackgroundProcessOutput'
        previews:
          type: array
          items:
            $ref: '#/components/schemas/PreviewAttachment'
    SessionCompactResponse:
      x-go-generate: true
      type: object
      required:
        - generation
        - tokens_before
        - tokens_after
        - chunks_compacted
        - session_compacted
        - target_tokens
        - target_met
        - reason
        - cache_hit
        - measurement_method
        - text_tokens_before
        - text_tokens_after
      additionalProperties: false
      properties:
        generation:
          type: integer
          minimum: 0
        tokens_before:
          type: integer
          minimum: 0
        tokens_after:
          type: integer
          minimum: 0
        chunks_compacted:
          type: integer
          minimum: 0
        session_compacted:
          type: boolean
          description: Whether a conversation summary was published.
        target_tokens:
          type: integer
          description: Target in the host transcript estimate units.
          minimum: 0
        target_met:
          type: boolean
          description: Whether the resulting transcript estimate fits the target.
        reason:
          type: string
          description: Compaction outcome or reason no session summary was accepted.
        cache_hit:
          type: boolean
          description: An unchanged unproductive summary attempt was reused without a provider call.
        measurement_method:
          type: string
          description: Ordinary text encoding or estimated; excludes provider framing and media tokens.
        text_tokens_before:
          type: integer
          description: Projected text units before compaction, including trust markers and tool arguments.
          minimum: 0
        text_tokens_after:
          type: integer
          description: Projected text units after compaction, using the same measurement method.
          minimum: 0
    ElevatedAccessEffect:
      type: string
      description: Authority beyond a major confinement boundary.
      enum:
        - direct_network
        - host_execution
        - process_control
        - local_service
    ElevatedAccessRecord:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - id
        - kind
        - title
        - scope
        - effects
      properties:
        id:
          type: string
          pattern: ^(grant|quiet)_[A-Za-z0-9_-]+$
        kind:
          type: string
          enum:
            - grant
            - quiet
        title:
          type: string
        scope:
          $ref: '#/components/schemas/ApprovalGrantScope'
        effects:
          type: array
          items:
            $ref: '#/components/schemas/ElevatedAccessEffect'
        expires_at:
          x-go-pointer: true
          type: string
          format: date-time
    ElevatedAccessSummary:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - root_session_id
        - approvals_enabled
        - total
        - records
        - shared_scopes
      properties:
        approvals_enabled:
          type: boolean
          description: False when Advanced turns approvals off.
        root_session_id:
          type: string
          format: uuid
        total:
          type: integer
          minimum: 0
        records:
          type: array
          items:
            $ref: '#/components/schemas/ElevatedAccessRecord'
        shared_scopes:
          type: array
          items:
            $ref: '#/components/schemas/ApprovalGrantScope'
    ElevatedAccessRevokeResult:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - id
        - disposition
      properties:
        id:
          type: string
          pattern: ^(grant|quiet)_[A-Za-z0-9_-]+$
        disposition:
          type: string
          enum:
            - revoked
            - already_absent
            - failed
        code:
          $ref: '#/components/schemas/ApiErrorCode'
        message:
          type: string
    RevokeElevatedAccessResponse:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - results
        - remaining
      properties:
        results:
          type: array
          items:
            $ref: '#/components/schemas/ElevatedAccessRevokeResult'
        remaining:
          $ref: '#/components/schemas/ElevatedAccessSummary'
    SessionContextResponse:
      x-go-generate: true
      type: object
      required:
        - estimated_tokens
        - budget_remaining
        - compaction_generation
        - oversized_chunks
      additionalProperties: false
      properties:
        estimated_tokens:
          type: integer
          minimum: 0
        budget_remaining:
          type: integer
          minimum: 0
        compaction_generation:
          type: integer
          minimum: 0
        oversized_chunks:
          type: integer
          minimum: 0
    SessionPosture:
      type: string
      enum:
        - spec
        - build
        - orchestrate
        - vet
    SessionStatus:
      type: string
      description: Session lifecycle state.
      enum:
        - preparing
        - idle
        - busy
        - error
    SessionListSort:
      type: string
      description: |
        activity orders by activity_at, created by created_at, title by case-folded title, and pin by pin_rank (pinned=true only).
      enum:
        - activity
        - created
        - title
        - pin
    SessionListOrder:
      type: string
      enum:
        - asc
        - desc
    SessionSummary:
      x-go-generate: true
      type: object
      required:
        - id
        - project_id
        - owner_person_id
        - posture
        - status
        - message_count
        - created_at
        - activity_at
      additionalProperties: false
      description: Metadata-only chat list row; worker child sessions are never listed
      properties:
        id:
          type: string
          format: uuid
        project_id:
          type: string
          format: uuid
        owner_person_id:
          type: string
          format: uuid
          description: Person who started the session.
        title:
          type: string
        posture:
          $ref: '#/components/schemas/SessionPosture'
        status:
          $ref: '#/components/schemas/SessionStatus'
        archived_at:
          x-go-pointer: true
          type: string
          format: date-time
        pin_rank:
          x-go-pointer: true
          type: integer
          minimum: 1
          description: Order among the project's pinned chats, lowest first; omitted when unpinned
        message_count:
          type: integer
          minimum: 0
        created_at:
          type: string
          format: date-time
        activity_at:
          type: string
          format: date-time
          description: When the transcript last gained a visible message, or the creation time before that
    SessionListPage:
      x-go-generate: true
      type: object
      required:
        - sessions
        - total
      additionalProperties: false
      description: One seek page of a project's chats plus the total under the same filter
      properties:
        sessions:
          type: array
          items:
            $ref: '#/components/schemas/SessionSummary'
        total:
          type: integer
          minimum: 0
        next_cursor:
          type: string
          description: Opaque cursor for the next page; omitted on the last page
    ToolCall:
      x-go-generate: true
      type: object
      required:
        - name
        - id
        - args
      additionalProperties: false
      properties:
        name:
          type: string
        id:
          type: string
          pattern: ^[A-Za-z0-9_.:-]+$
        args:
          type: object
          additionalProperties: true
        args_ref:
          x-go-pointer: true
          $ref: '#/components/schemas/ChatContentReference'
        display_title:
          type: string
          description: Host-prepared card subtitle from the tool presentation catalog.
        wire_id:
          type: string
          pattern: ^[A-Za-z0-9_.:-]+$
          description: Provider's own tool-call token, retained only for AI providers that key server-side state on it. Never identity; empty when the provider emits no usable token.
        args_truncated:
          type: boolean
          description: True when the provider stream ended at the completion token cap mid-arguments — args never parsed.
        args_malformed:
          type: boolean
          description: True when the provider finished normally but emitted invalid JSON arguments — args never parsed and nothing reached the tool.
        extra_content:
          type: object
          additionalProperties: true
          description: Provider-specific tool-call metadata required on replay.
    ToolApprovalPayload:
      x-go-generate: true
      type: object
      required:
        - plan
      additionalProperties: false
      properties:
        tool_call_id:
          type: string
          pattern: ^[A-Za-z0-9_.:-]+$
        plan:
          $ref: '#/components/schemas/ApprovalPlan'
        ai_rationale:
          x-go-name: AIRationale
          type: string
        ai_rationale_pending:
          x-go-name: AIRationalePending
          type: boolean
        joined_count:
          type: integer
          minimum: 1
        joined_tool_call_ids:
          x-go-name: JoinedToolCallIDs
          type: array
          items:
            type: string
            pattern: ^[A-Za-z0-9_.:-]+$
        consequence_band:
          description: Live presentation override for an open card. Raised when joiners escalate via MaxConsequence; mint-time plan.presentation is unchanged.
          $ref: '#/components/schemas/ConsequenceBand'
        consequence_code:
          description: Host-authored consequence line id paired with consequence_band when the live band is high_risk. Prefer over plan.presentation when set.
          $ref: '#/components/schemas/ConsequenceCode'
        repeat:
          x-go-pointer: true
          $ref: '#/components/schemas/ApprovalRepeat'
    ToolApprovalResolveBody:
      oneOf:
        - $ref: '#/components/schemas/ToolApprovalApproveBody'
        - $ref: '#/components/schemas/ToolApprovalRejectBody'
      discriminator:
        propertyName: action
        mapping:
          approve: '#/components/schemas/ToolApprovalApproveBody'
          reject: '#/components/schemas/ToolApprovalRejectBody'
    ToolApprovalApproveBody:
      type: object
      required:
        - kind
        - action
        - option_id
      additionalProperties: false
      properties:
        kind:
          type: string
          const: tool_approval
        action:
          allOf:
            - $ref: '#/components/schemas/ApprovalDecisionAction'
            - const: approve
          description: |
            Redaction is a host-authored option decision, never a client-authored alternate path.
        option_id:
          type: string
          pattern: ^[A-Za-z0-9_.:-]+$
          description: |
            Selects one exact opaque option from tool_approval.plan; authority is installed atomically before resolution.
    ToolApprovalRejectBody:
      type: object
      required:
        - kind
        - action
      additionalProperties: false
      properties:
        kind:
          type: string
          const: tool_approval
        action:
          allOf:
            - $ref: '#/components/schemas/ApprovalDecisionAction'
            - const: reject
          description: Denies the plan.
        guidance:
          type: string
          description: |
            Explicit human direction from the composer describing what the agent should do instead. This does not authorize an alternative action; it is returned to the agent with the denial.
        secrets:
          type: array
          maxItems: 32
          description: Managed secrets explicitly attached to rejecting guidance.
          items:
            $ref: '#/components/schemas/PromptSecretReferencePart'
    ToolResult:
      x-go-generate: true
      type: object
      required:
        - content
      additionalProperties: false
      properties:
        content:
          type: string
        content_ref:
          x-go-pointer: true
          $ref: '#/components/schemas/ChatContentReference'
        tool_args_ref:
          x-go-pointer: true
          $ref: '#/components/schemas/ChatContentReference'
        display_title:
          type: string
        invocation:
          x-go-pointer: true
          $ref: '#/components/schemas/InvocationReceipt'
        tool:
          type: string
          description: Name of the tool that produced this result. Carried on the result row so Den renders completed tool cards after the host replaces the assistant tool_calls batch on a reused row.
        tool_call_id:
          type: string
          pattern: ^[A-Za-z0-9_.:-]+$
          description: ID of the tool call this result answers, anchoring search reveal to the originating tool chicklet.
        assistant_message_id:
          type: string
          format: uuid
          description: Durable ID of the assistant row that issued this call.
        tool_args:
          type: object
          additionalProperties: true
          description: Immutable argument snapshot for the tool call.
        outcome:
          $ref: '#/components/schemas/ToolResultOutcome'
        codes:
          type: array
          items:
            type: string
          description: |
            Guidance codes the host raised against this result, in raise order. A result carries every code raised, and the first is what card selection and quiet chrome bind to. Host-stamped as data by the producer that raised it; never parsed back out of `content`.
        feedback:
          type: array
          description: Structured facts for every raised guidance code, in the same order as codes. Agents branch on code and details, never on rendered content.
          items:
            $ref: '#/components/schemas/ToolFeedback'
        ui_visibility:
          x-go-name: UiVisibility
          $ref: '#/components/schemas/ToolResultUiVisibility'
          description: |
            Quiet-chrome for Den verbose prefs. Host stamps from the policy registry (`guard:` emit, advisory/informational/expected_behavior categories, info|warning severity) or a structured reject `code` with no registry row. `benign` hides unless verbose; `normal` stays visible (worker_dispatch, SPEC_POSTURE, …).
        dispatch:
          x-go-pointer: true
          $ref: '#/components/schemas/WorkerDispatch'
          description: Producer-stated worker dispatch identity.
        completion:
          x-go-pointer: true
          $ref: '#/components/schemas/ToolCompletion'
          description: Producer-stated asynchronous or lifecycle completion fact.
        file_edit_preview:
          x-go-pointer: true
          $ref: '#/components/schemas/FileEditPreview'
        promotion_previews:
          type: array
          items:
            $ref: '#/components/schemas/FileEditPreview'
        file_edit:
          x-go-pointer: true
          $ref: '#/components/schemas/FileEditSnapshot'
        overlay_promotion:
          x-go-pointer: true
          $ref: '#/components/schemas/OverlayPromotion'
        visual:
          x-go-pointer: true
          $ref: '#/components/schemas/VisualArtifact'
        checkpoint_decision:
          x-go-pointer: true
          $ref: '#/components/schemas/CheckpointDecisionMeta'
        external_access:
          x-go-pointer: true
          $ref: '#/components/schemas/ExternalAccess'
          description: |
            Bounded exceptional external-access detail for this tool result when the host recorded mediated endpoints, local-socket authority, direct IP, or detection citations on the action.
        skill:
          x-go-pointer: true
          $ref: '#/components/schemas/SkillActivation'
          description: |
            Resolved skill behind a skills_read activation. The host stamps every field from the same catalog entry the activation envelope was rendered from, so the skill chicklet never parses that envelope's prose.
        process:
          x-go-pointer: true
          $ref: '#/components/schemas/ToolProcessHandle'
          description: |
            Set when the tool returned while its process was still running; absent means the call finished. Presence is what keeps a chicklet live.
        verdict:
          x-go-pointer: true
          $ref: '#/components/schemas/VerdictOutcome'
          description: |
            Set when submit_verdict recorded a review_loop verdict. The card renders these stated facts, never the result prose.
    ExternalAccess:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - modes
        - visibility_summary
      properties:
        modes:
          type: array
          items:
            $ref: '#/components/schemas/ExternalAccessMode'
        visibility_summary:
          $ref: '#/components/schemas/ExternalAccessVisibilitySummary'
        endpoints:
          type: array
          description: |
            Deduped observed mediated endpoints (host+port+transport+decision) with attempt counts. Declarations never appear here.
          items:
            $ref: '#/components/schemas/ExternalAccessEndpoint'
        sockets:
          type: array
          maxItems: 8
          description: Applied local-service socket grants (authority available, not observed connects).
          items:
            $ref: '#/components/schemas/ExternalAccessSocket'
        declared_destinations:
          type: array
          items:
            type: string
          description: |
            Declared destination strings for context only. Never treated as observed endpoints.
        direct:
          x-go-pointer: true
          $ref: '#/components/schemas/ExternalAccessDirect'
        detections:
          type: array
          items:
            $ref: '#/components/schemas/ExternalAccessDetection'
    ExternalAccessVisibility:
      type: string
      description: |
        Primitive per-effect visibility. observed = host saw the destination through mediation; declared = request context only; unobserved = authority existed for effects the host could not observe; none = affirmatively absent for this record.
      enum:
        - observed
        - declared
        - unobserved
        - none
    ExternalAccessVisibilitySummary:
      type: string
      description: |
        Derived action-level summary. mixed = observed mediated endpoints coexist with unobserved direct/daemon authority; unknown = incomplete or malformed detail; none = affirmatively no exceptional access on this record.
      enum:
        - observed
        - declared
        - unobserved
        - mixed
        - none
        - unknown
    ExternalAccessMode:
      type: string
      description: Exceptional external-access mode present on an action.
      enum:
        - mediated_http
        - mediated_socks
        - local_socket
        - direct_ip
        - full_bypass
    ExternalAccessTransport:
      type: string
      description: Mediated transport that observed an endpoint.
      enum:
        - http_connect
        - http_request
        - socks_tcp
    ExternalAccessDecision:
      type: string
      description: Host decision for an observed mediated endpoint.
      enum:
        - allow
        - deny
    ExternalAccessEndpoint:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - host
        - port
        - transport
        - visibility
        - decision
        - attempt_count
      properties:
        host:
          type: string
        port:
          x-go-type: uint16
          type: integer
          minimum: 0
          maximum: 65535
        transport:
          $ref: '#/components/schemas/ExternalAccessTransport'
        visibility:
          $ref: '#/components/schemas/ExternalAccessVisibility'
          description: Observed endpoints always use observed.
        decision:
          $ref: '#/components/schemas/ExternalAccessDecision'
        attempt_count:
          type: integer
          minimum: 1
        allowed_count:
          x-go-pointer: true
          type: integer
          minimum: 0
        denied_count:
          x-go-pointer: true
          type: integer
          minimum: 0
    ExternalAccessSocket:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - approved_path
        - resolved_path
        - scope
        - capability_visibility
        - connection_visibility
        - inner_effect_visibility
        - authority
      properties:
        approved_path:
          type: string
        resolved_path:
          type: string
        scope:
          type: string
          description: Grant lifetime scope (for example chat or current_action).
        capability_visibility:
          $ref: '#/components/schemas/ExternalAccessVisibility'
          description: Fixed observed — the host applied the capability.
        connection_visibility:
          $ref: '#/components/schemas/ExternalAccessVisibility'
          description: Fixed unobserved — the platform sandbox supplies no connect telemetry.
        inner_effect_visibility:
          $ref: '#/components/schemas/ExternalAccessVisibility'
          description: Fixed unobserved — daemon/container inner effects are not observed.
        authority:
          $ref: '#/components/schemas/ExternalAccessSocketAuthority'
    ExternalAccessSocketAuthority:
      type: string
      description: Fixed authority class for an applied local-service socket grant.
      enum:
        - outside_sandbox_daemon
    ExternalAccessDirect:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - scope
        - actual_destination_visibility
      properties:
        scope:
          $ref: '#/components/schemas/ExternalAccessDirectScope'
        actual_destination_visibility:
          $ref: '#/components/schemas/ExternalAccessVisibility'
          description: Fixed unobserved — direct actual destinations are never observed.
    ExternalAccessDirectScope:
      type: string
      description: Fixed scope for one-action direct IP.
      enum:
        - current_action
    ExternalAccessDetection:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - pack_id
        - rule_id
        - level
      properties:
        pack_id:
          type: string
          pattern: ^[a-zA-Z0-9_./%-]+$
        rule_id:
          type: string
          pattern: ^[a-zA-Z0-9_.-]+$
        rule_title:
          type: string
        level:
          type: string
        action_id:
          type: string
          format: uuid
          description: Exact action id the winning detection citation is bound to.
    ArtifactListItem:
      x-go-generate: true
      type: object
      required:
        - id
        - mime
        - source
        - session_id
        - created_at
      additionalProperties: false
      description: Metadata-only durable artifact row (no bytes, no host paths)
      properties:
        id:
          type: string
          format: uuid
        mime:
          type: string
        source:
          $ref: '#/components/schemas/VisualArtifactSource'
        caption:
          type: string
        evidence_handle:
          type: string
        page_id:
          type: string
          pattern: ^[A-Za-z0-9_.:-]+$
          description: Held browser page that produced live-tool media.
        session_id:
          type: string
          format: uuid
        workflow_run_id:
          type: string
          format: uuid
        tool_call_id:
          type: string
          pattern: ^[A-Za-z0-9_.:-]+$
        created_at:
          type: string
          format: date-time
        recorded_at:
          x-go-pointer: true
          type: string
          format: date-time
        duration_ms:
          x-go-name: DurationMS
          type: integer
          format: int64
          minimum: 0
        origin_message_id:
          type: string
          format: uuid
          description: Message that produced or presented this artifact when known
        references:
          type: array
          description: |
            What would break if this artifact were deleted, counted by kind. Absent or empty means nothing durable claims it.
          items:
            $ref: '#/components/schemas/ArtifactReferenceCount'
    ArtifactListResponse:
      x-go-generate: true
      type: object
      required:
        - artifacts
      additionalProperties: false
      properties:
        artifacts:
          type: array
          items:
            $ref: '#/components/schemas/ArtifactListItem'
        next_cursor:
          type: string
          description: Opaque cursor for the next project artifact page; empty at the end.
    WorkerSummaryStatus:
      type: string
      enum:
        - complete
        - partial
        - failed
        - canceled
        - held
        - open
        - needs_decision
    WorkerBlockerClass:
      type: string
      description: Machine class on worker request_decision — never inferred from stderr prose
      enum:
        - decision
        - checkpoint
        - sandbox
    WorkerDecisionRequest:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - worker_id
        - child_session_id
        - question
        - options
        - blocker_class
      properties:
        worker_id:
          type: string
          format: uuid
        child_session_id:
          type: string
          format: uuid
        question:
          type: string
        options:
          type: array
          items:
            type: string
        blocker_class:
          $ref: '#/components/schemas/WorkerBlockerClass'
        artifact_id:
          type: string
          format: uuid
          description: Optional session-tree VisualArtifact the worker wants judged
        artifact_ids:
          x-go-name: ArtifactIDs
          type: array
          minItems: 2
          maxItems: 4
          items:
            type: string
            format: uuid
          description: Optional 2–4 session-tree visuals for a compare preference
    CitationVerdict:
      type: string
      enum:
        - matched
        - traced
        - unverifiable
    WorkerSummaryMeta:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - worker_id
        - child_session_id
        - agent_type
        - status
        - envelope
      properties:
        source_context:
          x-go-pointer: true
          $ref: '#/components/schemas/SourceContext'
        delegation_id:
          type: string
          format: uuid
        leg_id:
          type: string
          format: uuid
        worker_id:
          type: string
          format: uuid
        child_session_id:
          type: string
          format: uuid
        agent_type:
          type: string
        status:
          $ref: '#/components/schemas/WorkerSummaryStatus'
        grounding:
          x-go-pointer: true
          $ref: '#/components/schemas/CitationGrounding'
        decision_request:
          x-go-pointer: true
          $ref: '#/components/schemas/WorkerDecisionRequest'
        envelope:
          type: string
          description: Structured worker completion block.
    CitationGrounding:
      x-go-generate: true
      x-go-field-order:
        - traced
        - host_assembled
        - hint_code
        - retry_count
        - observed_path_count
        - observed_paths_sample
        - observed_url_count
        - observed_urls_sample
        - checks
        - findings
        - cited_urls
        - prose_leak_count
        - prose_leaks_sample
        - prose_advisory_count
        - prose_advisories_sample
        - cited_evidence
        - evidence_records
        - evidence_record_count
        - verification
      type: object
      additionalProperties: false
      required:
        - traced
      properties:
        traced:
          type: boolean
        host_assembled:
          type: boolean
        hint_code:
          type: string
        retry_count:
          type: integer
        observed_path_count:
          type: integer
        observed_paths_sample:
          type: array
          items:
            type: string
        observed_url_count:
          type: integer
        observed_urls_sample:
          x-go-name: ObservedURLsSample
          type: array
          items:
            type: string
        checks:
          type: array
          items:
            $ref: '#/components/schemas/CitationGroundingCheck'
        findings:
          type: array
          items:
            $ref: '#/components/schemas/CitationGroundingFinding'
        cited_urls:
          x-go-name: CitedURLs
          type: array
          items:
            type: string
        cited_evidence:
          type: array
          items:
            $ref: '#/components/schemas/CitationGroundingCitedEvidence'
        prose_leak_count:
          type: integer
        prose_leaks_sample:
          type: array
          items:
            type: string
        prose_advisory_count:
          type: integer
        prose_advisories_sample:
          type: array
          items:
            type: string
        evidence_records:
          type: array
          items:
            $ref: '#/components/schemas/CitationGroundingEvidenceRecord'
        evidence_record_count:
          type: integer
          minimum: 0
          description: How many ledger records the audit drew evidence_records from. Larger than the array when the wire cap held some back, so a reader can say "24 of 61" rather than mistake the sample for the whole ledger.
        verification:
          x-go-pointer: true
          $ref: '#/components/schemas/CitationVerification'
    CitationGroundingCheck:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - id
        - label
        - status
      properties:
        id:
          type: string
          pattern: ^[a-z0-9_-]+$
        label:
          type: string
        status:
          $ref: '#/components/schemas/CitationGroundingCheckStatus'
        kind:
          type: string
          description: Citation checks audit typed citations; lifecycle checks audit worker activity (survey, artifact).
          enum:
            - citation
            - lifecycle
        vacuous:
          type: boolean
          description: A passed check that had no citations to verify.
        summary:
          type: string
        matched:
          type: array
          items:
            type: string
        failed:
          type: array
          items:
            type: string
    CitationGroundingEvidenceRecord:
      x-go-generate: true
      type: object
      additionalProperties: false
      properties:
        handle:
          type: string
        kind:
          type: string
        shape:
          type: string
        fidelity:
          type: string
          description: How faithfully this record reproduces what the tool observed — structured | scraped | opaque. Unrelated to Message.trust_tier, which answers whether instructions in content may be followed.
        tool:
          type: string
        path:
          type: string
        urls:
          x-go-name: URLs
          type: array
          items:
            type: string
          description: Every URL this observation grounds. A web_search record carries all of its result URLs; a fetch_url record carries the single fetched page.
        line:
          type: integer
        line_end:
          type: integer
          description: Last line of the first observed range; a read that returned lines 80–88 states both.
        match_count:
          type: integer
          minimum: 0
          description: For a search record, how many matching lines the tool returned.
        match_paths:
          type: integer
          minimum: 0
          description: For a search record, how many distinct files those matches fell in.
        url_titles:
          type: object
          additionalProperties:
            type: string
          description: Page titles the tool reported, keyed by URL. A fetch carries its page's title; a search carries one per result it returned.
        excerpt:
          type: string
        truncated:
          type: boolean
    WorkflowBoundaryMeta:
      x-go-generate: true
      type: object
      required:
        - event
      additionalProperties: false
      properties:
        event:
          x-go-type: string
          $ref: '#/components/schemas/WorkflowBoundaryKind'
        workflow_id:
          type: string
          pattern: ^[a-z0-9_-]+$
        workflow_version:
          type: string
        phase:
          type: string
        reason:
          type: string
    WorkflowBoundaryKind:
      type: string
      enum:
        - started
        - exited
        - canceled
        - completed
        - failed
        - paused
        - resumed
        - paused_on_child
        - interrupted
    Finding:
      x-go-generate: true
      type: object
      additionalProperties: false
      properties:
        agent:
          type: string
        summary:
          type: string
        ref:
          type: string
        recorded_at:
          x-go-type: string
          type: string
          format: date-time
      required:
        - agent
        - summary
    FindingsDigest:
      x-go-generate: true
      type: object
      additionalProperties: false
      properties:
        findings:
          type: array
          items:
            $ref: '#/components/schemas/Finding'
        revision:
          x-go-type: uint64
          type: integer
          format: int64
      required:
        - findings
        - revision
    FindingsEvent:
      x-go-generate: true
      type: object
      additionalProperties: false
      properties:
        revision:
          x-go-type: uint64
          type: integer
          format: int64
      required:
        - revision
    ProgressStep:
      x-go-generate: true
      type: object
      additionalProperties: false
      properties:
        state:
          x-go-type: string
          $ref: '#/components/schemas/ProgressState'
        label:
          type: string
      required:
        - state
        - label
    ProgressDigest:
      x-go-generate: true
      type: object
      additionalProperties: false
      properties:
        steps:
          type: array
          items:
            $ref: '#/components/schemas/ProgressStep'
        revision:
          x-go-type: uint64
          type: integer
          format: int64
      required:
        - steps
        - revision
    ProgressEvent:
      x-go-generate: true
      type: object
      additionalProperties: false
      properties:
        revision:
          x-go-type: uint64
          type: integer
          format: int64
      required:
        - revision
    TurnClock:
      x-go-generate: true
      type: object
      additionalProperties: false
      description: 'Active-time clock of one visible user turn in a session tree. A visible turn opens when a person''s prompt starts an idle root session and lasts until the next such prompt; host and worker continuations accrue to it. The live clock is published on the edges where it transitions: the first executing prompt in, the last executing prompt out, and a new visible user turn. Clients tick locally between edges as active_ms + (now − running_at), so no event is needed while it merely advances. Transcript pages carry the same shape for each turn they open.'
      properties:
        session_id:
          type: string
          format: uuid
          description: Root session the clock belongs to.
        opening_message_id:
          type: string
          format: uuid
          description: The user message that opened the visible turn. Omitted while the session tree has run without a person's prompt opening one.
        active_ms:
          type: integer
          format: int64
          description: Banked active time for the visible user turn in milliseconds. Accrues while at least one prompt in the session tree is executing, including an approval wait inside that execution. Pauses between executions. Resets when a new root user turn begins.
        work_ms:
          type: integer
          format: int64
          description: The part of active_ms, banked in milliseconds, during which no execution in the session tree was waiting on a person's decision, such as a tool approval or an edit review.
        running:
          type: boolean
          description: Whether the clock is currently advancing (a turn is in flight).
        running_at:
          x-go-type: string
          type: string
          format: date-time
          description: RFC 3339 instant the clock resumed; present only while running.
        settled_at:
          x-go-type: string
          type: string
          format: date-time
          description: RFC 3339 instant the clock last paused, which is when the visible turn's work last finished. Omitted before the first pause; a continuation that resumes the clock keeps the previous value.
      required:
        - session_id
        - active_ms
        - work_ms
        - running
    TurnLoad:
      x-go-generate: true
      type: object
      additionalProperties: false
      description: 'One decision the local decision engine recorded for a chat: what it offered a turn before the model''s first call, or how it matched a request the model made. The transcript page carries the decisions of the turns it shows; the turn_load event carries each one as it lands. An abstained decision is recorded like any other, with its reason.'
      properties:
        session_id:
          type: string
          format: uuid
        trigger:
          $ref: '#/components/schemas/TurnLoadTrigger'
        opening_message_id:
          type: string
          format: uuid
          description: The user message that opened the turn the decision belongs to.
        tool_call_id:
          type: string
          pattern: ^[A-Za-z0-9_.:-]+$
          description: The model call that asked, for request and lookup triggers. Omitted for a turn decision.
        engine:
          x-go-pointer: true
          $ref: '#/components/schemas/TurnLoadEngine'
          description: Present when an engine answered.
        abstained:
          type: boolean
          description: True when no engine answered; reason says why.
        reason:
          type: string
        elapsed_ms:
          type: integer
          format: int64
          description: Wall time of the decision.
        kind:
          x-go-pointer: true
          $ref: '#/components/schemas/TurnLoadKind'
        floor:
          type: array
          items:
            type: string
          description: Tools the surface offers on every call of the turn, sorted by name; empty outside a turn decision.
        tools:
          type: array
          items:
            $ref: '#/components/schemas/TurnLoadTool'
          description: The standing tools beyond the floor once a turn decision applied, sorted by name; empty outside a turn decision.
        boundary:
          x-go-pointer: true
          $ref: '#/components/schemas/TurnLoadBoundary'
          description: Present on a turn decision.
        preloaded_skill:
          x-go-pointer: true
          $ref: '#/components/schemas/TurnLoadSkill'
        guides:
          x-go-pointer: true
          $ref: '#/components/schemas/TurnLoadGuides'
        match:
          x-go-pointer: true
          $ref: '#/components/schemas/TurnLoadMatch'
      required:
        - session_id
        - trigger
        - opening_message_id
        - abstained
        - elapsed_ms
        - floor
        - tools
    TurnLoadTrigger:
      type: string
      description: What asked the decision engine. A turn decision runs before the model's first call of a visible user turn or a worker leg; a request resolves request_tools text; a lookup resolves skills_read text.
      enum:
        - turn
        - request
        - lookup
    TurnLoadMatchBy:
      type: string
      description: 'How a request or lookup resolved its text to names: the engine ranked the candidates, the text spelled names out, or nothing matched.'
      enum:
        - engine
        - name
        - none
    TurnLoadEngine:
      x-go-generate: true
      type: object
      additionalProperties: false
      description: The local decision model that answered.
      properties:
        name:
          type: string
          description: Engine name.
        model:
          type: string
          description: Backbone model identifier.
        head:
          type: string
          description: Decision head the answer came from.
        label:
          type: string
          description: The receipt's one-line engine label, name/model#head.
      required:
        - name
        - label
    TurnLoadKind:
      x-go-generate: true
      type: object
      additionalProperties: false
      description: The turn kind the engine chose, with its calibrated confidence.
      properties:
        value:
          type: string
        confidence:
          type: number
          format: double
      required:
        - value
        - confidence
    TurnLoadToolSource:
      type: string
      description: 'Why a tool stands loaded: a turn decision predicted it, or the model asked for it through request_tools.'
      enum:
        - predicted
        - requested
    TurnLoadTool:
      x-go-generate: true
      type: object
      additionalProperties: false
      description: One tool the chat's standing set offered beyond the surface floor when the turn opened.
      properties:
        tool:
          type: string
        source:
          $ref: '#/components/schemas/TurnLoadToolSource'
        p:
          type: number
          format: double
          description: The engine's P(true) that the request needed the tool, for a predicted tool.
        need:
          type: string
          description: The request_tools text that loaded the tool, for a requested tool.
        carried:
          type: boolean
          description: True when the tool joined on an earlier turn and stayed because the provider still held the prompt prefix.
      required:
        - tool
        - source
        - carried
    TurnLoadCacheState:
      type: string
      description: Whether the chat's standing prompt prefix is expected to be reusable when the turn opened. Warm decisions retain tools and can restore needed instructions; cold decisions can replace the standing surface.
      enum:
        - warm
        - cold
    TurnLoadColdReason:
      type: string
      description: 'The recorded fact that made a turn cold: the chat''s first decided turn, a route that caches nothing, an idle gap past the prefix''s lifetime, a different provider or model, or a local runner that no longer holds the model.'
      enum:
        - first_turn
        - uncached
        - idle
        - model_changed
        - not_resident
    TurnLoadBoundary:
      x-go-generate: true
      type: object
      additionalProperties: false
      description: Expected reuse of the standing prefix when the turn opened; history compaction does not expire this tier.
      properties:
        cache:
          $ref: '#/components/schemas/TurnLoadCacheState'
        reason:
          $ref: '#/components/schemas/TurnLoadColdReason'
        idle_ms:
          type: integer
          format: int64
          description: Time since the chat's last model call started, when there was one.
        cold_after_ms:
          type: integer
          format: int64
          description: How long the route keeps an idle prefix; omitted when the provider documents no figure.
      required:
        - cache
    TurnLoadGuides:
      x-go-generate: true
      type: object
      additionalProperties: false
      description: How many loadable instruction units the turn rendered and left out of the prompt.
      properties:
        rendered:
          type: integer
        omitted:
          type: integer
      required:
        - rendered
        - omitted
    TurnLoadMatch:
      x-go-generate: true
      type: object
      additionalProperties: false
      description: How a request_tools or skills_read text resolved to names.
      properties:
        need:
          type: string
          description: The text the model wrote.
        by:
          $ref: '#/components/schemas/TurnLoadMatchBy'
        names:
          type: array
          items:
            type: string
          description: The tools or skills the text resolved to, best first.
      required:
        - need
        - by
        - names
    QueueItem:
      x-go-generate: true
      type: object
      additionalProperties: false
      properties:
        id:
          type: string
          format: uuid
          description: Durable prompt submission id for the waiting turn.
        submitted_by:
          type: string
          format: uuid
          description: Person who sent this message. Linked items share one sender.
        text:
          type: string
        group_id:
          type: string
          format: uuid
          description: Shared id for "linked" items that drain together as one coalesced turn. Absent when the item drains as its own turn.
        created_at:
          x-go-type: string
          type: string
          format: date-time
      required:
        - id
        - submitted_by
        - text
        - created_at
    QueueDraft:
      x-go-generate: true
      type: object
      description: Editable projection of eligible text-only turns waiting to run.
      additionalProperties: false
      properties:
        queue_items:
          type: array
          items:
            $ref: '#/components/schemas/QueueItem'
        hold:
          type: boolean
          description: When true, round-end draining is suppressed while the user edits the queue (drag, link, reorder).
        sending:
          type: boolean
          description: True while the head item or linked head group is reserved for delivery into the active turn at its next safe boundary. Reserved items cannot be edited until they land, or until the reservation is released with cancel_send. A running loop only reaches that boundary between provider calls and tool batches, so a reservation can wait — indefinitely, while a tool approval is parked — and cancel_send is the way back.
        revision:
          x-go-type: uint64
          type: integer
          format: int64
      required:
        - queue_items
        - hold
        - sending
        - revision
    QueueEvent:
      x-go-generate: true
      type: object
      additionalProperties: false
      properties:
        revision:
          x-go-type: uint64
          type: integer
          format: int64
      required:
        - revision
    QueueMutateRequest:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - expected_revision
      properties:
        op:
          type: string
          enum:
            - reorder
            - link
            - unlink
            - remove
            - update
            - hold
            - fire_now
            - send
            - cancel_send
        expected_revision:
          x-go-type: uint64
          type: integer
          format: int64
          minimum: 0
          description: Queue revision the mutation was authored against; stale mutations are rejected atomically.
        operation_id:
          type: string
          format: uuid
        item_ids:
          x-go-name: ItemIDs
          type: array
          items:
            type: string
            format: uuid
        hold:
          x-go-pointer: true
          type: boolean
        text:
          x-go-pointer: true
          type: string
          description: Replacement message body for op "update" (targets item_ids[0]).
    GitFileEntry:
      x-go-generate: true
      type: object
      additionalProperties: false
      properties:
        path:
          type: string
          description: Repo-toplevel-relative path of the changed file (exactly what porcelain printed).
        status:
          type: string
          description: Two-character porcelain XY status code.
        root_id:
          type: string
          format: uuid
          description: Project root that contains this path; empty when the path falls under no project root.
        root_relative_path:
          type: string
          description: Path relative to that project root; empty when root_id is empty.
      required:
        - path
        - status
        - root_id
        - root_relative_path
    GitStatusSummary:
      x-go-generate: true
      type: object
      additionalProperties: false
      properties:
        available:
          type: boolean
        repo_id:
          type: string
          pattern: ^[a-zA-Z0-9_-]+$
        root_ids:
          x-go-name: RootIDs
          type: array
          items:
            type: string
            format: uuid
        revision:
          x-go-type: uint64
          type: integer
          format: uint64
          description: The published status generation these values come from; zero while the first snapshot loads.
        refreshing:
          type: boolean
          description: True while a newer generation is loading behind these values. The values remain usable.
        branch:
          type: string
        head_short:
          type: string
        upstream:
          type: string
        ahead:
          type: integer
        behind:
          type: integer
        dirty:
          type: boolean
        staged_count:
          type: integer
        unstaged_count:
          type: integer
        changed_count:
          type: integer
      required:
        - available
        - repo_id
        - root_ids
        - revision
        - refreshing
        - ahead
        - behind
        - dirty
        - staged_count
        - unstaged_count
        - changed_count
    GitChangesPage:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - repo_id
        - revision
        - refreshing
        - files
      properties:
        repo_id:
          type: string
          pattern: ^[a-zA-Z0-9_-]+$
        revision:
          x-go-type: uint64
          type: integer
          format: uint64
        refreshing:
          type: boolean
          description: True while a newer generation is loading behind this page. The page still answers for its own revision.
        files:
          type: array
          items:
            $ref: '#/components/schemas/GitFileEntry'
        next_cursor:
          type: string
    GitMutationResult:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - repo_id
        - revision
        - refreshing
      properties:
        repo_id:
          type: string
          pattern: ^[a-zA-Z0-9_-]+$
        revision:
          x-go-type: uint64
          type: integer
          format: uint64
        refreshing:
          type: boolean
    GitRepoEntry:
      x-go-generate: true
      type: object
      additionalProperties: false
      properties:
        repo_id:
          type: string
          pattern: ^[a-zA-Z0-9_-]+$
          description: Derived repository id; omitted when available is false.
        label:
          type: string
        root_ids:
          x-go-name: RootIDs
          type: array
          description: Project root ids this repository contains, in project-roots order.
          items:
            type: string
            format: uuid
        available:
          type: boolean
          description: False for a root inside no repository.
        branch:
          type: string
        head_short:
          type: string
        upstream:
          type: string
        ahead:
          type: integer
        behind:
          type: integer
        dirty:
          type: boolean
        staged_count:
          type: integer
        unstaged_count:
          type: integer
        changed_count:
          type: integer
        status_pending:
          type: boolean
          description: True while this repository's first status snapshot is loading; count and branch fields are not meaningful until it is false.
      required:
        - label
        - root_ids
        - available
        - ahead
        - behind
        - dirty
        - staged_count
        - unstaged_count
        - changed_count
    GitReposView:
      x-go-generate: true
      type: object
      additionalProperties: false
      properties:
        repos:
          type: array
          items:
            $ref: '#/components/schemas/GitRepoEntry'
        active_repo_id:
          type: string
          pattern: ^[a-zA-Z0-9_-]+$
          description: Repository id for the session's active root when available; omitted when the set has no available repository.
      required:
        - repos
    GitBranchEntry:
      x-go-generate: true
      type: object
      additionalProperties: false
      properties:
        name:
          type: string
        current:
          type: boolean
      required:
        - name
        - current
    GitBranchesView:
      x-go-generate: true
      type: object
      additionalProperties: false
      properties:
        branches:
          type: array
          items:
            $ref: '#/components/schemas/GitBranchEntry'
      required:
        - branches
    GitCheckoutRequest:
      x-go-generate: true
      type: object
      additionalProperties: false
      properties:
        branch:
          type: string
        create:
          type: boolean
          description: Create the branch (checkout -b) before switching.
      required:
        - branch
    GitInitRequest:
      x-go-generate: true
      type: object
      additionalProperties: false
      properties:
        root_id:
          type: string
          format: uuid
          description: Project root in which to initialize a repository.
      required:
        - root_id
    GitCommitRequest:
      x-go-generate: true
      type: object
      additionalProperties: false
      properties:
        message:
          type: string
        paths:
          type: array
          description: Paths to stage; all changes are staged when omitted.
          items:
            type: string
      required:
        - message
    GitStashRequest:
      x-go-generate: true
      type: object
      additionalProperties: false
      properties:
        message:
          type: string
    GitCommitMessageResponse:
      x-go-generate: true
      type: object
      additionalProperties: false
      properties:
        message:
          type: string
      required:
        - message
    GitWorktreeView:
      x-go-generate: true
      type: object
      additionalProperties: false
      description: |
        One chat's worktree binding. When bound is false, every other field is absent or zero except session_id. State is absent when unbound.
      properties:
        bound:
          type: boolean
          description: False means the chat has no worktree.
        session_id:
          type: string
          format: uuid
        repo_id:
          type: string
          pattern: ^[a-zA-Z0-9_-]+$
          description: The bound repository's derived id.
        label:
          type: string
          description: The repository's label, for copy.
        branch:
          type: string
          description: The session branch checked out in the worktree.
        base_branch:
          type: string
          description: The repository branch recorded at bind time; Land always targets this branch.
        path:
          type: string
          description: Absolute worktree path — shown in the stale state and nowhere else.
        state:
          type: string
          description: 'Present only when bound: ready when the worktree directory exists; stale when the binding''s path is missing.'
          enum:
            - ready
            - stale
        dirty:
          type: boolean
          description: The worktree has uncommitted changes.
        ahead_of_base:
          type: integer
          description: Commits on the session branch not in base_branch.
        behind_base:
          type: integer
          description: Commits on base_branch not in the session branch.
        land_blocked_reason:
          type: string
          description: Preflight reason Land can surface even when ahead counts are unavailable.
          enum:
            - base_branch_missing
        created_at:
          x-go-type: string
          type: string
          format: date-time
      required:
        - bound
        - session_id
        - dirty
        - ahead_of_base
        - behind_base
    GitWorktreeLandResult:
      x-go-generate: true
      type: object
      additionalProperties: false
      description: |
        Outcome of landing a session branch into the recorded base branch in the project folder. landed false with reason nothing_to_land is success with nothing to do; other reasons are refusals.
      properties:
        landed:
          type: boolean
        base_branch:
          type: string
        commits:
          type: integer
          description: Commits merged; zero when landed is false.
        reason:
          type: string
          description: |
            Empty on success. Otherwise one of nothing_to_land, base_missing, base_dirty, base_on_other_branch, base_branch_missing, detached_head, conflict.
        conflicts:
          type: array
          description: Repo-toplevel-relative conflict paths when reason is conflict.
          items:
            type: string
      required:
        - landed
        - base_branch
        - commits
        - reason
        - conflicts
    GitWorktreeBindRequest:
      x-go-generate: true
      type: object
      additionalProperties: false
      description: Creates a worktree for a chat in one project repository.
      properties:
        repo_id:
          type: string
          pattern: ^[a-zA-Z0-9_-]+$
          description: Repository to bind; must be available in the project's repository set.
        branch:
          type: string
          description: New branch the worktree checks out.
      required:
        - repo_id
        - branch
    GroundingEvent:
      x-go-generate: true
      type: object
      required:
        - session_id
        - code
      additionalProperties: false
      properties:
        session_id:
          type: string
          format: uuid
        code:
          type: string
        leg_id:
          type: string
          format: uuid
        delegation_id:
          type: string
          format: uuid
        escalated:
          type: boolean
    McpCheckRow:
      x-go-generate: true
      type: object
      required:
        - status
      additionalProperties: false
      properties:
        provider_id:
          type: string
          pattern: ^[a-zA-Z0-9_.-]+$
          description: The provider the row checks; absent for a refused overlay row that named no id.
        status:
          $ref: '#/components/schemas/McpCheckRowStatus'
        code:
          x-go-type: string
          $ref: '#/components/schemas/ApiErrorCode'
          description: Catalog code when the row failed a trust gate or sync.
        notice:
          x-go-pointer: true
          $ref: '#/components/schemas/NoticeCopy'
    McpCheckRowStatus:
      type: string
      description: Status of an MCP provider during health checks.
      enum:
        - healthy
        - disabled
        - error
    McpCheckResponse:
      x-go-generate: true
      type: object
      required:
        - providers
      additionalProperties: false
      properties:
        providers:
          type: array
          items:
            $ref: '#/components/schemas/McpCheckRow'
    McpToolInfo:
      x-go-generate: true
      type: object
      required:
        - name
      additionalProperties: false
      properties:
        name:
          type: string
          description: Host-qualified tool name (mcp_<provider>_<tool>).
        description:
          type: string
    McpProviderStatus:
      type: string
      enum:
        - disabled
        - needs_auth
        - connecting
        - ready
        - error
        - rejected
    McpProviderClass:
      type: string
      description: |
        Host-derived deployment class. local is stdio or loopback HTTP; web is remote HTTPS. Never authored in overlay YAML.
      enum:
        - local
        - web
    McpToolLoading:
      type: string
      description: Controls when an enabled MCP provider's tool definitions enter model requests.
      enum:
        - auto
        - always
    McpRecipeEnvKey:
      x-go-generate: true
      type: object
      required:
        - key
        - label
      additionalProperties: false
      properties:
        key:
          type: string
        label:
          type: string
    McpProvider:
      x-go-generate: true
      type: object
      required:
        - id
        - enabled
        - class
        - tool_loading
      additionalProperties: false
      properties:
        id:
          type: string
          pattern: ^[a-zA-Z0-9_.-]+$
        enabled:
          type: boolean
        class:
          $ref: '#/components/schemas/McpProviderClass'
        tool_loading:
          $ref: '#/components/schemas/McpToolLoading'
        transport:
          type: string
          enum:
            - stdio
            - http
        connection_source:
          type: string
          enum:
            - distro
            - user
            - project
          description: |
            Catalog layer that last set url or command. Rejected trust rows use the layer that attempted the bad overlay; last_error holds the reason code.
        status:
          $ref: '#/components/schemas/McpProviderStatus'
        last_error:
          x-go-type: string
          $ref: '#/components/schemas/ApiErrorCode'
          description: Catalog code for the most recent sync failure or RejectedRow reason.
        notice:
          x-go-pointer: true
          $ref: '#/components/schemas/NoticeCopy'
          description: Rendered copy for `last_error`.
        tool_count:
          x-go-pointer: true
          type: integer
          minimum: 0
        profiles:
          type: array
          items:
            type: string
          description: Distro workflow profile names referencing this provider (display only).
        source:
          type: string
          enum:
            - default
            - override
          description: |
            Present when listing with project_id. default inherits device enablement; override is set in the project overlay.
        command:
          type: string
        args:
          type: array
          items:
            type: string
        url:
          type: string
          description: |
            Canonical HTTP URL after host inference. A host:port without a scheme is stored as http:// on loopback and https:// otherwise.
        token_present:
          type: boolean
          description: True when a static token is stored (value never returned).
        headers_present:
          type: boolean
          description: True when static HTTP headers are stored (values never returned).
        env_present:
          type: boolean
          description: True when stdio env map has keys (values never returned).
        signed_in:
          type: boolean
          description: True when MCP OAuth tokens are present.
        recipe:
          type: string
          description: Bundled recipe id this row was added from. Empty for Custom.
        auth:
          $ref: '#/components/schemas/McpRecipeAuth'
        credential_label:
          type: string
          description: Recipe-supplied label for the static token field.
        credential_hint:
          type: string
        docs_url:
          type: string
        credential_wire:
          $ref: '#/components/schemas/McpCredentialWire'
        credential_header:
          type: string
          description: Header name when credential_wire is header.
    McpOAuthStartResponse:
      x-go-generate: true
      type: object
      required:
        - authorize_url
        - state
      additionalProperties: false
      properties:
        authorize_url:
          type: string
          description: Authorization endpoint URL (open in system browser).
        state:
          type: string
          description: Opaque CSRF value bound to this authorization. Echoed back on the redirect and required by the manual completion path.
        redirect_uri:
          type: string
          description: Loopback URL the host is listening on for this authorization (RFC 8252 §7.3, ephemeral port). The redirect completes the exchange on its own; oauth/complete is the fallback for an authorization server that will not redirect to loopback.
    McpOAuthCancelRequest:
      x-go-generate: true
      type: object
      required:
        - state
      additionalProperties: false
      properties:
        state:
          type: string
          minLength: 1
          description: Pending authorization to cancel; newer attempts and saved credentials are unaffected.
    McpOAuthCompleteRequest:
      x-go-generate: true
      type: object
      required:
        - code
        - state
      additionalProperties: false
      properties:
        code:
          type: string
          writeOnly: true
          description: Single-use, PKCE-bound authorization code from the provider redirect.
        state:
          type: string
          description: Opaque CSRF value from the authorization request, echoed by the provider.
    CreateMcpProviderRequest:
      x-go-generate: true
      type: object
      additionalProperties: false
      description: |
        Create overlay row. Default enabled=false. Provide source=recipe with recipe_id, or source=custom with id and url or command.
      required:
        - source
      properties:
        source:
          type: string
          enum:
            - recipe
            - custom
        recipe_id:
          type: string
          pattern: ^[a-zA-Z0-9_.-]+$
        id:
          type: string
          pattern: ^[a-zA-Z0-9_.-]+$
        enabled:
          x-go-pointer: true
          type: boolean
        tool_loading:
          $ref: '#/components/schemas/McpToolLoading'
        command:
          type: string
        args:
          type: array
          items:
            type: string
        url:
          type: string
          description: |
            HTTP MCP URL. A host:port without a scheme is accepted: loopback becomes http://, any other host becomes https://. Remote http:// is refused.
        env:
          type: object
          writeOnly: true
          additionalProperties:
            type: string
          description: Stdio env map (write-only; values never returned on GET).
        headers:
          type: object
          writeOnly: true
          additionalProperties:
            type: string
          description: Static HTTP headers (write-only).
        token:
          type: string
          writeOnly: true
          description: |
            Write-only static token. Placement follows credential_wire (default Authorization: Bearer).
        credential_wire:
          $ref: '#/components/schemas/McpCredentialWire'
        credential_header:
          type: string
          description: Header name when credential_wire is header.
    McpRecipeAuth:
      type: string
      enum:
        - static_token
        - oauth
        - none
    McpCredentialWire:
      type: string
      enum:
        - bearer
        - token_token
        - header
      description: |
        How a stored static token is placed on the HTTP request. bearer is Authorization: Bearer. token_token is Authorization: Token token=. header uses credential_header as the name and the token as the value.
    McpRecipe:
      x-go-generate: true
      type: object
      required:
        - id
        - label
        - auth
        - added
        - project_ok
        - class
      additionalProperties: false
      properties:
        id:
          type: string
          pattern: ^[a-zA-Z0-9_.-]+$
        label:
          type: string
        hint:
          type: string
        docs_url:
          type: string
        url:
          type: string
        command:
          type: string
        args:
          type: array
          items:
            type: string
        auth:
          $ref: '#/components/schemas/McpRecipeAuth'
        credential_label:
          type: string
        credential_hint:
          type: string
        credential_wire:
          $ref: '#/components/schemas/McpCredentialWire'
        credential_header:
          type: string
        env_keys:
          type: array
          description: |
            Optional stdio env key names the recipe expects the operator to fill after add (labels only; values are never shipped).
          items:
            $ref: '#/components/schemas/McpRecipeEnvKey'
        class:
          $ref: '#/components/schemas/McpProviderClass'
        added:
          type: boolean
          description: Already present in this catalog scope.
        project_ok:
          x-go-name: ProjectOK
          type: boolean
          description: |
            True when a project overlay could declare this recipe (loopback HTTP, no credentials). Remote and stdio recipes are device-only.
    McpRecipeCatalogResponse:
      x-go-generate: true
      type: object
      required:
        - recipes
      additionalProperties: false
      properties:
        recipes:
          type: array
          items:
            $ref: '#/components/schemas/McpRecipe'
    ModelPolicy:
      x-go-generate: true
      type: object
      description: An absent coordinator or lite assigns no model at this layer.
      required:
        - agent_pool
      additionalProperties: false
      properties:
        coordinator:
          x-go-type: '*ModelRefDTO'
          $ref: '#/components/schemas/ModelRef'
        lite:
          x-go-type: '*ModelRefDTO'
          $ref: '#/components/schemas/ModelRef'
        agent_pool:
          x-go-type: AgentPoolDTO
          $ref: '#/components/schemas/AgentPool'
        thinking_overrides:
          type: array
          items:
            $ref: '#/components/schemas/ThinkingOverride'
          description: Overrides keyed by provider and model; omitted pairs inherit.
    ModelPolicyPatch:
      x-go-generate: true
      type: object
      minProperties: 1
      additionalProperties: false
      description: Omitted keys stay stored. A null coordinator or lite clears this layer's assignment so it inherits. A present thinking_overrides array replaces this layer's overrides; an empty array restores inheritance.
      properties:
        coordinator:
          x-go-type: '*ModelRefDTO'
          oneOf:
            - $ref: '#/components/schemas/ModelRef'
            - type: 'null'
        lite:
          x-go-type: '*ModelRefDTO'
          oneOf:
            - $ref: '#/components/schemas/ModelRef'
            - type: 'null'
        agent_pool:
          x-go-type: '*AgentPoolDTO'
          $ref: '#/components/schemas/AgentPool'
        thinking_overrides:
          x-go-pointer: true
          type: array
          items:
            $ref: '#/components/schemas/ThinkingOverride'
    ModelRef:
      type: object
      required:
        - provider_id
        - model
      additionalProperties: false
      properties:
        provider_id:
          type: string
          pattern: ^[a-zA-Z0-9_.-]+$
        model:
          type: string
    ApprovalCategory:
      type: string
      enum:
        - tool
        - command
        - mcp
        - path
        - host
        - write_root
        - host_resource
    ApprovalConfigResponse:
      x-go-generate: true
      x-go-field-order:
        - scope
        - rules
        - merged_from
        - managed_rules
        - approval_posture
        - ai_rationale_enabled
        - never_ask
        - field_sources
        - defaults
      type: object
      required:
        - scope
        - rules
        - managed_rules
        - merged_from
        - ai_rationale_enabled
        - never_ask
      additionalProperties: false
      properties:
        scope:
          $ref: '#/components/schemas/SettingsScope'
        rules:
          type: array
          items:
            $ref: '#/components/schemas/ApprovalRule'
        managed_rules:
          type: array
          description: Read-only effective approval rules contributed by extension packs. Project entries are additive and can never replace device entries.
          items:
            $ref: '#/components/schemas/ManagedApprovalRule'
        merged_from:
          type: array
          items:
            type: string
        approval_posture:
          x-go-type: string
          $ref: '#/components/schemas/ApprovalPosture'
          description: Effective ask-line (light|balanced|strict), after project overlay merge.
        ai_rationale_enabled:
          x-go-name: AIRationaleEnabled
          type: boolean
          description: |
            Effective AI rationale toggle after merge. Default true.
        never_ask:
          type: boolean
          description: |
            Effective never-ask override after merge. True means the approval layer is disabled outright — no discretionary, substrate, secret-screen, or detection asks. Global-only: a project overlay may restore asking but can never set this true. Default false.
        field_sources:
          x-go-pointer: true
          $ref: '#/components/schemas/ApprovalFieldSources'
          description: |
            Present on project scope. default = inherit Settings; override = project set.
        defaults:
          x-go-pointer: true
          $ref: '#/components/schemas/ApprovalDefaults'
          description: |
            Present on project scope. Device-global effective values for the following-Settings summary when the project override is disabled.
    DetectionPackList:
      x-go-generate: true
      type: object
      required:
        - packs
        - rejected
      additionalProperties: false
      properties:
        packs:
          type: array
          items:
            $ref: '#/components/schemas/DetectionPack'
        rejected:
          type: object
          additionalProperties:
            $ref: '#/components/schemas/DetectionPackRejectedRow'
          description: |
            Rows in this project's detection-packs.yaml overlay the merge refused, keyed by pack id. Present only when the request named a project whose overlay applies; a refused row changed nothing, so without this a pack the person believes they turned off is still running.
    DetectionPack:
      x-go-generate: true
      type: object
      required:
        - id
        - label
        - description
        - source
        - enabled
        - removable
        - rules
      additionalProperties: false
      properties:
        id:
          type: string
          pattern: ^[a-z0-9][a-z0-9-]{1,39}$
        label:
          type: string
        description:
          type: string
        source:
          type: string
          enum:
            - bundled
            - generated
            - extension
            - device
          description: |
            Where the pack's bytes came from. bundled and generated ship inside the app, extension arrives with an installed extension pack, and device is a folder imported into the configuration directory.
        provider_pack_id:
          type: string
          pattern: ^[a-zA-Z0-9_-][a-zA-Z0-9_.-]*(/[a-zA-Z0-9_-][a-zA-Z0-9_.-]*)*$
          description: |
            Extension pack carrying this detection pack. Absent only for an imported folder, which belongs to no extension.
        removable:
          type: boolean
          description: |
            Whether this pack can be deleted from the detections catalog. A pack an extension provides is removed by uninstalling that pack.
        enabled:
          type: boolean
        equivalent_binaries:
          type: array
          items:
            type: string
          description: |
            Drop-in alternatives this pack's rules also match, flattened from the pack's equivalents map. Display only.
        rules:
          type: array
          items:
            $ref: '#/components/schemas/DetectionRuleSummary'
        load_warnings:
          type: array
          items:
            type: string
          description: |
            What this pack could not load — a rule disabled in extension state, refused as a foreign contribution, or that would not parse.
    DetectionRuleSummary:
      x-go-generate: true
      type: object
      required:
        - id
        - title
        - level
        - supported
      additionalProperties: false
      properties:
        id:
          type: string
          pattern: ^[a-z0-9_-]+$
        title:
          type: string
        description:
          type: string
        level:
          type: string
          enum:
            - informational
            - low
            - medium
            - high
            - critical
        supported:
          type: boolean
        unsupported_reason:
          type: string
        references:
          type: array
          items:
            type: string
        recent_asks:
          type: integer
          minimum: 1
          description: Approval asks for this rule since the app started. Process-lifetime only; not persisted across restarts.
    DetectionMatch:
      x-go-generate: true
      type: object
      required:
        - pack_id
        - rule_id
        - rule_title
        - level
        - correlation_id
      additionalProperties: false
      properties:
        pack_id:
          type: string
          pattern: ^[a-z0-9][a-z0-9-]{1,39}$
        rule_id:
          type: string
          pattern: ^[a-z0-9_-]+$
        rule_title:
          type: string
        level:
          type: string
          enum:
            - medium
            - high
            - critical
        correlation_id:
          type: string
          pattern: ^[a-z0-9_-]+$
          description: Stable task-bounded related-alert grouping id. Informational only; never reusable authority.
    DetectionPackImportRequest:
      x-go-generate: true
      type: object
      required:
        - path
      additionalProperties: false
      properties:
        path:
          type: string
          description: Absolute path to a folder containing pack.yaml.
        dry_run:
          type: boolean
        replace:
          type: boolean
    DetectionPackImportResult:
      x-go-generate: true
      type: object
      required:
        - dry_run
        - pack
        - rejected_rules
        - ignored
      additionalProperties: false
      properties:
        dry_run:
          type: boolean
        pack:
          $ref: '#/components/schemas/DetectionPack'
        rejected_rules:
          x-go-type: '[]DetectionPackRejectedRule'
          type: array
          items:
            type: object
            required:
              - file
              - reason
            additionalProperties: false
            properties:
              file:
                type: string
              reason:
                type: string
        ignored:
          type: array
          items:
            type: string
        rehearsal:
          type: array
          items:
            type: string
          description: |
            Cases the folder's own fixtures.yaml declared that did not hold when run through the production adapters. Informational — the pack still imports, because a rule that misses only means silence.
    UpdateDetectionPackRequest:
      x-go-generate: true
      type: object
      additionalProperties: false
      minProperties: 1
      properties:
        enabled:
          x-go-pointer: true
          type: boolean
    ApprovalFieldSources:
      x-go-generate: true
      type: object
      required:
        - approval_posture
        - ai_rationale_enabled
        - never_ask
      additionalProperties: false
      properties:
        approval_posture:
          x-go-type: string
          $ref: '#/components/schemas/ApprovalFieldSource'
        ai_rationale_enabled:
          x-go-type: string
          x-go-name: AIRationaleEnabled
          $ref: '#/components/schemas/ApprovalFieldSource'
        never_ask:
          x-go-type: string
          $ref: '#/components/schemas/ApprovalFieldSource'
    ApprovalFieldSource:
      type: string
      enum:
        - default
        - override
    ApprovalDefaults:
      x-go-generate: true
      type: object
      required:
        - approval_posture
        - ai_rationale_enabled
        - never_ask
      additionalProperties: false
      properties:
        approval_posture:
          x-go-type: string
          $ref: '#/components/schemas/ApprovalPosture'
        ai_rationale_enabled:
          x-go-name: AIRationaleEnabled
          type: boolean
        never_ask:
          type: boolean
          description: Device-global disable state before a project restoration override.
    ApprovalEffect:
      type: string
      enum:
        - deny
        - ask
    ApprovalPosture:
      type: string
      description: |
        Ask-line. light asks only for actions that remove recovery (substrate boundary requests, outbound secret screen, critical detections); balanced adds high-confidence high-severity signals and new destinations after external-content ingest; strict adds MCP and all new-host confirmation. Disabling approvals entirely is never_ask, not a posture.
      enum:
        - light
        - balanced
        - strict
    LandedChangeScope:
      type: string
      description: |
        Landed-change scan width. path_scoped scans the changed paths; full_root scans the project root.
      enum:
        - path_scoped
        - full_root
    ApprovalRule:
      x-go-generate: true
      type: object
      required:
        - category
        - pattern
        - effect
      additionalProperties: false
      properties:
        category:
          $ref: '#/components/schemas/ApprovalCategory'
        pattern:
          type: string
        effect:
          $ref: '#/components/schemas/ApprovalEffect'
    ApprovalGrant:
      x-go-generate: true
      type: object
      required:
        - id
        - scope
        - category
        - pattern
        - title
        - coverage
        - granted_at
        - expires_when
        - reask_when
      additionalProperties: false
      description: |
        One revocable host-minted approval lease. Listed by GET /v1/approval-grants and removed by POST /v1/approval-grants/revoke. Unix socket leases use category socket_path with approved_path and resolved_path. Managed-secret leases name their secrets and recipients; local connection leases remain separately revocable.
      properties:
        elevated_effects:
          description: Host-classified elevated authority; empty for ordinary approvals.
          type: array
          items:
            $ref: '#/components/schemas/ElevatedAccessEffect'
        secret_names:
          type: array
          description: Display names of the protected values covered by this lease.
          items:
            type: string
        secret_recipients:
          type: array
          description: Reviewed receivers, without secret values or fingerprints.
          items:
            $ref: '#/components/schemas/ApprovalSecretRecipient'
        id:
          type: string
          pattern: ^[a-z0-9_-]+$
          description: Opaque id bound to the complete authority and security witness.
        scope:
          $ref: '#/components/schemas/ApprovalGrantScope'
        category:
          $ref: '#/components/schemas/ApprovalGrantCategory'
        pattern:
          type: string
          description: |
            Searchable host label. For socket_path this mirrors approved_path; authority matching uses approved_path and resolved_path. Secret releases expose only a count label; their fingerprints stay host-only.
        title:
          type: string
          description: Host-rendered bounded-lease label.
        granted_at:
          type: string
          format: date-time
          description: When the host minted the lease.
        expires_at:
          x-go-pointer: true
          type: string
          format: date-time
        coverage:
          type: string
          description: Reviewed host copy describing exactly what the lease covers.
        expires_when:
          type: string
        reask_when:
          type: string
        project_id:
          type: string
          format: uuid
          description: |
            Canonical identity of the project a project-scoped lease binds to. Grants group by this, not by folder: attaching, detaching or moving a project's folders never moves or invalidates its saved approvals.
        project_dir:
          type: string
          description: |
            A folder the lease was granted from, for display only. Absent for a project with no folders attached.
        unavailable:
          type: boolean
          description: |
            True when the granted target no longer exists on this host — a write_root whose path fails Lstat, or a socket_path whose approved path no longer resolves to a usable socket. Display and bulk-clear only; never auto-revoked.
        action_count:
          type: integer
          minimum: 1
          description: |
            Number of exact actions an action_set lease covers. Exact action identities are host-only opaque digests and never cross the wire.
        resource_ids:
          x-go-name: ResourceIDs
          type: array
          items:
            type: string
          description: |
            Host resource ids for host_resource leases. Coverage composes per id.
        approved_path:
          type: string
          description: Exact AF_UNIX path the human approved (socket_path grants).
        resolved_path:
          type: string
          description: Current host-resolved target for the approved path.
        repointed:
          type: boolean
          description: |
            True when re-resolution succeeds but the target differs from the stored resolved_path (grant is not applied until renewed).
        expired:
          type: boolean
          description: True when expires_at is in the past (display; list may still return it briefly).
        effective_authority:
          $ref: '#/components/schemas/SocketCapabilityAuthority'
        authority_warning:
          type: string
          description: |
            Host copy stating the service runs outside the sandbox and may act on files, processes, devices, and network.
        source:
          type: string
          description: Origin of the lease (settings, checkpoint, recovery).
        chat_session_id:
          type: string
          format: uuid
          description: The chat's root session id when scope is chat.
        session_title:
          type: string
          description: Chat title on chat-scoped rows, for grouping in Settings.
        revoke_applies_to:
          type: string
          description: |
            Host copy for revoke consequences. Socket grants use "Applies to the next command or terminal. Already-running processes retain access."
    ApprovalGrantScope:
      type: string
      enum:
        - chat
        - project
        - device
    ApprovalGrantCategory:
      type: string
      description: Categories that can carry reusable lease authority.
      enum:
        - tool
        - mcp
        - path
        - host
        - write_root
        - action_set
        - socket_path
        - host_resource
        - secret
        - secret_redact
        - direct_ip
        - local_listen
        - loopback_connect
        - egress_command
        - execution_capability
        - agent_policy
        - package_coordinate
    ApprovalGrantsResponse:
      x-go-generate: true
      type: object
      required:
        - grants
      additionalProperties: false
      properties:
        grants:
          type: array
          items:
            $ref: '#/components/schemas/ApprovalGrant'
        quiets:
          type: object
          additionalProperties:
            $ref: '#/components/schemas/AskQuiet'
          description: Live ask quiets for the listed chats (suppression, not authority).
    CreateApprovalGrantRequest:
      x-go-generate: true
      type: object
      required:
        - category
        - scope
      additionalProperties: false
      description: |
        Deliberate Settings creation of one durable grant with scope project or device. Category socket_path takes socket_path; category host takes host_pattern. Execution cards and model tools cannot call this.
      properties:
        category:
          $ref: '#/components/schemas/ApprovalGrantCategory'
        scope:
          $ref: '#/components/schemas/ApprovalGrantScope'
        socket_path:
          type: string
          description: Absolute path to an existing AF_UNIX socket on this device.
        host_pattern:
          type: string
          description: |
            A host or registrable-site pattern the host derived (for example `*.github.com`), optionally bound to one tunnel port (`*.github.com:443`). The host normalizes it through the same derivation an approval card uses; a pattern it would not mint is refused.
        project_id:
          type: string
          format: uuid
          description: Required for project scope.
        expires_at:
          x-go-pointer: true
          type: string
          format: date-time
          description: Optional expiry. Omitted, a project-scope grant expires 7 days after creation and a device-scope grant 30 days after.
    ResolveSocketGrantRequest:
      x-go-generate: true
      type: object
      required:
        - socket_path
      additionalProperties: false
      description: |
        Resolve one absolute socket path for the deliberate Settings create flow before confirmation. No grant is written.
      properties:
        socket_path:
          type: string
    ResolveSocketGrantResponse:
      x-go-generate: true
      type: object
      required:
        - approved_path
        - resolved_path
        - effective_authority
        - authority_warning
      additionalProperties: false
      properties:
        approved_path:
          type: string
        resolved_path:
          type: string
        effective_authority:
          $ref: '#/components/schemas/SocketCapabilityAuthority'
        authority_warning:
          type: string
    ProviderMeta:
      x-go-generate: true
      type: object
      required:
        - id
        - kind
        - configured
        - credential_present
        - requires_api_key
        - features
        - models
      additionalProperties: false
      properties:
        id:
          type: string
          pattern: ^[a-zA-Z0-9_.-]+$
        kind:
          type: string
          description: Provider kind shared by related instances.
        label:
          type: string
          description: Human-facing display name for the AI provider instance.
        base_url:
          type: string
          description: URL, SDK region, or empty derived value according to endpoint_style.
        endpoint_style:
          x-go-type: string
          $ref: '#/components/schemas/ProviderEndpointStyle'
        configured:
          type: boolean
          description: |
            True when the provider can make API calls. Keyed AI providers require a non-empty credential saved via PUT /v1/providers/{provider_id}/credential (environment variables do not configure). Local keyless providers are ready without credentials; ambient providers must resolve their declared credential chain. List membership is providers.local.yaml — Den does not keep a parallel hide allowlist.
        credential_present:
          type: boolean
          description: True when credentials are saved for this provider instance.
        credential_source:
          type: string
          enum:
            - none
            - stored
            - ambient
          description: Credential source selected for this instance; environment hints are never a source.
        credential_verified_at:
          x-go-pointer: true
          type: string
          format: date-time
          description: When the provider last accepted this instance's credentials on a live call. Omitted until the host has observed an accepted call since the credentials last changed.
        requires_api_key:
          type: boolean
          description: |
            True when the AI provider needs a Settings-saved API key; false for keyless/local providers and for instances of an ambient_auth kind that selected the ambient credential chain. Catalog api_key_env is a docs hint only and does not auto-configure.
        ambient_auth:
          type: string
          description: |
            Names the credential chain this kind can authenticate with instead of a stored key (aws-sdk-chain, google-adc). Empty when a stored key is the only way in. Setting requires_api_key false on an instance of such a kind selects the ambient chain — there is no separate credential-mode field.
        secret_screen_trusted:
          type: boolean
          description: |
            True while this instance is trusted with detected credentials, so a request to it raises no secret card and sends the detected value unchanged. Transcript redaction, managed-secret references, and the credential vault are unaffected. The decision is bound to the resolved destination (kind, endpoint, endpoint style, ambient auth); changing any of those withdraws it. Omitted means false.
        platforms:
          type: array
          description: |
            Product host platforms this kind supports (macos, windows, linux). Empty or omitted means all platforms. Ship providers.yaml is SSOT; the host omits unsupported kinds/instances from list projections.
          items:
            type: string
            enum:
              - macos
              - windows
              - linux
        features:
          $ref: '#/components/schemas/ProviderFeatures'
        models:
          type: array
          items:
            $ref: '#/components/schemas/ProviderModelMeta'
        configured_models:
          type: array
          description: Explicit model or deployment rows stored for this provider instance before live availability is applied.
          items:
            $ref: '#/components/schemas/ProviderModelMeta'
        usage_quota:
          x-go-pointer: true
          $ref: '#/components/schemas/ProviderUsageQuota'
        ready_to_assign:
          type: boolean
          description: |
            True when the provider is configured and has at least one ConversationEligible model with positive capability evidence for at least one policy slot, after role exclusions (exists-slot).
        catalog_authoritative:
          type: boolean
          description: |
            True when this kind is mapped to models.dev and a usable modelfeed document is present (catalog-first merge).
        catalog_status:
          type: string
          enum:
            - ok
            - stale
            - unavailable
          description: models.dev document freshness for catalog merge.
        discovery_status:
          type: string
          enum:
            - ok
            - empty
            - error
            - skipped
          description: Live /models discovery outcome for this list projection.
    ProviderModelMeta:
      x-go-generate: true
      type: object
      required:
        - id
        - capabilities
        - eligibility
      additionalProperties: false
      properties:
        thinking:
          $ref: '#/components/schemas/ThinkingCapabilities'
        id:
          type: string
          pattern: ^[a-zA-Z0-9_.-]+$
        input_per_1k_nano_usd:
          x-go-pointer: true
          x-go-name: InputPer1KNanoUSD
          type: integer
          format: int64
        output_per_1k_nano_usd:
          x-go-pointer: true
          x-go-name: OutputPer1KNanoUSD
          type: integer
          format: int64
        priced_as:
          type: string
          description: Underlying catalog model id used to price a customer-named deployment.
        context_length:
          type: integer
          description: The model's usable context window in tokens when known (pinned in config or discovered from the host); omitted when unknown.
        capabilities:
          $ref: '#/components/schemas/ProviderModelCapabilities'
        eligibility:
          $ref: '#/components/schemas/ModelRoleEligibilitySet'
    ProviderProbeResult:
      x-go-generate: true
      type: object
      required:
        - ok
      additionalProperties: false
      properties:
        ok:
          x-go-name: OK
          type: boolean
        code:
          $ref: '#/components/schemas/ApiErrorCode'
        message:
          type: string
        latency_ms:
          type: integer
          format: int64
    UpdateApprovalsSettingsRequest:
      x-go-generate: true
      type: object
      additionalProperties: false
      description: |
        Settings patch for approvals. Omit rules to preserve the global overlay. When rules is present, only deny/ask entries are accepted (any effect:allow → 400); overlay allow grants are kept. Durable allows mutate only via checkpoint resolve + POST …/grants/revoke.
      properties:
        rules:
          type: array
          items:
            $ref: '#/components/schemas/ApprovalRule'
          description: |
            Optional. Omit to preserve overlay rules. Present = replace overlay deny/ask only (allows rejected).
        approval_posture:
          x-go-type: '*string'
          oneOf:
            - $ref: '#/components/schemas/ApprovalPosture'
            - type: 'null'
          description: |
            When present, sets posture for the request scope (global or project). Omitted preserves the current overlay value (project inherits global when unset). Null resets to inherit from global (project scope only).
        ai_rationale_enabled:
          x-go-pointer: true
          x-go-name: AIRationaleEnabled
          type:
            - boolean
            - 'null'
          description: |
            When present, toggles AI rationale on approval cards for the request scope. Omitted preserves the current value (project inherits global when unset). Null resets to inherit from global (project scope only).
        never_ask:
          x-go-pointer: true
          type:
            - boolean
            - 'null'
          description: |
            Global scope: true disables the approval layer outright and false turns it back on. Project scope: false may restore asking for that project; true is rejected because repo content must never be able to disable approvals. Omitted preserves the current overlay value. Default false. Null resets to inherit from global (project scope only).
    ResolveCheckpointRequest:
      oneOf:
        - $ref: '#/components/schemas/ToolApprovalResolveBody'
        - $ref: '#/components/schemas/ContentApplyResolveBody'
      discriminator:
        propertyName: kind
        mapping:
          tool_approval: '#/components/schemas/ToolApprovalResolveBody'
          content_apply: '#/components/schemas/ContentApplyResolveBody'
    SetProviderCredentialRequest:
      x-go-generate: true
      type: object
      required:
        - api_key
      additionalProperties: false
      properties:
        api_key:
          type: string
          minLength: 1
          writeOnly: true
    SettingsLimitsPatch:
      x-go-generate: true
      description: Merge selected limits. Omitted fields remain unchanged; null restores the global value for project scope or the built-in default for global scope.
      type: object
      additionalProperties: false
      properties:
        max_iterations:
          type:
            - integer
            - 'null'
          minimum: 1
          description: Tool-loop iteration cap per prompt turn.
        overlay_promote_max_iterations:
          type:
            - integer
            - 'null'
          minimum: 1
          description: Iteration cap for coordinator overlay promote surface.
        max_tool_result_bytes:
          type:
            - integer
            - 'null'
          minimum: 0
          description: Tool results larger than this are truncated before LLM history append.
        coordinator_loop:
          x-go-pointer: true
          type:
            - boolean
            - 'null'
          description: When true (default), host may auto-prompt coordinator after worker legs.
        max_coordinator_loop_cycles:
          type:
            - integer
            - 'null'
          minimum: 0
          description: Per workflow run cap on coordinator loop wake cycles.
        llm_turn_timeout_ms:
          type:
            - integer
            - 'null'
          minimum: 1000
          multipleOf: 1000
          description: Wall-clock cap (milliseconds) for one LLM stream completion, in whole seconds.
        coordinator_host_turn_timeout_ms:
          type:
            - integer
            - 'null'
          minimum: 1000
          multipleOf: 1000
          description: Wall-clock cap (milliseconds) for one host-initiated coordinator prompt turn, in whole seconds.
        coordinator_max_sleep_ms:
          type:
            - integer
            - 'null'
          minimum: 1000
          multipleOf: 1000
          description: Maximum wait() sleep duration (milliseconds) for the coordinator loop, in whole seconds.
        await_parent_workers_timeout_ms:
          type:
            - integer
            - 'null'
          minimum: 1000
          multipleOf: 1000
          description: Wall-clock cap (milliseconds) blocking until parent-session workers finish, in whole seconds.
        worker_tool_budget_default:
          type:
            - integer
            - 'null'
          minimum: 2
          maximum: 120
          description: Worker tool-round ceiling applied when neither the task nor its planned leg sets max_tool_loops. The host never raises a ceiling on its own; a worker asks with request_budget and the coordinator grants with extend_worker_budget.
        worker_tool_budget_min:
          type:
            - integer
            - 'null'
          minimum: 2
          maximum: 120
          description: Smallest worker tool-round ceiling the coordinator may set (host floor is 2).
        worker_tool_budget_max:
          type:
            - integer
            - 'null'
          minimum: 2
          maximum: 120
          description: Largest worker tool-round ceiling the coordinator may set or grant.
        session_spend_ceiling_nano_usd:
          x-go-name: SessionSpendCeilingNanoUSD
          type:
            - integer
            - 'null'
          format: int64
          minimum: 0
          description: Opt-in per-session nano-USD spend limit (enforced only when spend_ceiling_enabled is true and spend is priced).
        spend_warning_ratio:
          type:
            - number
            - 'null'
          minimum: 0.1
          maximum: 0.95
          description: Share of the session spend limit at which the host emits a one-shot warning.
        spend_ceiling_enabled:
          type:
            - boolean
            - 'null'
          description: When true and session_spend_ceiling_nano_usd is greater than zero, land and pause the loop once estimated session spend reaches the ceiling.
        spend_soft_stop:
          x-go-pointer: true
          type:
            - boolean
            - 'null'
          description: When true (default), a coordinator that crosses the ceiling while already running receives one final tool-capable wind-down round. A response without tool calls is final; after tool calls, the host requests one prose-only closeout. New prompts and worker sessions remain blocked at the ceiling.
    SettingsLimitsResponse:
      x-go-generate: true
      type: object
      required:
        - scope
        - max_iterations
        - max_tool_result_bytes
        - overlay_promote_max_iterations
        - llm_turn_timeout_ms
        - coordinator_host_turn_timeout_ms
        - coordinator_max_sleep_ms
        - await_parent_workers_timeout_ms
        - worker_tool_budget_default
        - worker_tool_budget_min
        - worker_tool_budget_max
        - spend_soft_stop
        - merged_from
      additionalProperties: false
      properties:
        scope:
          $ref: '#/components/schemas/SettingsScope'
        max_iterations:
          type: integer
          description: Tool-loop iteration cap per prompt turn.
        overlay_promote_max_iterations:
          type: integer
          description: Iteration cap for coordinator overlay promote surface.
        max_tool_result_bytes:
          type: integer
          description: Tool results larger than this are truncated before LLM history append.
        coordinator_loop:
          x-go-pointer: true
          type: boolean
          description: When true (default), host may auto-prompt coordinator after worker legs.
        max_coordinator_loop_cycles:
          type: integer
          description: Per workflow run cap on coordinator loop wake cycles.
        llm_turn_timeout_ms:
          type: integer
          minimum: 1000
          multipleOf: 1000
          description: Wall-clock cap (milliseconds) for one LLM stream completion, in whole seconds.
        coordinator_host_turn_timeout_ms:
          type: integer
          minimum: 1000
          multipleOf: 1000
          description: Wall-clock cap (milliseconds) for one host-initiated coordinator prompt turn, in whole seconds.
        coordinator_max_sleep_ms:
          type: integer
          minimum: 1000
          multipleOf: 1000
          description: Maximum wait() sleep duration (milliseconds) for the coordinator loop, in whole seconds.
        await_parent_workers_timeout_ms:
          type: integer
          minimum: 1000
          multipleOf: 1000
          description: Wall-clock cap (milliseconds) blocking until parent-session workers finish, in whole seconds.
        worker_tool_budget_default:
          type: integer
          minimum: 2
          maximum: 120
          description: Worker tool-round ceiling applied when neither the task nor its planned leg sets max_tool_loops. The host never raises a ceiling on its own; a worker asks with request_budget and the coordinator grants with extend_worker_budget.
        worker_tool_budget_min:
          type: integer
          minimum: 2
          maximum: 120
          description: Smallest worker tool-round ceiling the coordinator may set (host floor is 2).
        worker_tool_budget_max:
          type: integer
          minimum: 2
          maximum: 120
          description: Largest worker tool-round ceiling the coordinator may set or grant.
        session_spend_ceiling_nano_usd:
          x-go-name: SessionSpendCeilingNanoUSD
          type: integer
          format: int64
          minimum: 0
          description: Opt-in per-session nano-USD spend limit (enforced only when spend_ceiling_enabled is true and spend is priced).
        spend_warning_ratio:
          type: number
          minimum: 0.1
          maximum: 0.95
          description: Share of the session spend limit at which the host emits a one-shot warning.
        spend_ceiling_enabled:
          type: boolean
          description: When true and session_spend_ceiling_nano_usd is greater than zero, land and pause the loop once estimated session spend reaches the ceiling.
        spend_soft_stop:
          type: boolean
          default: true
          description: When true, a coordinator that crosses the ceiling while already running receives one final tool-capable wind-down round. A response without tool calls is final; after tool calls, the host requests one prose-only closeout. New prompts and worker sessions remain blocked at the ceiling.
        merged_from:
          type: array
          items:
            type: string
    ContentReviewRule:
      x-go-generate: true
      type: object
      required:
        - path
      additionalProperties: false
      properties:
        tool:
          type: string
        path:
          type: string
        reason:
          type: string
        disabled_until_at:
          x-go-pointer: true
          type: string
          format: date-time
          description: Optional pause on this rule. While this instant is in the future, edits matching the rule apply without content review; afterwards the rule asks again. Set by the edit-review card's time-boxed skip.
    ReviewSettingsResponse:
      x-go-generate: true
      type: object
      required:
        - scope
        - review_paths
        - merged_from
      additionalProperties: false
      properties:
        scope:
          $ref: '#/components/schemas/SettingsScope'
        review_paths:
          type: array
          items:
            $ref: '#/components/schemas/ContentReviewRule'
        merged_from:
          type: array
          items:
            type: string
    UpdateReviewSettingsRequest:
      x-go-generate: true
      type: object
      additionalProperties: false
      properties:
        review_paths:
          type: array
          items:
            $ref: '#/components/schemas/ContentReviewRule'
    VerifySettingsResponse:
      x-go-generate: true
      type: object
      required:
        - scope
        - test
        - verify_path
        - backend_configured
      additionalProperties: false
      properties:
        scope:
          $ref: '#/components/schemas/SettingsScope'
        test:
          type: string
        verify_path:
          type: string
        detected_command:
          type: string
        detected_source:
          type: string
        backend_configured:
          type: boolean
        suggestion_state:
          $ref: '#/components/schemas/VerifySuggestionState'
        dismissed:
          type: boolean
    UpdateVerifySettingsRequest:
      x-go-generate: true
      type: object
      additionalProperties: false
      properties:
        test:
          type: string
    SecurityScannersSettingsResponse:
      x-go-generate: true
      type: object
      required:
        - enabled
        - merged_from
        - landed_change_scope
        - source_verify
      additionalProperties: false
      properties:
        enabled:
          type: boolean
          description: |
            Main switch for Security scanners. When false, Context → Security scanners is hidden, project-open and tool/workflow scans do not run, and scan tools reject.
        merged_from:
          type: array
          items:
            type: string
        landed_change_scope:
          $ref: '#/components/schemas/LandedChangeScope'
          description: |
            Effective landed-change scan scope.
        source_verify:
          $ref: '#/components/schemas/SourceVerify'
          description: |
            Effective source re-read policy for scan publications.
    UpdateSecurityScannersSettingsRequest:
      x-go-generate: true
      type: object
      additionalProperties: false
      properties:
        enabled:
          type: boolean
        landed_change_scope:
          $ref: '#/components/schemas/LandedChangeScope'
        source_verify:
          $ref: '#/components/schemas/SourceVerify'
    SettingsPricing:
      x-go-generate: true
      type: object
      additionalProperties: false
      properties:
        cost_tracking_enabled:
          type: boolean
        cost_tracking_since_at:
          x-go-pointer: true
          type: string
          format: date-time
          description: Present when tracking is on; stamped on off→on.
        sources:
          type: array
          description: 'Feed rows with at most one selected (`enabled: true`); tracking on requires exactly one.'
          items:
            $ref: '#/components/schemas/SettingsPricingSource'
    SettingsPricingSource:
      x-go-generate: true
      type: object
      required:
        - id
        - enabled
      additionalProperties: false
      properties:
        id:
          type: string
          pattern: ^[a-z0-9_-]+$
        enabled:
          type: boolean
          description: True for the single selected pricing source.
    PricingSourceMeta:
      x-go-generate: true
      type: object
      required:
        - id
        - label
        - kind
        - enabled
        - status
      additionalProperties: false
      properties:
        id:
          type: string
          pattern: ^[a-z0-9_-]+$
        label:
          type: string
        kind:
          type: string
        enabled:
          type: boolean
        status:
          $ref: '#/components/schemas/PricingSourceStatus'
        updated_at:
          x-go-pointer: true
          type: string
          format: date-time
        fetched_at:
          x-go-pointer: true
          type: string
          format: date-time
        model_count:
          type: integer
          minimum: 0
        refreshing:
          type: boolean
          description: A background fetch of this source is in flight; status and freshness still describe the last settled attempt.
    PricingSourceStatus:
      type: string
      enum:
        - ok
        - stale
        - error
        - offline
    SettingsPricingResponse:
      x-go-generate: true
      type: object
      required:
        - cost_tracking_enabled
        - sources
        - available_sources
      additionalProperties: false
      properties:
        cost_tracking_enabled:
          type: boolean
        cost_tracking_since_at:
          x-go-pointer: true
          type: string
          format: date-time
          description: Present when tracking is on; stamped on off→on.
        sources:
          type: array
          description: 'Catalog feed rows with at most one selected (`enabled: true`); tracking on requires exactly one.'
          items:
            $ref: '#/components/schemas/SettingsPricingSource'
        available_sources:
          type: array
          items:
            $ref: '#/components/schemas/PricingSourceMeta'
    TrustSurfaceId:
      type: string
      description: One surface of project-supplied configuration.
      enum:
        - agents_md
        - skills
        - project_settings
        - project_mcp
        - scan_config
        - prompt_overrides
        - extension_config
        - extension_suggestions
    TrustSurfaceGroup:
      type: string
      description: 'Groups surfaces by effect: steering, default replacement, or device install suggestion.'
      enum:
        - steering
        - replaces_defaults
        - suggestion
    TrustSurfaceSetting:
      x-go-generate: true
      type: object
      required:
        - id
        - label
        - group
        - enabled
      additionalProperties: false
      description: One configurable surface with its device-level switch.
      properties:
        id:
          $ref: '#/components/schemas/TrustSurfaceId'
        label:
          type: string
        group:
          $ref: '#/components/schemas/TrustSurfaceGroup'
        enabled:
          type: boolean
    TrustSettingsResponse:
      x-go-generate: true
      type: object
      required:
        - surfaces
      additionalProperties: false
      description: Device-wide off switches for what any project may supply. A quiet kill switch, not the primary UX — day-to-day choices are per project.
      properties:
        surfaces:
          type: array
          items:
            $ref: '#/components/schemas/TrustSurfaceSetting'
    UpdateTrustSettingsRequest:
      x-go-generate: true
      type: object
      additionalProperties: false
      properties:
        enabled:
          type: object
          additionalProperties:
            type: boolean
          description: Partial surface id to enabled; unnamed surfaces keep their value.
    TrustSurfaceItem:
      x-go-generate: true
      type: object
      required:
        - name
        - root_id
        - path
      additionalProperties: false
      description: One concrete thing a surface supplies.
      properties:
        name:
          type: string
        detail:
          type: string
          description: Human-readable qualifier — an MCP transport, a pack source.
        root_id:
          type: string
          format: uuid
          description: Attached project root containing this file item.
        path:
          type: string
          description: Root-relative path for file items.
        lines:
          type: integer
          description: Prose size hint for instruction files.
    ProjectTrustSurface:
      x-go-generate: true
      type: object
      required:
        - id
        - label
        - group
        - count
        - items
        - device_enabled
        - project_enabled
        - applying
        - seen
      additionalProperties: false
      description: One discovered surface joined to what the human enabled and last read.
      properties:
        id:
          $ref: '#/components/schemas/TrustSurfaceId'
        label:
          type: string
        group:
          $ref: '#/components/schemas/TrustSurfaceGroup'
        count:
          type: integer
          description: Number of discovered items. For skills this is what the loader will use, so a name shadowed across roots is counted once.
        items:
          type: array
          items:
            $ref: '#/components/schemas/TrustSurfaceItem'
        device_enabled:
          type: boolean
        project_enabled:
          type: boolean
        applying:
          type: boolean
          description: Whether the surface currently affects the project.
        seen:
          type: boolean
          description: Whether the tree matches the version last read. An unseen surface still applies; reading does not change what the agent gets.
    ProjectTrust:
      x-go-generate: true
      type: object
      required:
        - project_id
        - surfaces
        - unread_count
        - review
      additionalProperties: false
      description: What a project supplies, which of it applies, and what has moved since it was read. One read shared by the composer chip and Project configuration so those surfaces cannot disagree.
      properties:
        review:
          $ref: '#/components/schemas/ProjectTrustReview'
        unread_count:
          type: integer
          description: Distinct files whose current contents have not been marked read.
        project_id:
          type: string
          format: uuid
        surfaces:
          type: array
          items:
            $ref: '#/components/schemas/ProjectTrustSurface'
    ProjectTrustReview:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - id
        - project_id
        - changes
      description: The shared Trust comparison. Marking it read preserves its identity and files.
      properties:
        id:
          type: string
          format: uuid
        project_id:
          type: string
          format: uuid
        changes:
          type: array
          items:
            $ref: '#/components/schemas/TrustFileChange'
    TrustFileChange:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - id
        - root_id
        - root_label
        - path
        - surface_ids
        - kind
        - before
        - after
      properties:
        id:
          type: string
          format: uuid
        root_id:
          type: string
          format: uuid
          description: Root identity retained even after the root is detached.
        root_label:
          type: string
        path:
          type: string
        surface_ids:
          type: array
          items:
            $ref: '#/components/schemas/TrustSurfaceId'
        kind:
          type: string
          enum:
            - added
            - modified
            - removed
        before:
          type: string
          description: Exact retained text when Trust was previously opened; empty for an addition.
        after:
          type: string
          description: Exact text captured for this opening; empty for a removal.
    UpdateProjectTrustRequest:
      x-go-generate: true
      type: object
      additionalProperties: false
      minProperties: 1
      properties:
        enabled:
          type: object
          additionalProperties:
            type: boolean
          description: Partial surface id to enabled; unnamed surfaces keep their value.
    SettingsScope:
      type: string
      enum:
        - global
        - project
    UpdateMcpProviderRequest:
      x-go-generate: true
      type: object
      additionalProperties: false
      description: |
        Patch enablement and/or mutable connection fields.
      minProperties: 1
      properties:
        enabled:
          x-go-pointer: true
          type: boolean
        tool_loading:
          x-go-pointer: true
          $ref: '#/components/schemas/McpToolLoading'
        command:
          x-go-pointer: true
          type: string
        args:
          x-go-pointer: true
          type: array
          items:
            type: string
        url:
          x-go-pointer: true
          type: string
          description: |
            HTTP MCP URL. A host:port without a scheme is accepted: loopback becomes http://, any other host becomes https://. Remote http:// is refused.
        env:
          x-go-pointer: true
          type: object
          writeOnly: true
          additionalProperties:
            type: string
          description: Stdio env map (write-only; omit to leave unchanged).
        headers:
          x-go-pointer: true
          type: object
          writeOnly: true
          additionalProperties:
            type: string
          description: Static HTTP headers (write-only; omit to leave unchanged).
        token:
          x-go-pointer: true
          type: string
          writeOnly: true
          description: |
            Write-only static token. Placement follows credential_wire (default Authorization: Bearer).
        credential_wire:
          x-go-pointer: true
          $ref: '#/components/schemas/McpCredentialWire'
        credential_header:
          x-go-pointer: true
          type: string
          description: Header name when credential_wire is header.
    CreateProviderRequest:
      x-go-generate: true
      type: object
      required:
        - id
      additionalProperties: false
      properties:
        id:
          type: string
          pattern: ^[a-zA-Z0-9_.-]+$
          description: Instance id the caller chooses; unique among provider instances.
        kind:
          type: string
          description: Provider type for this instance, immutable afterward. Defaults to the instance id.
        base_url:
          type: string
          description: Omit to take the kind's catalog endpoint; empty for kinds whose endpoint is derived.
        label:
          type: string
          description: Human-facing display name. Empty displays the instance id.
        api_key_env:
          type: string
          description: |
            Optional catalog hint naming a conventional environment variable (docs / UI only). Keys are saved via the credential routes.
        requires_api_key:
          x-go-pointer: true
          type: boolean
          description: Whether this AI provider needs an API key. Omit to take the kind's default.
        secret_screen_trusted:
          x-go-pointer: true
          type: boolean
          description: True trusts the destination this instance resolves with detected credentials.
        models:
          type: array
          description: Configured models. Omit so live discovery fills the list.
          items:
            $ref: '#/components/schemas/ProviderModelConfig'
    UpdateProviderRequest:
      x-go-generate: true
      type: object
      additionalProperties: false
      properties:
        base_url:
          type:
            - string
            - 'null'
          description: Null restores the provider kind endpoint. Empty for kinds whose endpoint is derived.
        label:
          type:
            - string
            - 'null'
          description: Human-facing display name. Null restores the instance id.
        api_key_env:
          type:
            - string
            - 'null'
          description: |
            Optional catalog hint naming a conventional environment variable (docs / UI only). Does not configure or authenticate the AI provider — keys must be saved via the credential routes. Null restores the kind hint.
        requires_api_key:
          x-go-pointer: true
          type:
            - boolean
            - 'null'
          description: Whether this AI provider needs an API key. Null inherits the provider kind default.
        secret_screen_trusted:
          x-go-pointer: true
          type:
            - boolean
            - 'null'
          description: |
            True trusts the destination this update resolves with detected credentials; false withdraws it. Omit to keep the stored decision, which the host withdraws on its own when the update changes the destination. Null withdraws the stored trust decision.
        models:
          type:
            - array
            - 'null'
          description: Configured model overrides. Null clears stored overrides so live discovery supplies available models.
          items:
            $ref: '#/components/schemas/ProviderModelConfig'
    WebSearchProvider:
      type: string
      enum:
        - brave
        - marginalia
        - google_cse
        - tavily
        - kagi
        - serper
        - searxng
        - mwmbl
        - hn
        - wikipedia
        - nixos_wiki
        - gentoo_wiki
        - mankier
        - ietf_rfc
        - discourse_rust
        - discourse_golang
        - mdn
        - stackexchange
        - stackexchange_apple
        - stackexchange_superuser
        - stackexchange_askubuntu
        - stackexchange_serverfault
        - stackexchange_unix
        - microsoft_learn
        - arxiv
        - github
        - gitlab
        - gdelt
        - openalex
        - crossref
        - europepmc
        - wikidata
        - wiktionary
        - internetarchive
        - openlibrary
        - huggingface
        - crates_io
        - pkg_go_dev
        - npm
        - jsr
        - nuget
        - rubygems
        - maven_central
        - packagist
        - pub_dev
        - hex
        - cran
        - hackage
        - metacpan
        - nvd
        - css_tricks
        - caniuse
        - direct
    WebResearchProviderKind:
      type: string
      enum:
        - keyed
        - keyed_extra
        - keyless_endpoint
        - keyless
    WebResearchDirectCardContent:
      x-go-generate: true
      type: object
      required:
        - provider_id
        - kind
        - label
      additionalProperties: false
      properties:
        provider_id:
          type: string
          enum:
            - direct
        kind:
          type: string
          enum:
            - direct
        label:
          type: string
    WebResearchDirectStatus:
      x-go-generate: true
      type: object
      required:
        - configured
        - card
      additionalProperties: false
      properties:
        configured:
          type: boolean
        card:
          $ref: '#/components/schemas/WebResearchDirectCardContent'
    WebResearchProviderMeta:
      x-go-generate: true
      type: object
      required:
        - id
        - kind
        - label
        - roles
        - configured
        - credential_present
      additionalProperties: false
      properties:
        id:
          $ref: '#/components/schemas/WebSearchProvider'
        kind:
          $ref: '#/components/schemas/WebResearchProviderKind'
        label:
          type: string
        roles:
          type: array
          items:
            $ref: '#/components/schemas/WebResearchProviderRole'
        default_enabled:
          type: boolean
        configured:
          type: boolean
        credential_present:
          type: boolean
        credential_source:
          type: string
          enum:
            - none
            - stored
            - environment
        credential_slot:
          type: string
        allow_private_endpoint:
          type: boolean
          description: Catalog flag — when true, endpoint may use http and private/LAN addresses (still blocking loopback and link-local metadata).
        config:
          type: object
          additionalProperties:
            type: string
    WebResearchProvidersResponse:
      x-go-generate: true
      type: object
      required:
        - direct
        - providers
      additionalProperties: false
      properties:
        direct:
          $ref: '#/components/schemas/WebResearchDirectStatus'
        providers:
          type: array
          items:
            $ref: '#/components/schemas/WebResearchProviderMeta'
    WebResearchSettings:
      x-go-generate: true
      type: object
      required:
        - warming
        - guess_domains
        - search_enabled
        - enabled_providers
      additionalProperties: false
      properties:
        warming:
          type: boolean
          description: Background index warming from explicitly shared URLs and explicit web activity.
        guess_domains:
          type: boolean
          description: When results are thin, a model may scout hosts to fill the index.
        search_enabled:
          type: boolean
        enabled_providers:
          type: array
          description: Complete enabled provider set. An empty array enables no search provider.
          items:
            type: string
    UpdateWebResearchSettingsRequest:
      x-go-generate: true
      type: object
      additionalProperties: false
      properties:
        warming:
          x-go-pointer: true
          type: boolean
        guess_domains:
          x-go-pointer: true
          type: boolean
        search_enabled:
          x-go-pointer: true
          type: boolean
        enabled_providers:
          type: array
          description: Complete replacement provider set when supplied. An empty array disables every search provider.
          items:
            type: string
    UpdateWebResearchProviderRequest:
      x-go-generate: true
      type: object
      additionalProperties: false
      properties:
        config:
          type: object
          additionalProperties:
            type: string
    WorkerPolicyFeedback:
      x-go-generate: true
      description: Policy feedback frozen when the worker report was evaluated; retained through queue persistence and parent delivery.
      type: object
      additionalProperties: false
      required:
        - code
        - effect
        - copy
      properties:
        code:
          type: string
        effect:
          type: string
        copy:
          type: object
          additionalProperties:
            type: string
        details:
          type: object
          additionalProperties: true
    WorkerResult:
      x-go-generate: true
      type: object
      additionalProperties: false
      properties:
        summary:
          type: string
        completion_report:
          x-go-pointer: true
          $ref: '#/components/schemas/WorkerCompletionReport'
        change_report:
          x-go-pointer: true
          $ref: '#/components/schemas/WorkerChangeReport'
        grounding:
          x-go-pointer: true
          $ref: '#/components/schemas/CitationGrounding'
        response:
          type: string
        status:
          type: string
        hint_code:
          type: string
        policy_feedback:
          x-go-pointer: true
          $ref: '#/components/schemas/WorkerPolicyFeedback'
        host_assembled:
          type: boolean
    ExecutionTarget:
      type: string
      enum:
        - local
        - runner
    SpawnReason:
      type: string
      enum:
        - initial
        - retry
        - human_request
        - closeout
    WorkerStatus:
      type: string
      enum:
        - pending
        - running
        - waiting
        - complete
        - failed
        - canceled
        - held
    WorkerMergeStatus:
      type: string
      description: |
        Lifecycle of a write overlay. Every transition is an explicit event
        the coordinator can observe — there is no silent "skipped" state.
      enum:
        - pending
        - applying
        - merged
        - rebasing
        - orphaned
        - rejected
        - aborted
    WorkerPromotePathOutcome:
      type: string
      description: |
        Per-path merge status within one overlay promote. Every declared path
        ends in one of clean, applied, or conflict — no skipped category.
      enum:
        - clean
        - conflict
        - applied
    WorkerPromoteOrderKind:
      type: string
      description: |
        How a clean overlay path relates to pending sibling overlays on shared
        files. independent — 3-way clean; sibling order does not change outcome.
        sequential — shares paths with siblings; promote one at a time and re-preview.
        clean_if_first — clean now but conflicts if listed siblings land first.
        clean_after — not clean now; becomes clean after listed siblings promote.
      enum:
        - independent
        - sequential
        - clean_if_first
        - clean_after
    WorkerPromoteConflictTier:
      type: string
      description: |
        Language-agnostic conflict classification on preview_overlay /
        promote_overlay path_status and conflict_digest. line_shift — high
        hunk_count from offset drift after siblings landed. overlapping_edit —
        few hunks with distinct primary vs branch bodies. artifact — binary or
        opaque bytes; host proposes drop.
      enum:
        - line_shift
        - overlapping_edit
        - artifact
    WorkerPromoteResolutionAction:
      type: string
      description: |
        Host-resolved merge intent for promote_overlay resolutions and
        ready_resolutions entries.
      enum:
        - drop
        - keep_theirs
        - keep_ours
        - keep_both
    WorkerPromoteHunkResolution:
      type: object
      required:
        - start_line
        - end_line
        - content
      additionalProperties: false
      properties:
        start_line:
          type: integer
          minimum: 1
        end_line:
          type: integer
          minimum: 1
        content:
          type: string
    WorkerReadyResolution:
      type: object
      required:
        - path
        - status
      additionalProperties: false
      properties:
        path:
          type: string
        status:
          $ref: '#/components/schemas/WorkerPromotePathOutcome'
        path_class:
          type: string
        conflict_tier:
          $ref: '#/components/schemas/WorkerPromoteConflictTier'
        action:
          $ref: '#/components/schemas/WorkerPromoteResolutionAction'
        proposed_content:
          type: string
        proposed_reconciled:
          type: boolean
        suggested_hunks:
          type: array
          items:
            $ref: '#/components/schemas/WorkerPromoteHunkResolution'
        needs_review:
          type: boolean
        advisory:
          type: string
    WorkerMergeHunk:
      type: object
      required:
        - start_line
        - end_line
      additionalProperties: false
      properties:
        start_line:
          type: integer
          minimum: 1
        end_line:
          type: integer
          minimum: 1
        base:
          type: string
        primary:
          type: string
        branch:
          type: string
    WorkerMergeConflict:
      type: object
      required:
        - path
        - reason
      additionalProperties: false
      properties:
        path:
          type: string
        reason:
          type: string
        base:
          type: string
        primary:
          type: string
        branch:
          type: string
        branch_delta:
          type: string
        base_delta:
          type: string
        summary:
          type: array
          items:
            type: string
        hunks:
          type: array
          items:
            $ref: '#/components/schemas/WorkerMergeHunk'
        overlap_worker_ids:
          type: array
          items:
            type: string
        conflict_tier:
          $ref: '#/components/schemas/WorkerPromoteConflictTier'
    WorkerPromoteConflictDigest:
      type: object
      required:
        - path
      additionalProperties: false
      properties:
        path:
          type: string
        summary:
          type: array
          items:
            type: string
        branch_delta:
          type: string
        base_delta:
          type: string
        conflict_tier:
          $ref: '#/components/schemas/WorkerPromoteConflictTier'
    WorkerOverlayIntent:
      type: object
      additionalProperties: false
      description: Compact worker goal excerpt returned by preview_overlay.
      properties:
        summary:
          type: string
        scope_paths:
          description: Optional worker focus hints. They do not filter promotion.
          type: array
          items:
            type: string
    OverlayRebaseOutcome:
      type: object
      required:
        - overlay_id
        - status
      additionalProperties: false
      description: Result of rebasing a stacked child overlay after its parent landed on primary.
      properties:
        overlay_id:
          type: string
          pattern: ^[a-zA-Z0-9_-]+$
        status:
          $ref: '#/components/schemas/WorkerMergeStatus'
        conflicts:
          type: array
          items:
            type: string
    WorkerMergeResult:
      x-tool-output:
        - promote_overlay
        - preview_overlay
      description: The promote_overlay and preview_overlay tool result.
      type: object
      required:
        - job_id
        - mode
      additionalProperties: false
      properties:
        job_id:
          type: string
          format: uuid
        mode:
          type: string
        merge_status:
          $ref: '#/components/schemas/WorkerMergeStatus'
        agent_type:
          type: string
        paths:
          type: array
          items:
            type: string
        changed_paths:
          type: array
          items:
            type: string
        clean_paths:
          type: array
          items:
            type: string
        applied:
          type: array
          items:
            type: string
        path_status:
          type: array
          items:
            $ref: '#/components/schemas/WorkerPromotePathStatus'
        overlap_job_ids:
          type: array
          items:
            type: string
        promote_order:
          $ref: '#/components/schemas/WorkerPromoteOrderKind'
        promote_after:
          type: array
          items:
            type: string
        blocked_by:
          type: array
          items:
            type: string
        conflict_digest:
          type: array
          items:
            $ref: '#/components/schemas/WorkerPromoteConflictDigest'
        conflicts:
          type: array
          items:
            $ref: '#/components/schemas/WorkerMergeConflict'
        overlay_intent:
          $ref: '#/components/schemas/WorkerOverlayIntent'
        spill_path:
          type: string
          description: Host-data-relative path to the full promote spill body (promote-spills/<overlay_id>.json). Page with read/grep; never an absolute ~/.config/paintedwolf path.
        ready_resolutions:
          type: array
          items:
            $ref: '#/components/schemas/WorkerReadyResolution'
        error:
          type: string
        rebased:
          type: array
          items:
            $ref: '#/components/schemas/OverlayRebaseOutcome'
        source_evidence:
          $ref: '#/components/schemas/WorkerSourceEvidence'
    WorkerSourceEvidence:
      type: object
      additionalProperties: false
      required:
        - kind
        - status
      description: Advisory source validation compiled from the worker's inspection assessment or current settled check receipts. Inspection does not assert a passing project test or waive a workflow evidence gate. Missing validation does not block completion or promotion.
      properties:
        kind:
          type: string
        status:
          type: string
          enum:
            - satisfied
            - unmet
        source_revision:
          type: string
        source_root_digest:
          type: string
        reason:
          type: string
        evidence_refs:
          type: array
          items:
            type: string
    WorkerPromotePathStatus:
      x-go-generate: true
      type: object
      required:
        - path
        - status
      additionalProperties: false
      properties:
        path:
          type: string
        status:
          $ref: '#/components/schemas/WorkerPromotePathOutcome'
        reason:
          type: string
        hunk_count:
          type: integer
          minimum: 0
        promote_order:
          $ref: '#/components/schemas/WorkerPromoteOrderKind'
        promote_after:
          type: array
          items:
            type: string
        blocked_by:
          type: array
          items:
            type: string
        promote_order_note:
          type: string
        conflict_tier:
          $ref: '#/components/schemas/WorkerPromoteConflictTier'
    WorkerPromoteJobPathStatus:
      x-go-generate: true
      type: object
      required:
        - worker_id
      additionalProperties: false
      properties:
        worker_id:
          type: string
          format: uuid
        paths:
          type: array
          items:
            $ref: '#/components/schemas/WorkerPromotePathStatus'
    WorkerTask:
      type: object
      required:
        - id
        - status
        - agent_type
        - created_at
      additionalProperties: false
      properties:
        id:
          type: string
          format: uuid
        child_session_id:
          type: string
          format: uuid
        delegation_id:
          type: string
          format: uuid
        leg_id:
          type: string
          format: uuid
        parent_session_id:
          type: string
          format: uuid
        workflow_run_id:
          type: string
          format: uuid
        workflow_phase:
          type: string
        workflow_work_id:
          type: string
          pattern: ^[a-z0-9_-]+$
        project_id:
          type: string
          format: uuid
        workspace_root_id:
          type: string
          format: uuid
        workspace_path:
          type: string
        merge_status:
          $ref: '#/components/schemas/WorkerMergeStatus'
          description: Lifecycle of this write overlay; every transition is an explicit event
        overlay_id:
          type: string
          pattern: ^[a-zA-Z0-9_-]+$
          description: |
            Stable identifier for this write overlay; other dispatches may stack
            on it by setting scope.base_overlay_id to this value. Empty for read
            scopes.
        max_tool_loops:
          type: integer
          minimum: 2
          maximum: 120
          description: Worker tool-round ceiling (host default 20, range 2–120); raised only when the coordinator grants a request; final iteration is prose-only
        budget_request:
          $ref: '#/components/schemas/WorkerBudgetRequest'
          description: The worker's unanswered request for more tool rounds; absent once granted or when none was made
        tool_loops_used:
          type: integer
          minimum: 0
          description: Completed tool rounds this worker run; omitted when zero and the job never started
        tool_calls_used:
          type: integer
          minimum: 0
          description: Tool calls this worker has settled over its lifetime; moves forward only
        turn_tool_calls:
          type: integer
          minimum: 0
          description: Size of the tool batch executing right now; zero between turns
        turn_tools_done:
          type: integer
          minimum: 0
          description: Calls already settled in that in-flight batch; never exceeds turn_tool_calls
        context_usage:
          $ref: '#/components/schemas/WorkerContextUsage'
          description: Live per-worker context occupancy for the Den worker-card ring; absent until the first turn completes
        workspace_preparation:
          $ref: '#/components/schemas/WorkspacePreparation'
          description: Ephemeral progress while an isolated write workspace is being prepared
        touched_paths:
          type: array
          items:
            type: string
          description: Live repo-relative paths the worker has mutated (pulse roster)
        execution_target:
          $ref: '#/components/schemas/ExecutionTarget'
        runner_id:
          type: string
          pattern: ^[a-zA-Z0-9_-]+$
        claimed_by:
          type: string
        agent_type:
          type: string
        status:
          $ref: '#/components/schemas/WorkerStatus'
        spawn_reason:
          $ref: '#/components/schemas/SpawnReason'
        brief:
          type: string
          description: Worker assignment goal used for card titles.
        scope:
          $ref: '#/components/schemas/TaskScope'
        files:
          type: array
          items:
            type: string
        result:
          $ref: '#/components/schemas/WorkerResult'
        failure:
          $ref: '#/components/schemas/WorkerFailure'
        error:
          type: string
          description: Operator/debug detail; user-facing copy is in failure when present.
        created_at:
          type: string
          format: date-time
        started_at:
          type: string
          format: date-time
        completed_at:
          type: string
          format: date-time
    ComposeDecisionPhase:
      x-go-generate: true
      type: object
      required:
        - id
        - prompt
        - options
      additionalProperties: false
      properties:
        id:
          type: string
          pattern: ^[a-z0-9_-]+$
        prompt:
          type: string
        options:
          type: array
          items:
            type: string
    ComposeEffectiveSummary:
      x-go-generate: true
      type: object
      required:
        - phases
        - coordinator_brief
      additionalProperties: false
      properties:
        extends:
          type: string
        phases:
          type: array
          items:
            $ref: '#/components/schemas/ComposePhaseSummary'
        phases_removed_from_parent:
          type: array
          items:
            type: string
        posture_transitions:
          type: array
          items:
            $ref: '#/components/schemas/ComposePostureTransition'
        execution_mode_transitions:
          type: array
          items:
            $ref: '#/components/schemas/ComposeExecutionModeTransition'
        default_execution_mode:
          type: string
          enum:
            - investigate
            - orchestrate
            - state_derived
        feedback_phases:
          x-go-name: Feedback
          type: array
          items:
            $ref: '#/components/schemas/ComposeFeedbackPhase'
        decision_phases:
          x-go-name: Decisions
          type: array
          items:
            $ref: '#/components/schemas/ComposeDecisionPhase'
        gates_by_phase:
          type: object
          additionalProperties:
            type: array
            items:
              type: string
        coordinator_brief:
          type: string
        requires_isolation:
          type: boolean
    ComposeFeedbackPhase:
      x-go-generate: true
      type: object
      required:
        - id
        - prompt
      additionalProperties: false
      properties:
        id:
          type: string
          pattern: ^[a-z0-9_-]+$
        prompt:
          type: string
    ComposeFromTemplateRequest:
      x-go-generate: true
      type: object
      required:
        - template_id
        - params
      additionalProperties: false
      properties:
        template_id:
          type: string
          pattern: ^[a-zA-Z0-9_/-]+$
        params:
          type: object
          additionalProperties: {}
    ComposePhaseOnEnter:
      x-go-generate: true
      type: object
      additionalProperties: false
      properties:
        set_posture:
          type: string
        set_execution_mode:
          type: string
          enum:
            - investigate
            - orchestrate
            - state_derived
    ComposePhaseSummary:
      x-go-generate: true
      type: object
      required:
        - id
      additionalProperties: false
      properties:
        id:
          type: string
          pattern: ^[a-z0-9_-]+$
        complete_when:
          type: string
        next:
          type: string
        on_enter:
          x-go-pointer: true
          $ref: '#/components/schemas/ComposePhaseOnEnter'
        gates:
          type: array
          items:
            type: string
    ComposePostureTransition:
      x-go-generate: true
      type: object
      required:
        - phase
        - posture
      additionalProperties: false
      properties:
        phase:
          type: string
        posture:
          type: string
    ComposeValidationError:
      x-go-generate: true
      type: object
      required:
        - field
        - code
        - message
      additionalProperties: false
      properties:
        field:
          type: string
        code:
          type: string
        message:
          type: string
        replacement:
          type: string
    ComposeValidationDetails:
      x-go-generate: true
      description: The `details` of a `workflow_validation_failed` error.
      type: object
      required:
        - errors
      additionalProperties: false
      properties:
        errors:
          type: array
          items:
            $ref: '#/components/schemas/ComposeValidationError'
    ComposeWorkflowResponse:
      x-go-generate: true
      type: object
      required:
        - summary
        - effective_yaml
        - effective_summary
      additionalProperties: false
      properties:
        summary:
          $ref: '#/components/schemas/WorkflowSummary'
        effective_yaml:
          type: string
        effective_summary:
          $ref: '#/components/schemas/ComposeEffectiveSummary'
    CoordinatorRunContext:
      x-go-generate: true
      type: object
      additionalProperties: false
      properties:
        workflow_id:
          type: string
          pattern: ^[a-z0-9_-]+$
        workflow_version:
          type: string
        run_id:
          type: string
          format: uuid
        run_status:
          type: string
        current_phase:
          type: string
        coordinator_brief:
          type: string
        failed_leaves:
          type: array
          items:
            type: string
        allowed_agents:
          type: array
          items:
            type: string
        requires_isolation:
          type: boolean
        has_compose_draft:
          type: boolean
        phase_execution_mode:
          type: string
          enum:
            - investigate
            - orchestrate
        workflow_default_execution_mode:
          type: string
          enum:
            - investigate
            - orchestrate
        pending_feedback:
          x-go-pointer: true
          $ref: '#/components/schemas/PendingFeedback'
        request:
          x-go-pointer: true
          $ref: '#/components/schemas/WorkflowRequestState'
        feedback_phases:
          type: array
          items:
            $ref: '#/components/schemas/ComposeFeedbackPhase'
        decision_phases:
          type: array
          items:
            $ref: '#/components/schemas/ComposeDecisionPhase'
        batch_phase:
          x-go-type: string
          $ref: '#/components/schemas/CoordinatorBatchPhase'
        batch_seq:
          type: integer
          minimum: 0
        phase_coordinator_surface:
          type: string
        phase_surface_template:
          type: string
        phase_mode_refs:
          type: array
          items:
            type: string
        workflow_investigate_eligible:
          x-go-pointer: true
          type: boolean
        advance_when_gate_met:
          type: string
          enum:
            - auto
            - coordinator
        surface_profile:
          type: string
        phase_host_held:
          type: boolean
          description: The host holds the current phase until its obligations or topology legs settle; the coordinator only answers the person meanwhile.
    ActiveWorkflowRunResponse:
      x-go-generate: true
      type: object
      required:
        - run
      additionalProperties: false
      properties:
        run:
          x-go-pointer: true
          nullable: true
          $ref: '#/components/schemas/WorkflowRun'
    AdvanceWorkflowRunRequest:
      x-go-generate: true
      type: object
      required:
        - expected_revision
      additionalProperties: false
      properties:
        expected_revision:
          type: integer
          format: int64
          minimum: 1
    FireWorkflowTransitionRequest:
      x-go-generate: true
      type: object
      required:
        - expected_revision
      additionalProperties: false
      properties:
        expected_revision:
          type: integer
          format: int64
          minimum: 1
    PendingFeedback:
      x-go-generate: true
      x-go-field-order:
        - phase_id
        - prompt
        - response_type
        - options
        - allow_other
        - artifact_id
        - artifact_ids
        - purpose
        - issued_revision
        - secret
      type: object
      description: Live coordinator→human ask awaiting a response.
      required:
        - phase_id
        - prompt
      additionalProperties: false
      properties:
        phase_id:
          type: string
          pattern: ^[a-z0-9_-]+$
          description: Route key and stable identity for the pending input.
        prompt:
          type: string
          description: Card question (ask_user one decision; markdown OK; host-capped at 800 code points so the prompt fits the 12rem composer-dock prompt area).
        response_type:
          type: string
          enum:
            - text
            - single_choice
            - multi_choice
            - secret
          description: Discriminates the resolve endpoint — text answers go to feedback, choices to decisions, and secret values to the protected secret endpoint.
        options:
          type: array
          items:
            type: string
          description: Choice options for single_choice/multi_choice asks.
        allow_other:
          type: boolean
          description: Whether a free-text answer outside options is accepted (choice asks).
        artifact_id:
          type: string
          format: uuid
          description: Exact visual artifact under review.
        artifact_ids:
          x-go-name: ArtifactIDs
          type: array
          maxItems: 4
          items:
            type: string
            format: uuid
          description: Exact visual artifacts under comparison.
        purpose:
          type: string
          enum:
            - clarify
            - review
            - compare
            - secret
        secret:
          x-go-pointer: true
          $ref: '#/components/schemas/SecretInputMeta'
        issued_revision:
          type: integer
          format: int64
          minimum: 1
          description: Run revision that issued this request; retained with the answer for audit.
    PersistWorkflowRequest:
      x-go-generate: true
      type: object
      required:
        - version
        - confirm
      additionalProperties: false
      properties:
        version:
          type: string
        confirm:
          type: boolean
        trigger:
          type: string
    PersistWorkflowResponse:
      x-go-generate: true
      type: object
      required:
        - path
        - summary
      additionalProperties: false
      properties:
        path:
          type: string
        summary:
          $ref: '#/components/schemas/WorkflowSummary'
    ResolveUserFeedbackRequest:
      x-go-generate: true
      type: object
      required:
        - expected_revision
        - response
      additionalProperties: false
      properties:
        expected_revision:
          type: integer
          format: int64
          minimum: 1
        response:
          type: string
        secrets:
          type: array
          maxItems: 32
          items:
            $ref: '#/components/schemas/PromptSecretReferencePart'
    ResolveUserSecretRequest:
      x-go-generate: true
      type: object
      required:
        - expected_revision
        - secret_value
      additionalProperties: false
      properties:
        expected_revision:
          type: integer
          format: int64
          minimum: 1
        secret_value:
          type: string
          minLength: 4
          maxLength: 65536
          writeOnly: true
          description: Raw value delivered only to protected storage; never returned. The floor is the length the outbound screen can recognize; a shorter answer is refused rather than protected in name only.
    SecretInputMeta:
      x-go-generate: true
      type: object
      required:
        - name
        - scope
      additionalProperties: false
      properties:
        name:
          type: string
        purpose:
          type: string
        scope:
          type: string
          enum:
            - chat
            - project
          description: Chat scope lets only this chat and its workers use the reference; project scope lets later chats use it too.
        agent_use_ttl_ms:
          x-go-name: AgentUseTTLMs
          type: integer
          format: int64
          minimum: 0
          description: Optional lifetime before the agent can no longer substitute the capability. Authenticated human reveal remains available for recovery, and this does not alter an external credential.
    ResolveWorkflowDecisionRequest:
      x-go-generate: true
      type: object
      required:
        - expected_revision
      additionalProperties: false
      properties:
        expected_revision:
          type: integer
          format: int64
          minimum: 1
        choice:
          type: string
        choices:
          type: array
          items:
            type: string
        comment:
          type: string
        secrets:
          type: array
          maxItems: 32
          items:
            $ref: '#/components/schemas/PromptSecretReferencePart'
    StartWorkflowRunRequest:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - operation_id
        - workflow_id
        - workflow_version
      properties:
        operation_id:
          type: string
          format: uuid
          description: Stable client mutation identity. An exact retry returns the original run; reusing this id with different input is rejected with 409 idempotency_conflict.
        workflow_id:
          type: string
          minLength: 1
          pattern: ^[a-z0-9_-]+$
        workflow_version:
          type: string
          minLength: 1
        preset_id:
          type: string
          pattern: ^[a-z0-9_-]+$
          description: Preset declared by the selected workflow version; its parameters apply before explicit parameters. An unknown preset is rejected.
        request:
          type: string
          description: Explicit request attached to this workflow start. When omitted or blank, the manifest default is used or its open question is presented.
        parameters:
          type: object
          additionalProperties:
            type: string
          description: Values for parameters declared by the selected workflow. Unknown names and invalid values are rejected.
        blueprint_path:
          type: string
          description: Project-relative path under the overlay blueprints/ directory
        blueprint_title:
          type: string
        replace_run_id:
          type: string
          format: uuid
          description: Active run the human reviewed when replacing a non-ambient lineage.
        expected_revision:
          type: integer
          format: int64
          minimum: 1
          description: Revision displayed with replace_run_id.
    WorkflowControlRequest:
      x-go-generate: true
      type: object
      required:
        - expected_revision
      additionalProperties: false
      properties:
        expected_revision:
          type: integer
          format: int64
          minimum: 1
        reason:
          type: string
    WorkflowEvent:
      x-go-generate: true
      type: object
      additionalProperties: false
      properties:
        event:
          $ref: '#/components/schemas/WorkflowEventKind'
        workflow_id:
          type: string
          pattern: ^[a-z0-9_-]+$
        workflow_run_id:
          type: string
          format: uuid
        run:
          x-go-pointer: true
          $ref: '#/components/schemas/WorkflowRun'
          description: |
            Authoritative run state at publication, in the same enriched shape the
            run endpoints return. Present on every run transition, so a client
            applies the run from the stream rather than a refetch that races the
            `message` rows naming it. Absent on events that are not a run
            transition (persisted manifest, gate pending).
        previous_phase:
          type: string
        phase:
          type: string
        status:
          type: string
        branch_instruction:
          type: string
        guidance:
          type: array
          items:
            $ref: '#/components/schemas/ScanGuidanceSummary'
        path:
          type: string
        version:
          type: string
        created_by:
          type: string
    WorkflowRun:
      x-go-generate: true
      x-go-field-order:
        - id
        - session_id
        - project_id
        - parent_run_id
        - workflow_id
        - workflow_version
        - attach_policy
        - status
        - revision
        - current_phase
        - blueprint_path
        - pause_reason
        - failure
        - start_message_id
        - end_message_id
        - created_at
        - updated_at
        - paused_at
        - completed_at
        - ui
      type: object
      required:
        - id
        - session_id
        - workflow_id
        - workflow_version
        - status
        - revision
        - current_phase
        - created_at
        - updated_at
      additionalProperties: false
      properties:
        id:
          type: string
          format: uuid
        session_id:
          type: string
          format: uuid
        project_id:
          type: string
          format: uuid
        workflow_id:
          type: string
          pattern: ^[a-z0-9_-]+$
        workflow_version:
          type: string
        attach_policy:
          type: string
          description: |
            Catalog attach policy for this run's manifest, persisted with the run.
            `session_create` marks the ambient-capable recipe; ambient root also requires no parent_run_id.
          enum:
            - session_create
        status:
          $ref: '#/components/schemas/WorkflowRunStatus'
        revision:
          type: integer
          format: int64
          minimum: 1
          description: Monotonic mutable-state revision for stale-command rejection.
        current_phase:
          type: string
        blueprint_path:
          type: string
          description: Project-relative path under the overlay blueprints/ directory
        pause_reason:
          type: string
        failure:
          x-go-pointer: true
          $ref: '#/components/schemas/WorkflowFailure'
        start_message_id:
          type: string
          format: uuid
        end_message_id:
          type: string
          format: uuid
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
        paused_at:
          x-go-pointer: true
          type: string
          format: date-time
        completed_at:
          x-go-pointer: true
          type: string
          format: date-time
        parent_run_id:
          x-go-pointer: true
          type: string
          format: uuid
        ui:
          x-go-pointer: true
          $ref: '#/components/schemas/WorkflowRunUi'
    WorkflowRunStatus:
      type: string
      enum:
        - running
        - paused
        - paused_on_child
        - complete
        - failed
        - canceled
        - interrupted
    WorkflowScope:
      type: string
      enum:
        - bundled
        - project
        - session
    WorkflowSummary:
      x-go-generate: true
      type: object
      required:
        - id
        - version
        - name
      additionalProperties: false
      properties:
        id:
          type: string
          pattern: ^[a-z0-9_-]+$
        version:
          type: string
        name:
          type: string
        description:
          type: string
        trigger:
          type: string
        request:
          x-go-pointer: true
          $ref: '#/components/schemas/WorkflowRequestSpec'
        initial_posture:
          type: string
        topology:
          type: string
          description: Optional topology id under config/topologies/ for orchestrated pipeline runs
        icon:
          type: string
          description: Semantic glyph name for launcher tiles (e.g. route, radar, shield)
        featured:
          type: boolean
          description: Surfaces this workflow as a quick-start tile in the new-session launcher
        requires_repo:
          type: boolean
          description: Workflow operates on an attached repo; launcher disables it for rootless draft sessions
        supports_blueprints:
          type: boolean
          description: Manifest declares a blueprint block; surfaces in the Blueprints zone workflow launcher
        report_enabled:
          type: boolean
          description: Opted-in workflow yields a downloadable grounded PDF report for completed runs
        phases:
          type: array
          items:
            type: string
        presets:
          type: array
          items:
            $ref: '#/components/schemas/WorkflowPresetSummary'
        scope:
          $ref: '#/components/schemas/WorkflowScope'
    WorkflowTemplateParameter:
      x-go-generate: true
      type: object
      required:
        - type
      additionalProperties: false
      properties:
        type:
          type: string
        required:
          type: boolean
        default: {}
    WorkflowTemplateSummary:
      x-go-generate: true
      type: object
      required:
        - id
        - description
        - parameters
      additionalProperties: false
      properties:
        id:
          type: string
          pattern: ^[a-zA-Z0-9_/-]+$
        description:
          type: string
        parameters:
          type: object
          additionalProperties:
            $ref: '#/components/schemas/WorkflowTemplateParameter'
    ApiErrorCode:
      type: string
      description: Closed machine-readable error identity emitted by the private Den-sidecar API.
      enum:
        - source_view_not_found
        - source_view_revision_changed
        - source_view_capacity
        - source_view_frame_too_large
        - source_view_preparing
        - trust_review_changed
        - project_inventory_unavailable
        - source_analysis_incomplete
        - source_analysis_unavailable
        - invalid_page_cursor
        - unsupported_media_type
        - method_not_allowed
        - background_process_not_found
        - body_too_large
        - prompt_text_too_large
        - diagnostics_export_failed
        - pricing_no_source
        - backup_invalid
        - backup_incomplete
        - backup_incompatible
        - backup_unavailable
        - store_incompatible
        - export_format_invalid
        - checkpoint_not_found
        - approval_grant_not_found
        - session_not_idle
        - rewind_anchor_not_found
        - rewind_anchor_ineligible
        - rewind_blocked
        - rewind_plan_changed
        - rewind_mode_unsupported
        - clone_failed
        - folder_exists
        - folder_not_empty
        - project_mutation_in_progress
        - promotion_conflict
        - promotion_not_found
        - draft_root_immutable
        - project_busy
        - checkpoint_not_pending
        - delegation_not_found
        - delegation_leg_not_found
        - feedback_not_pending
        - feedback_response_required
        - file_briefing_disabled
        - file_briefing_not_found
        - file_briefing_unavailable
        - source_version_unavailable
        - choice_transition_not_found
        - choice_transition_actor_denied
        - choice_transition_not_armed
        - choice_transition_pending_input
        - decision_choice_invalid
        - not_choice_phase
        - git_checkout_failed
        - git_commit_failed
        - git_discard_failed
        - git_init_failed
        - git_pull_failed
        - git_push_failed
        - git_no_changes
        - git_draft_failed
        - git_repo_not_found
        - git_stash_failed
        - worktree_not_bound
        - worktree_already_bound
        - worktree_branch_exists
        - worktree_dirty
        - worktree_stale
        - worktree_land_blocked
        - internal_error
        - invalid_json
        - invalid_path
        - write_root_is_filesystem_root
        - write_root_is_home
        - write_root_not_absolute
        - write_root_under_secret_store
        - blueprint_content_conflict
        - idempotency_conflict
        - invalid_arguments
        - invalid_query
        - command_disabled
        - choice_source_not_found
        - choice_source_failed
        - choice_source_invalid
        - contribution_source_unavailable
        - search_source_not_found
        - contribution_search_failed
        - contribution_search_invalid
        - operation_output_invalid
        - source_mutation_diverged
        - source_history_changed
        - workflow_run_not_active
        - cursor_generation_expired
        - event_replay_unavailable
        - invalid_request
        - provider_catalog_unavailable
        - invalid_workflow_transition
        - invalid_workflow_target
        - invalid_workflow_revision
        - invalid_workflow_replacement_target
        - message_not_found
        - not_found
        - path_not_found
        - persist_not_confirmed
        - phase_gate_unmet
        - blueprint_not_draft
        - blueprint_not_editable
        - blueprint_not_found
        - blueprint_path_taken
        - blueprint_run_active
        - detection_pack_not_removable
        - detection_pack_id_collision
        - blueprint_launch_incompatible
        - blueprint_launch_unsupported
        - human_approval_not_ready
        - project_not_found
        - extension_suggestion_changed
        - extension_suggestion_invalid
        - overlay_format_unsupported
        - overlay_format_invalid
        - root_not_found
        - root_busy
        - duplicate_root
        - duplicate_root_label
        - project_removal_assessment_changed
        - project_removal_operation_conflict
        - extension_state_changed
        - extension_candidate_rejected
        - contributions_unavailable
        - command_not_found
        - invocation_scope_mismatch
        - contribution_frame_changed
        - command_unavailable
        - contribution_not_authorized
        - mcp_call_failed
        - path_missing
        - trust_surface_off
        - artifact_empty
        - artifact_too_large
        - artifact_foreign
        - artifact_unavailable
        - artifact_deleted
        - artifact_not_found
        - backup_restore_pending
        - recording_duration_invalid
        - recording_start_invalid
        - user_image_not_visible
        - unsupported_attachment
        - attachment_too_large
        - attachment_undecodable
        - attachment_not_found
        - attachment_unavailable
        - reference_out_of_jail
        - attachment_scanned_no_text
        - no_project_root
        - source_path_denied
        - source_path_protected
        - source_move_incomplete
        - source_path_busy
        - source_recovery_failed
        - source_not_found
        - source_binary_denied
        - source_read_only
        - source_binary
        - unsupported_encoding
        - source_raw_not_image
        - source_raw_too_large
        - source_workspace_mismatch
        - source_write_conflict
        - source_permission_change_failed
        - editor_revision_conflict
        - editor_replica_epoch
        - editor_replica_identity
        - editor_root_detached
        - source_presentation_effect_changed
        - source_already_exists
        - source_cross_root
        - source_not_empty
        - source_inventory_pending
        - source_trash_failed
        - source_unavailable
        - project_policy_denied
        - provider_not_found
        - provider_in_use
        - provider_not_configured
        - provider_empty_completion
        - provider_response_interrupted
        - provider_context_too_small
        - provider_tool_calls_unsupported
        - provider_tool_calls_in_prose
        - provider_rate_limited
        - provider_overloaded
        - provider_server_error
        - thinking_override_unavailable
        - provider_request_rejected
        - provider_unreachable
        - provider_silent
        - model_refused
        - model_catalog_unreachable
        - model_catalog_refresh_failed
        - mcp_provider_unreachable
        - mcp_sync_failed
        - mcp_tool_pin_unreadable
        - mcp_id_required
        - mcp_transport_required
        - mcp_transport_conflict
        - mcp_provider_not_found
        - mcp_update_empty
        - mcp_oauth_unavailable
        - mcp_oauth_failed
        - mcp_oauth_not_supported
        - mcp_oauth_registration_required
        - mcp_recipe_not_found
        - mcp_persist_failed
        - remote_requires_https
        - invalid_url
        - project_stdio_forbidden
        - project_headers_forbidden
        - project_remote_forbidden
        - project_enable_forbidden
        - overlay_unknown_field
        - unreadable_layer
        - rate_limited
        - queue_revision_conflict
        - queue_empty
        - queue_send_pending
        - queue_send_reserved
        - queue_no_send_to_cancel
        - scan_not_complete
        - scan_not_found
        - no_scanner_available
        - invalid_entry
        - community_driver_forbidden
        - community_parser_forbidden
        - mapper_missing
        - project_fields_forbidden
        - argv_project_path_forbidden
        - argv_shell_forbidden
        - duplicate_id
        - search_query_invalid
        - search_pattern_invalid
        - search_export_invalid_format
        - search_export_sarif_scope
        - security_disabled
        - grounding_escalated
        - session_spend_ceiling_reached
        - prompt_failed
        - host_fault
        - worker_closeout_exhausted
        - worker_execute_failed
        - worker_workspace_disk_full
        - worker_owner_unsettled
        - worker_workspace_unavailable
        - session_archived
        - session_not_found
        - session_not_pinned
        - session_preparation_failed
        - session_preparing
        - session_stopping
        - session_workflow_not_found
        - unauthorized
        - forbidden
        - worker_not_found
        - worker_not_cancelable
        - leg_not_pending
        - decision_not_pending
        - workflow_active
        - workflow_revision_conflict
        - workflow_replacement_target_required
        - workflow_start_requires_human_approval
        - blueprint_draft_required
        - workflow_not_found
        - workflow_not_runnable
        - workflow_run_not_found
        - report_not_found
        - managed_secret_revoked
        - managed_secret_agent_use_expired
        - managed_secret_reveal_unavailable
        - managed_secret_reveal_denied
        - managed_secret_reveal_changed
        - managed_secret_reveal_challenge_not_found
        - managed_secret_value_unavailable
        - auth_unavailable
        - recovery_snapshot_invalid
        - source_text_too_large
        - source_content_too_large
        - source_version_changed
        - source_path_ambiguous
        - file_operation_unavailable
        - file_operation_capacity
        - file_operation_not_cancelable
        - ignore_file_changed
        - stock_pack_immutable
        - message_references_changed
        - chat_content_changed
        - history_preview_changed
        - pricing_source_invalid
        - pricing_source_unreachable
        - pricing_source_not_found
        - pricing_source_disabled
        - sandbox_socket_path_invalid
        - sandbox_socket_path_not_found
        - sandbox_socket_path_not_socket
        - sandbox_socket_path_refused
        - sandbox_socket_path_limit
        - sandbox_socket_path_changed
        - ask_user_rejected
        - workflow_validation_failed
        - managed_secret_not_found
        - source_history_not_found
        - source_presentation_not_found
        - source_pin_not_found
        - source_effect_not_found
        - source_version_not_found
        - source_commit_not_found
        - source_file_not_found
        - git_movement_not_found
        - editor_document_not_found
        - file_operation_not_found
        - research_provider_not_found
        - research_credential_not_found
        - scanner_not_found
        - scanner_catalog_entry_not_found
        - detection_pack_not_found
        - host_resource_not_found
        - ignore_entry_not_found
        - secret_review_not_found
        - extension_pack_not_found
        - extension_unit_not_found
        - extension_meta_pack_not_found
        - extension_profile_not_found
        - extension_suggestion_not_found
        - project_removal_not_found
        - chat_content_not_found
        - manual_llm_request_not_found
        - preparation_receipt_not_found
        - history_protection_not_found
    NoticeAction:
      type: string
      enum:
        - open_ai_providers
        - retry_contribution_frame
        - prompt_retry
        - prompt_keep_going
        - prompt_rewind_and_retry
    NoticeTier:
      type: string
      enum:
        - catastrophic
        - non_catastrophic
    NoticeScope:
      type: string
      enum:
        - app
        - project
        - session
    CatastrophicDetail:
      x-go-generate: true
      type: object
      description: |-
        Support-report facts for a catastrophic result, shown on the stop screen and copyable by the user.
        This is the one payload a user can read before the app is usable, so it is what gets pasted into a public issue. Every field is an allowlisted, machine-anonymous fact. It excludes: absolute paths, anything under the user's home directory, hostname, user name, project or session ids, project names, provider ids, environment dumps, and log tails. The host builds it at one chokepoint so redaction cannot be bypassed per probe.
      additionalProperties: false
      properties:
        code:
          type: string
          description: The user-notice code, e.g. `OS_BELOW_FLOOR`.
        resolution:
          type: string
          description: Which declared resolution applied.
        probe_id:
          type: string
          pattern: ^[a-z0-9_-]+$
          description: Stable probe id that produced the result.
        app_version:
          type: string
          description: Host version string.
        build:
          type: string
          description: VCS revision when the binary was stamped with one. Absent for unstamped builds rather than reported as empty.
        schema_version:
          type: integer
          description: Durable store schema version the host expects.
        os_name:
          x-go-name: OSName
          type: string
          description: Operating system name, e.g. `macOS`.
        os_version:
          x-go-name: OSVersion
          type: string
          description: Operating system version, when it could be read.
        os_floor:
          x-go-name: OSFloor
          type: string
          description: Minimum operating system version this build supports.
        arch:
          type: string
          description: CPU architecture, e.g. `arm64`.
        config_dir_label:
          type: string
          description: Human label for the configuration directory (e.g. `~/.config/paintedwolf`), never an absolute path.
        facts:
          type: object
          additionalProperties:
            type: string
          description: The probe's redacted detail map, passed through unchanged.
        observed_at:
          x-go-type: string
          type: string
          format: date-time
          description: When the host observed the condition.
    PreflightProbe:
      x-go-generate: true
      x-go-field-order:
        - id
        - status
        - code
        - detail
        - tier
        - scope
        - resolution
        - catastrophic_detail
        - title
        - message
        - suggested_action
        - actions
      type: object
      required:
        - id
        - status
      additionalProperties: false
      properties:
        id:
          type: string
          pattern: ^[a-z0-9_-]+$
          description: Stable probe id, e.g. `git_engine`.
        status:
          type: string
          enum:
            - ok
            - degraded
            - blocked
        code:
          type: string
          description: User-notice code for a non-ok result, e.g. `GIT_ENGINE_UNAVAILABLE`. Absent when status is `ok`.
        tier:
          $ref: '#/components/schemas/NoticeTier'
          description: Whether this result leaves the app usable. Host-declared in the user-notice catalog; clients must not derive it from `status`.
        scope:
          $ref: '#/components/schemas/NoticeScope'
          description: Where this result renders. Host-declared; clients must not infer placement from the surface the notice arrived on.
        resolution:
          type: string
          description: Which declared resolution produced `tier`/`scope`. One code can mean two different conditions — `OS_BELOW_FLOOR` blocks the app when the OS is genuinely too old but is only a missing capability when the version could not be read — so the resolution, not the code, is the unit of partitioning. `default` for unconditional notices.
        catastrophic_detail:
          x-go-pointer: true
          $ref: '#/components/schemas/CatastrophicDetail'
        detail:
          type: object
          additionalProperties:
            type: string
          description: Structured facts for the copy template (e.g. `want`/`got`). Never a path under the user's home directory and never a secret.
        title:
          type: string
          description: Rendered catalog title. Den displays wire copy only.
        message:
          type: string
          description: Rendered catalog message.
        suggested_action:
          type: string
          description: Rendered catalog remedy. Preflight reports and never remediates — the app does not run this on the user's behalf.
        actions:
          type: array
          items:
            $ref: '#/components/schemas/NoticeAction'
          description: Structured in-app destinations for the remedy; clients never infer them from copy.
    AttachmentCapabilities:
      x-go-generate: true
      type: object
      description: |
        Composer affordances, grouped by the plane each byte bound protects. These let the client show limits before an upload; admission is decided by the host on the upload route, never here.
      required:
        - auto_attach_paste_bytes
        - max_inline_text_bytes
        - max_attachments
        - max_references
        - max_images
        - max_upload_bytes
        - max_image_bytes
        - max_body_bytes
        - max_turn_bytes
        - max_body_preview_bytes
        - max_large_text_preview_bytes
        - max_turn_preview_bytes
        - max_document_bytes
        - max_video_bytes
        - image_mime_types
        - video_mime_types
        - text_mime_types
        - text_extensions
        - text_basenames
      additionalProperties: false
      properties:
        auto_attach_paste_bytes:
          type: integer
          minimum: 1
          description: Composer plane — a UTF-8 plain-text paste at or above this size becomes a text attachment
        max_inline_text_bytes:
          type: integer
          minimum: 1
          description: Prompt plane — largest UTF-8 user-prose body accepted inline
        max_attachments:
          type: integer
          minimum: 1
          description: Byte-carrying attachments permitted on one turn
        max_references:
          type: integer
          minimum: 1
          description: Jailed coordinate references permitted on one turn
        max_images:
          type: integer
          minimum: 1
        max_upload_bytes:
          x-go-type: int64
          type: integer
          minimum: 1
          description: Transport plane — largest single upload body accepted off the wire
        max_image_bytes:
          x-go-type: int64
          type: integer
          minimum: 1
          description: Transport plane — largest raster accepted
        max_body_bytes:
          x-go-type: int64
          type: integer
          minimum: 1
          description: Materialization plane — largest body permitted to land on disk
        max_turn_bytes:
          x-go-type: int64
          type: integer
          minimum: 1
          description: Materialization plane — total attachment bytes one turn may reference
        max_body_preview_bytes:
          type: integer
          minimum: 1
          description: Prompt plane — ordinary per-body preview ceiling
        max_large_text_preview_bytes:
          type: integer
          minimum: 1
          description: Prompt plane — per-body preview ceiling for text at or above auto_attach_paste_bytes
        max_turn_preview_bytes:
          type: integer
          minimum: 1
          description: Prompt plane — aggregate body-preview bytes admitted across all attachments and references on one turn
        max_document_bytes:
          x-go-type: int64
          type: integer
          minimum: 1
          description: Largest body the host will parse as a document (extraction is in-memory)
        max_video_bytes:
          x-go-type: int64
          type: integer
          minimum: 1
          description: Largest video the host decodes for its frames (the decoder holds it in memory)
        image_mime_types:
          x-go-name: ImageMIMETypes
          type: array
          items:
            type: string
        video_mime_types:
          x-go-name: VideoMIMETypes
          type: array
          description: Video containers the host accepts; whether a file's codec plays is decided at upload
          items:
            type: string
        text_mime_types:
          x-go-name: TextMIMETypes
          type: array
          items:
            type: string
        text_extensions:
          type: array
          items:
            type: string
        text_basenames:
          type: array
          items:
            type: string
    PreflightReport:
      x-go-generate: true
      type: object
      required:
        - overall
        - probes
        - attachment_capabilities
      additionalProperties: false
      properties:
        overall:
          type: string
          enum:
            - ok
            - degraded
            - blocked
          description: Worst status across all probes. `degraded` means a capability is unavailable while the app still works; `blocked` means it cannot function.
        probes:
          type: array
          description: Probe results in registration order.
          items:
            $ref: '#/components/schemas/PreflightProbe'
        attachment_capabilities:
          $ref: '#/components/schemas/AttachmentCapabilities'
    PendingWorkflowStart:
      x-go-generate: true
      x-go-field-order:
        - workflow_id
        - workflow_version
        - preset_id
        - label
      type: object
      required:
        - workflow_id
        - workflow_version
      additionalProperties: false
      properties:
        workflow_id:
          type: string
          pattern: ^[a-z0-9_-]+$
        workflow_version:
          type: string
        label:
          type: string
        preset_id:
          type: string
          pattern: ^[a-z0-9_-]+$
    SessionBackgroundDirectStatus:
      type: string
      enum:
        - active
        - unknown
    SessionBackgroundDirectJob:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - process_id
        - status
      properties:
        process_id:
          type: string
          pattern: ^[A-Za-z0-9_-]+$
        tool_call_id:
          type: string
          pattern: ^[A-Za-z0-9_.:-]+$
        status:
          $ref: '#/components/schemas/SessionBackgroundDirectStatus'
    SessionBackgroundExecutionJob:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - process_id
        - capability
        - status
      properties:
        process_id:
          type: string
          pattern: ^[A-Za-z0-9_-]+$
        tool_call_id:
          type: string
          pattern: ^[A-Za-z0-9_.:-]+$
        capability:
          type: string
          enum:
            - process_control
            - host_execution
        status:
          $ref: '#/components/schemas/SessionBackgroundDirectStatus'
    SessionProtectionState:
      x-go-generate: true
      x-go-field-order:
        - control_plane_reads_allowed
        - additional_write_roots
        - mediation_unavailable
        - sandbox_bypass
        - background_direct
        - background_execution
      type: object
      additionalProperties: false
      description: |
        Persistent app/session protection chrome. Mediation unavailable and full sandbox bypass are distinct — never one ambiguous uncontained banner.
      properties:
        mediation_unavailable:
          type: boolean
          description: |
            Network mediation cannot start. Outbound connections are blocked; commands that do not use the network are unaffected.
        sandbox_bypass:
          type: boolean
          description: |
            Sandbox protections are turned off. Commands may access the machine and network without mediation.
        control_plane_reads_allowed:
          type: boolean
          description: |
            A startup override removes the app configuration read floor. Protected key material remains subject to its independent rules.
        additional_write_roots:
          type: array
          description: |
            Resolved folders added to default write access by a startup override. Protected paths within these folders remain restricted.
          items:
            type: string
        background_direct:
          type: array
          description: Active or reconstructed background direct-IP processes.
          items:
            $ref: '#/components/schemas/SessionBackgroundDirectJob'
        background_execution:
          type: array
          description: Processes with approved process control or host execution that have not been observed exiting.
          items:
            $ref: '#/components/schemas/SessionBackgroundExecutionJob'
    SessionUiState:
      x-go-generate: true
      type: object
      additionalProperties: false
      properties:
        pending_workflow_start:
          x-go-type: '*PendingWorkflowStart'
          oneOf:
            - $ref: '#/components/schemas/PendingWorkflowStart'
            - type: 'null'
        protection:
          x-go-pointer: true
          $ref: '#/components/schemas/SessionProtectionState'
    UpdateSessionRequest:
      x-go-generate: true
      type: object
      minProperties: 1
      maxProperties: 1
      additionalProperties: false
      description: |
        One session lifecycle command. Exactly one of title, archived, pinned, or pin_position is required; compound commands are rejected without changing the session.
      properties:
        title:
          x-go-pointer: true
          type: string
          minLength: 1
          maxLength: 80
          description: User-set display title; empty after trim is rejected
        archived:
          x-go-pointer: true
          type: boolean
          description: |
            true moves the session out of the working set (sidebar, attention, default list); false restores it. History and search are untouched.
        pinned:
          x-go-pointer: true
          type: boolean
          description: |
            true adds the session to the end of its project's pinned chats; false unpins it. Archived and worker child sessions cannot be pinned.
        pin_position:
          x-go-pointer: true
          type: integer
          minimum: 1
          description: |
            Moves a pinned session to this 1-based position in its project's pinned order; a position past the end moves it last. The project's pinned chats are renumbered 1…n.
    ActivityKind:
      type: string
      description: Closed host work phase; presentation copy is client-supplied.
      enum:
        - preparing_context
        - running_tool
        - starting_workflow
        - awaiting_wake
        - deciding
    ActivityStatus:
      type: string
      description: Lifecycle edge for one host-observed activity lease.
      enum:
        - active
        - done
    ToolProgress:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - phase
        - files_searched
        - files_skipped
        - matches
      description: Measured discovery work. Counts are observations, not a completion estimate.
      properties:
        phase:
          type: string
          enum:
            - selecting
            - searching
        files_searched:
          type: integer
          minimum: 0
        files_skipped:
          type: integer
          minimum: 0
        matches:
          type: integer
          minimum: 0
    MessageOrigin:
      type: string
      description: Structured source of model-visible content; independent of provider role.
      enum:
        - host
        - user
        - model
        - tool
        - peer_agent
        - project
        - attachment
        - retrieval
        - unknown
    ContentAuthority:
      type: string
      description: |
        Instruction authority assigned by the host. Trust never grants authority.
      enum:
        - system
        - developer
        - user
        - none
        - unknown
    ContentTrustTier:
      type: string
      description: Host trust classification for the content channel; independent of authority.
      enum:
        - trusted
        - untrusted
        - unknown
    MessageReferenceKind:
      type: string
      description: |
        What a retrieval part points at, stamped from the reference variant the host ingested. Clients branch on this rather than on the hint prose the fence carries for the model.
      enum:
        - path_file
        - path_folder
        - search_hit
    MessageContentPart:
      x-go-generate: true
      type: object
      required:
        - content
        - origin
        - authority
        - trust_tier
      additionalProperties: false
      properties:
        content:
          type: string
        origin:
          $ref: '#/components/schemas/MessageOrigin'
        authority:
          $ref: '#/components/schemas/ContentAuthority'
        trust_tier:
          $ref: '#/components/schemas/ContentTrustTier'
        source:
          type: string
          description: Optional host-stamped source identity such as a tool or attachment name.
        media_type:
          type: string
          description: Optional MIME type for this text projection.
        blob_id:
          type: string
          pattern: ^[0-9a-f]{64}$
          description: Stable blob ID for a payload attachment, used to restage Edit/rewind.
        path:
          type: string
          description: |
            Host-stamped locator the agent can open. Payload attachments use a host-data-relative path (`prompt-attachments/{blob_id}/{name}`). path_file / path_folder retrieval parts use the path relative to `root_id`, qualified with `@label/` when that root is not primary. Absent when the part has no openable body (search_hit, user prose, host notices).
        root_id:
          type: string
          format: uuid
          description: |
            Attached root a path_file / path_folder reference resolves under. Absent on payload attachments, search hits, prose, and host notices.
        size_bytes:
          x-go-type: int64
          type: integer
          minimum: 0
          description: |
            Full decoded body size for a payload attachment, before preview truncation. Absent for coordinate-only references.
        reference_kind:
          $ref: '#/components/schemas/MessageReferenceKind'
          description: |
            Set on retrieval parts only. Absent for user prose, host notices, and attachment payloads, whose kind comes from origin and media_type.
        hit_kind:
          type: string
          description: |
            Indexed row's hit_kind for a search_hit reference — the same value the evidence index resolved, not the client's request.
        source_ref:
          type: string
          description: Indexed row's source_ref for a search_hit reference.
        source_session_id:
          type: string
          format: uuid
          description: Indexed row's canonical session coordinate for a search_hit reference.
        start_line:
          type: integer
          minimum: 1
          description: |
            Inclusive 1-based first line of a path_file reference's scope. Absent when the reference named a whole file.
        end_line:
          type: integer
          minimum: 1
          description: Inclusive 1-based last line of a path_file reference's scope.
    RedactionKind:
      type: string
      description: |
        What a replacement claims. A secret was detected and removed; a managed_reference is a protected value written as its capability reference, so the source still holds the value the reference names; an observer_mask is a standing policy that never showed the value to observers and asserts nothing about its content.
      enum:
        - secret
        - managed_reference
        - observer_mask
    RedactionSource:
      type: string
      description: |
        The evidence lens that justified one replacement. policy accompanies observer_mask, where no detection took place.
      enum:
        - shape_rule
        - container_harvest
        - remembered_match
        - policy
    RedactedSpan:
      x-go-generate: true
      type: object
      required:
        - field
        - start
        - length
        - kind
      additionalProperties: false
      description: |
        One replacement in this message copy. Offsets index the redacted string, and length is the marker's own width — the placeholder or the capability reference — never the width of the value it replaced, so a span can be marked without disclosing how long the original was.
      properties:
        field:
          type: string
          description: |
            Dotted path of the redacted field within this message, such as content, tool_result.content, or tool_calls.0.args.command.
        start:
          type: integer
          minimum: 0
          description: Rune offset of the marker within the redacted field value.
        length:
          type: integer
          minimum: 1
          description: Rune length of the marker.
        kind:
          $ref: '#/components/schemas/RedactionKind'
        source:
          $ref: '#/components/schemas/RedactionSource'
        rule_id:
          type: string
          pattern: ^[A-Za-z0-9_.:-]+$
          description: Catalog rule identity, when a shape rule named the value.
        rule_title:
          type: string
          description: Human-readable rule name for presentation.
    HostSecretRedactionMeta:
      x-go-generate: true
      type: object
      required:
        - spans
      additionalProperties: false
      description: |
        Host-recorded provenance for spans replaced in this message copy. Presence is authoritative; literal marker text in content is not a redaction signal. Every replacement in this copy is listed, so a client renders from the list rather than by searching content, and the list describes this copy alone rather than accumulating across projections of one message.
      properties:
        spans:
          type: array
          minItems: 1
          items:
            $ref: '#/components/schemas/RedactedSpan'
    ProgressState:
      type: string
      description: State of one checklist row. Derived by the host from the coordinator's markdown markers; the coordinator never writes these strings directly.
      enum:
        - pending
        - done
        - na
    ProgressCompleteMeta:
      x-go-generate: true
      type: object
      additionalProperties: false
      properties:
        steps:
          type: array
          items:
            $ref: '#/components/schemas/ProgressStep'
        seq:
          type: integer
      required:
        - steps
        - seq
    ProgressChangeKind:
      type: string
      enum:
        - created
        - updated
        - done
        - na
        - reopened
        - removed
    ProgressChange:
      x-go-generate: true
      type: object
      additionalProperties: false
      properties:
        kind:
          $ref: '#/components/schemas/ProgressChangeKind'
        label:
          type: string
        prev_label:
          type: string
          description: The step's prior label when it was re-scoped (updated rows only).
        state:
          x-go-type: string
          $ref: '#/components/schemas/ProgressState'
      required:
        - kind
        - label
        - state
    ProgressUpdateSummary:
      x-go-generate: true
      type: object
      additionalProperties: false
      description: Aggregate progress update; the full checklist remains in ProgressDigest.
      properties:
        change_count:
          type: integer
        total_steps:
          type: integer
        pending:
          type: integer
        done:
          type: integer
        na:
          x-go-name: NA
          type: integer
      required:
        - change_count
        - total_steps
        - pending
        - done
        - na
    ProgressUpdateMeta:
      x-go-generate: true
      type: object
      additionalProperties: false
      description: Incremental progress update with a plan, delta, or aggregate summary.
      properties:
        changes:
          type: array
          items:
            $ref: '#/components/schemas/ProgressChange'
        steps:
          type: array
          items:
            $ref: '#/components/schemas/ProgressStep'
        summary:
          x-go-pointer: true
          $ref: '#/components/schemas/ProgressUpdateSummary'
        initial:
          type: boolean
        seq:
          type: integer
      required:
        - seq
    FeedbackResponseType:
      type: string
      enum:
        - text
        - single_choice
        - multi_choice
        - secret
    WorkflowFeedbackMeta:
      x-go-generate: true
      type: object
      required:
        - phase_id
        - prompt
        - response_type
      additionalProperties: false
      properties:
        phase_id:
          type: string
          pattern: ^[a-z0-9_-]+$
        prompt:
          type: string
          description: 'Card question shown to the user. ask_user: one decision (prefer choice options); markdown OK for brief context; host-capped at 800 Unicode code points so the prompt fits the 12rem composer-dock prompt area without scrolling.'
        response_type:
          $ref: '#/components/schemas/FeedbackResponseType'
        options:
          type: array
          items:
            type: string
        allow_other:
          type: boolean
        answer:
          type: string
        resolved_by:
          type: string
          description: user when a person closes the card
        resolved_by_person_id:
          type: string
          format: uuid
          description: The person who closed the card; present when resolved_by is user
        artifact_id:
          type: string
          format: uuid
          description: Optional session-tree VisualArtifact shown on the feedback card (single review/clarify)
        artifact_ids:
          x-go-name: ArtifactIDs
          type: array
          minItems: 2
          maxItems: 4
          items:
            type: string
            format: uuid
          description: Compare preference — 2–4 session-tree visuals labeled A… by order
        purpose:
          type: string
          enum:
            - clarify
            - review
            - compare
            - secret
          description: clarify = scope; review = judge one visual; compare = preference among artifact_ids
        secret:
          x-go-pointer: true
          $ref: '#/components/schemas/SecretInputMeta'
    WorkflowExplainFullPass:
      x-go-generate: true
      type: object
      description: The full scan pass the phase requested or joined, read from the project's security overview.
      required:
        - assessment_id
        - project_id
      additionalProperties: false
      properties:
        assessment_id:
          type: string
          format: uuid
        project_id:
          type: string
          format: uuid
        root_id:
          type: string
          format: uuid
          description: The attached folder the pass scans; absent for the project's primary folder.
    WorkflowExplainTopology:
      x-go-generate: true
      type: object
      description: The topology stages the phase binds; their legs are read from the run's `ui.topology_legs`.
      required:
        - stages
      additionalProperties: false
      properties:
        stages:
          type: array
          minItems: 1
          items:
            type: string
    WorkflowExplainProgress:
      x-go-generate: true
      type: object
      description: The records whose progress the note shows. Absent when the phase waits on nothing with progress to show.
      additionalProperties: false
      properties:
        full_pass:
          x-go-pointer: true
          $ref: '#/components/schemas/WorkflowExplainFullPass'
        topology:
          x-go-pointer: true
          $ref: '#/components/schemas/WorkflowExplainTopology'
    WorkflowExplainMeta:
      x-go-generate: true
      type: object
      description: A host-written note that says what a phase the host holds is doing. The text is the manifest's; progress names the live record the phase waits on.
      required:
        - phase_id
        - summary
        - body
      additionalProperties: false
      properties:
        phase_id:
          type: string
          pattern: ^[a-z0-9_-]+$
        summary:
          type: string
          description: One line, shown as the chicklet title.
        body:
          type: string
        progress:
          x-go-pointer: true
          $ref: '#/components/schemas/WorkflowExplainProgress'
    BlueprintTranscriptStatus:
      type: string
      enum:
        - proposed
        - awaiting_approval
        - approved
        - revised
        - superseded
        - rejected
    BlueprintCardPhase:
      type: string
      enum:
        - drafting
        - ready
        - changed
        - approved
        - building
        - rejected
    BlueprintMeta:
      x-go-generate: true
      type: object
      required:
        - blueprint_path
        - revision
        - revision_key
        - status
        - phase
        - phase_label
        - blueprint_title
        - can_approve
        - collapsed
        - show_actions
      additionalProperties: false
      properties:
        blueprint_path:
          type: string
          description: Project-relative path under the overlay blueprints/ directory
        revision:
          type: integer
          minimum: 0
        revision_key:
          type: string
        status:
          $ref: '#/components/schemas/BlueprintTranscriptStatus'
        phase:
          description: Card chrome state. `rejected` is the terminal record for a run that ended with the blueprint still a draft — the human answered no, or the run was superseded; status carries which.
          $ref: '#/components/schemas/BlueprintCardPhase'
        phase_label:
          type: string
        blueprint_title:
          type: string
        can_approve:
          type: boolean
        collapsed:
          type: boolean
        show_actions:
          type: boolean
    CompletionReportScope:
      type: string
      description: |
        What a grounded completion report is a report of. `session` covers a chat session's own work; `phase` covers one workflow phase; `run` is the run's declared deliverable, produced in the phase gated on topology_report_delivered. Stated on the record, never re-derived from live run state.
      enum:
        - session
        - phase
        - run
    CompletionReportFindingLocation:
      x-go-generate: true
      type: object
      additionalProperties: false
      description: Where a finding lives — an evidence handle, a place, or both.
      properties:
        handle:
          type: string
        path:
          type: string
        line:
          type: integer
          minimum: 0
    CompletionReportFindingDisposition:
      type: string
      description: |
        What a finding asks for. `act` needs work; `accept` is a risk kept on purpose; `held` is a surface examined and found sound.
      enum:
        - act
        - accept
        - held
    CompletionReportFinding:
      x-go-generate: true
      type: object
      required:
        - title
      additionalProperties: false
      description: |
        One assessed conclusion a report states: what it is, how it grades, what it means if nothing is done, and what to do about it.
      properties:
        id:
          type: string
          pattern: ^[A-Za-z0-9_-]+$
          description: Stable reference a reader can quote; shared with a verdict claim when the finding was adjudicated.
        title:
          type: string
          description: The finding in one line, in plain language.
        severity:
          type: string
          description: Severity in the subject's own vocabulary; ranked labels take a tone, unranked ones render plainly.
        status:
          type: string
          description: Where the finding stands, in the subject's own word.
        impact:
          type: string
          description: What it means if nothing is done.
        action:
          type: string
          description: What to do about it.
        where:
          type: array
          items:
            $ref: '#/components/schemas/CompletionReportFindingLocation'
        disposition:
          $ref: '#/components/schemas/CompletionReportFindingDisposition'
        answers:
          type: object
          additionalProperties:
            type: string
          description: |
            Answers to the workflow's declared rating questions, keyed by question id. Absent when a claim with the same id carries them.
        scan_group_ids:
          type: array
          items:
            type: string
          description: Scanner groups of the run's inventory this finding assesses.
    CompletionReportAskEffort:
      type: string
      description: How much work saying yes commits the team to.
      enum:
        - small
        - medium
        - large
    CompletionReportAsk:
      x-go-generate: true
      type: object
      required:
        - do
        - effort
      additionalProperties: false
      description: |
        The one decision a report asks of its reader, written for someone who has never seen the work.
      properties:
        do:
          type: string
          description: What the reader is asked to decide or approve.
        effort:
          $ref: '#/components/schemas/CompletionReportAskEffort'
        why:
          type: string
          description: The consequence that makes the ask worth the reader's time.
    CompletionReportSetAside:
      x-go-generate: true
      type: object
      required:
        - reason
      additionalProperties: false
      description: |
        Scanner groups accounted for together: named by id, or selected as every group one scanner reported entirely inside the listed path globs.
      properties:
        scan_group_ids:
          type: array
          items:
            type: string
            pattern: ^[A-Za-z0-9_-]+$
        scanner:
          type: string
        paths:
          type: array
          items:
            type: string
        reason:
          type: string
          description: Why these groups need no assessment of their own.
    CompletionReportDefectCode:
      type: string
      description: |
        The closeout rejection code of the failed requirement. `REPORT_FENCE_UNREADABLE`: the coordinator's report JSON had members the report does not declare, or of the wrong type; the host stored the members it could read and names the rest. `REPORT_DOCUMENT_INVALID`: a field broke the report schema's rules. `REPORT_CLAIM_UNREPORTED`: a claim the review left open or overturned has no finding. `REPORT_INVENTORY_UNACCOUNTED`: scanner groups have neither an assessment nor a set-aside.
      enum:
        - REPORT_FENCE_UNREADABLE
        - REPORT_DOCUMENT_INVALID
        - REPORT_CLAIM_UNREPORTED
        - REPORT_INVENTORY_UNACCOUNTED
    CompletionReportDefect:
      x-go-generate: true
      type: object
      required:
        - code
        - reason
      additionalProperties: false
      description: One document requirement a stored run report failed.
      properties:
        code:
          $ref: '#/components/schemas/CompletionReportDefectCode'
        reason:
          type: string
          description: What the check found, as it told the coordinator.
        subjects:
          type: array
          items:
            type: string
          description: A sample of what the reason is about, such as claim ids or scanner groups.
        count:
          type: integer
          description: How many subjects there are; larger than the sample when it was capped.
    CompletionReportMeta:
      x-go-generate: true
      type: object
      required:
        - scope
      additionalProperties: false
      description: |
        Identity of a grounded completion report. Present on every message of kind completion_report. Only a run-scoped completion from a workflow with enabled reports can produce a PDF; session and phase completions are ordinary replies without document downloads.
      properties:
        scope:
          $ref: '#/components/schemas/CompletionReportScope'
        surface_id:
          type: string
          pattern: ^[a-z0-9_-]+$
          description: Coordinator surface that delivered the report (its exit class is report).
        phase:
          type: string
          description: Workflow phase that produced the report; empty at session scope.
        headline:
          type: string
          description: |
            One sentence stating the report's conclusion, authored with the report. The rendered document leads with it; absent, the document leads with its title.
        summary:
          type: string
          description: |
            The report's assessment in plain language, written for a reader who will not open the detail behind it.
        findings:
          type: array
          items:
            $ref: '#/components/schemas/CompletionReportFinding'
          description: |
            The assessed conclusions the report states, most severe first. These are the report's own findings, distinct from raw scanner rows.
        limits:
          type: array
          items:
            type: string
          description: |
            Coverage gaps the report declares, one per entry: what was not checked, run, or reached. Rendered as a boxed list so a gap survives a reader who never reaches the closing paragraph.
        ask:
          x-go-pointer: true
          $ref: '#/components/schemas/CompletionReportAsk'
        set_asides:
          type: array
          items:
            $ref: '#/components/schemas/CompletionReportSetAside'
          description: |
            Scanner groups the report accounts for without assessing them one by one, each with its reason.
        defects:
          type: array
          items:
            $ref: '#/components/schemas/CompletionReportDefect'
          description: |
            Run scope only. The document requirements this report still failed when its repairs ran out and the host stored it as drafted, one per failing requirement. Absent on a report the host accepted; a report that carries defects ends its run as not accepted.
    IndexWarmingMeta:
      x-go-generate: true
      type: object
      required:
        - trigger
      additionalProperties: false
      properties:
        trigger:
          type: string
        tier:
          type: string
        topic:
          type: string
        hosts:
          type: array
          items:
            type: string
        pages:
          type: integer
        duration_ms:
          type: integer
          format: int64
        skip_reason:
          type: string
    ChatContentRow:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - index
        - offset
        - text
        - spans
      properties:
        index:
          type: integer
        offset:
          type: integer
        text:
          type: string
        spans:
          type: array
          items:
            $ref: '#/components/schemas/RedactedSpan'
    ChatContentReference:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - field
        - sha256
        - total_runes
        - size_bytes
        - rows
      description: Retained screened content; the inline field is a bounded preview, never a replacement for this revision.
      properties:
        field:
          type: string
          enum:
            - content
            - tool_output
            - tool_args
        tool_call_id:
          type: string
          pattern: ^[A-Za-z0-9_.:-]+$
        sha256:
          type: string
          pattern: ^[0-9a-f]{64}$
        total_runes:
          type: integer
        size_bytes:
          type: integer
        rows:
          type: integer
          description: Number of display rows in this revision.
        preview_rows:
          type: array
          items:
            $ref: '#/components/schemas/ChatContentRow'
    InvocationStatus:
      type: string
      enum:
        - running
        - completed
        - rejected
        - error
        - interrupted
    InvocationEvidence:
      x-go-generate: true
      type: object
      required:
        - kind
      additionalProperties: false
      properties:
        kind:
          type: string
        ref:
          type: string
        owner_ref:
          type: string
    InvocationFailure:
      x-go-generate: true
      type: object
      required:
        - code
        - class
        - retryable
      additionalProperties: false
      description: Typed terminal outcome for any invocation that did not complete.
      properties:
        code:
          type: string
        class:
          type: string
        retryable:
          type: boolean
        owner_ref:
          type: string
        details:
          type: object
          additionalProperties: true
          description: Subsystem-owner facts explaining the failure.
    InvocationIsolation:
      x-go-generate: true
      type: object
      required:
        - code
        - disposition
      additionalProperties: false
      description: Typed isolation outcome from the generic invocation boundary. It does not replace the selected subsystem owner and may accompany either a completed attempted effect or a pre-owner rejection.
      properties:
        code:
          type: string
        disposition:
          type: string
          enum:
            - retry
            - human_decision
            - control_plane
    InvocationReceipt:
      x-go-generate: true
      type: object
      required:
        - id
        - tool
        - tool_call_id
        - contract_digest
        - args_digest
        - owner
        - lifecycle
        - reversibility
        - evidence_policy
        - recovery_policy
        - status
        - invoked
        - evidence
        - started_at
      additionalProperties: false
      properties:
        id:
          type: string
          format: uuid
        tool:
          type: string
        tool_call_id:
          type: string
          pattern: ^[A-Za-z0-9_.:-]+$
        contract_digest:
          type: string
        args_digest:
          type: string
        owner:
          type: string
        lifecycle:
          type: string
          enum:
            - read_only
            - db_transaction
            - journaled_mutation
            - durable_job
            - effect_attempt
            - ephemeral_control
        reversibility:
          type: string
          enum:
            - reversible
            - recoverable
            - irreversible
        evidence_policy:
          type: string
          enum:
            - result
            - commit
            - journal
            - job
            - attempt
            - control
        recovery_policy:
          type: string
          enum:
            - none
            - owner
            - journal
            - resume
            - abandon
        status:
          $ref: '#/components/schemas/InvocationStatus'
        invoked:
          type: boolean
        evidence:
          $ref: '#/components/schemas/InvocationEvidence'
        failure:
          x-go-pointer: true
          $ref: '#/components/schemas/InvocationFailure'
        isolation:
          x-go-pointer: true
          $ref: '#/components/schemas/InvocationIsolation'
        source_revision:
          type: string
          description: Host worktree generation after this invocation settled. Verification evidence applies only while this exact generation remains current.
        source_root_digest:
          type: string
          description: SHA-256 identity of the workspace root, without exposing its local path.
        source_verdict:
          type: string
          enum:
            - passed
            - failed
            - unverifiable
          description: Terminal verdict stated by a command or verify subsystem owner, bound to source_revision. Absent for every other tool and for a launch that had not exited. This is the fact that discharges a source verification obligation; the result body never is.
        started_at:
          type: string
          format: date-time
        settled_at:
          x-go-pointer: true
          type: string
          format: date-time
    ToolResultOutcome:
      type: string
      enum:
        - completed
        - rejected
        - error
    FeedbackSubject:
      x-go-generate: true
      type: object
      required:
        - kind
        - id
      additionalProperties: false
      properties:
        kind:
          type: string
        id:
          type: string
          pattern: ^[A-Za-z0-9_.:-]+$
    ToolFeedback:
      x-go-generate: true
      type: object
      required:
        - code
      additionalProperties: false
      properties:
        code:
          type: string
        details:
          type: object
          additionalProperties: true
        subject:
          x-go-pointer: true
          $ref: '#/components/schemas/FeedbackSubject'
    ToolResultUiVisibility:
      type: string
      description: |
        Transcript quiet-chrome for tool cards. `benign` hides unless Diagnostics verbose; `normal` stays visible.
      enum:
        - normal
        - benign
    WorkerDispatch:
      x-go-generate: true
      type: object
      required:
        - worker_id
      additionalProperties: false
      properties:
        worker_id:
          type: string
          format: uuid
        child_session_id:
          type: string
          format: uuid
        agent_type:
          type: string
        delegation_id:
          type: string
          pattern: ^[A-Za-z0-9_.:-]+$
        leg_id:
          type: string
          pattern: ^[A-Za-z0-9_.:-]+$
    ToolCompletion:
      x-go-generate: true
      type: object
      required:
        - operation
        - state
      additionalProperties: false
      properties:
        operation:
          type: string
        state:
          type: string
        resource_kind:
          type: string
        resource_id:
          type: string
          pattern: ^[A-Za-z0-9_.:-]+$
        result_handle:
          type: string
          pattern: ^[A-Za-z0-9_.:-]+$
    FileEditReference:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - message_id
        - tool_call_id
        - index
      properties:
        message_id:
          type: string
          format: uuid
        tool_call_id:
          type: string
          pattern: ^[A-Za-z0-9_.:-]+$
        index:
          type: integer
          minimum: 0
          description: Zero for file_edit; one-based index for a promotion file.
    FileEditPreview:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - path
        - reference
        - added
        - removed
        - before_sha256
        - after_sha256
        - created
        - deleted
      properties:
        path:
          type: string
        root_id:
          type: string
          format: uuid
        reference:
          $ref: '#/components/schemas/FileEditReference'
        added:
          type: integer
          minimum: 0
        removed:
          type: integer
          minimum: 0
        before_sha256:
          type: string
          x-go-name: BeforeSHA256
        after_sha256:
          type: string
          x-go-name: AfterSHA256
        created:
          type: boolean
        deleted:
          type: boolean
    FileEditSnapshot:
      x-go-generate: true
      type: object
      required:
        - path
        - after
      additionalProperties: false
      properties:
        root_id:
          type: string
          format: uuid
          description: Attached project root containing the file.
        deleted:
          type: boolean
          description: The write removed the file; its diff has no current file target.
        path:
          type: string
        before:
          type:
            - string
            - 'null'
        after:
          type: string
    OverlayPromotion:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - files
      description: Project file snapshots recorded by the host at the merge event.
      properties:
        files:
          type: array
          items:
            $ref: '#/components/schemas/FileEditSnapshot'
    VisualArtifactSource:
      type: string
      description: |
        Producer face: render (host-rasterized markup), capture (observed running surface), fetch (image retrieved from the web), user (prompt attachment), or workspace (a project image file read as is).
      enum:
        - render
        - capture
        - fetch
        - user
        - workspace
    VisualArtifact:
      x-go-generate: true
      type: object
      required:
        - id
        - mime
        - source
      additionalProperties: false
      properties:
        id:
          type: string
          format: uuid
        mime:
          type: string
        bytes:
          x-go-type: '[]byte'
          type: string
          format: byte
        store_ref:
          type: boolean
        source:
          $ref: '#/components/schemas/VisualArtifactSource'
        caption:
          type: string
        evidence_handle:
          type: string
        page_id:
          type: string
          pattern: ^[A-Za-z0-9_.:-]+$
          description: Held browser page that produced live-tool media.
        recorded_at:
          x-go-pointer: true
          type: string
          format: date-time
          description: Wall-clock start of a time-based visual recording.
        duration_ms:
          x-go-name: DurationMS
          type: integer
          format: int64
          minimum: 0
          description: Duration of a time-based visual recording in milliseconds.
        width:
          type: integer
          minimum: 1
          description: Raster width in pixels, read from the stored image header. Absent for non-raster media.
        height:
          type: integer
          minimum: 1
          description: Raster height in pixels, read from the stored image header. Absent for non-raster media.
        perceive:
          type: boolean
          description: When true, host attaches this image on vision-capable models
        tool_call_id:
          type: string
          pattern: ^[A-Za-z0-9_.:-]+$
          description: Originating tool_use id for perceive attach
        origin_message_id:
          type: string
          format: uuid
          description: Assistant transcript row that originated this artifact.
    CheckpointDecisionMeta:
      x-go-generate: true
      type: object
      required:
        - checkpoint_id
        - kind
        - status
      additionalProperties: false
      properties:
        checkpoint_id:
          type: string
          format: uuid
        kind:
          $ref: '#/components/schemas/CheckpointKind'
        status:
          $ref: '#/components/schemas/CheckpointStatus'
        tool:
          type: string
          description: |
            Tool name shown on the card chip — presentation.tool when the plan carried one (argv tool for write-root / egress), else the proposed action tool.
        subject:
          type: string
          description: |
            Human-facing grant subject of the decision — command, path, host, or other single subject shown on the approval card. Persisted so the durable decision chicklet stays informative after resolve.
        causing_command:
          type: string
          description: |
            Argv that raised the card when subject is not already that command (write root, destination, secret, …). Empty when the subject is the reviewed command itself.
        location:
          type: string
          description: |
            Secret identity line persisted from presentation.location (`origin → destination`). Empty on non-secret decisions.
        guidance:
          type: string
          description: |
            Explicit human direction attached to a denial. Informational only; it never grants authority for a replacement action.
        grant_ids:
          x-go-name: GrantIDs
          type: array
          items:
            type: string
            pattern: ^[A-Za-z0-9_.:-]+$
          description: |
            Every host-opaque ApprovalGrant.id installed by the selected option. Composed options can install several; the decision chicklet revokes them together.
        grant_scope:
          $ref: '#/components/schemas/ApprovalGrantScope'
        grant_title:
          type: string
          description: Reviewed host title of the selected lease.
    SkillActivation:
      x-go-generate: true
      type: object
      required:
        - name
        - instructions
      additionalProperties: false
      properties:
        name:
          type: string
          description: Skill id from SKILL.md frontmatter.
        description:
          type: string
          description: Full authored description, not the bounded index projection.
        instructions:
          type: string
          description: |
            SKILL.md body the activation delivered, without the host's directory and file trailer. Carried so the card renders the skill rather than re-deriving it from the envelope text.
        dir:
          type: string
          description: |
            Provenance label for where the skill was read from — an absolute host path for a project or installed skill, a config-relative path for one that ships in the binary. It is a label, not an openable path.
        resources:
          type: array
          items:
            type: string
          description: Bundled files listed in the activation, relative to the skill directory.
        resources_omitted:
          type: integer
          minimum: 0
          description: Regular resources beyond the listing bound.
        project:
          type: boolean
          description: Skill came from the project overlay rather than a host or bundled pack.
        pack_id:
          type: string
          pattern: ^[A-Za-z0-9_.:-]+$
        license:
          type: string
        compatibility:
          type: string
        allowed_tools:
          type: string
          description: |
            Author's allowed-tools frontmatter. Shown so a skill that declares tool limits reads honestly; this host never honors it as a permission.
    ToolProcessHandle:
      x-go-generate: true
      type: object
      required:
        - handle
        - running
      additionalProperties: false
      description: |
        A background handle the tool returned instead of an exit: a command job, or a read-only call that outlived its foreground wait and keeps running. The calling agent polls a command with command_output and a held call with held_result, and can sleep on either with wait(conditions=[{"kind":"process_done"}]); Den binds its chicklet to the session process mirror through the handle.
      properties:
        handle:
          type: string
          minLength: 1
          pattern: ^[A-Za-z0-9_-]+$
          description: Live background handle, a command job or a held call, to poll, wait on, or stop.
        running:
          type: boolean
          description: |
            The host's view when the result was written. It never flips on exit; the session process mirror reports that.
    CitationGroundingCheckStatus:
      type: string
      description: Host-authored semantic outcome of a grounding check. Advisory checks landed but require review; they are neither passes nor failures.
      enum:
        - passed
        - failed
        - advisory
    CitationGroundingFinding:
      x-go-generate: true
      type: object
      additionalProperties: false
      properties:
        handle:
          type: string
          description: Host-minted evidence handle resolved from the session ledger.
        path:
          type: string
        line:
          type: integer
        excerpt:
          type: string
        verdict:
          $ref: '#/components/schemas/CitationVerdict'
        note:
          type: string
        openable:
          x-go-pointer: true
          type: boolean
          description: True when the cited path resolves to a regular file under the project jail; false when it is a directory, missing, or outside the jail; absent when the host did not resolve the path. Den links only when openable is not false.
    CitationGroundingCitedEvidence:
      x-go-generate: true
      type: object
      additionalProperties: false
      properties:
        handle:
          type: string
          description: Host-minted evidence handle resolved from the session ledger.
        path:
          type: string
        line:
          type: integer
        excerpt:
          type: string
        verdict:
          $ref: '#/components/schemas/CitationVerdict'
        openable:
          x-go-pointer: true
          type: boolean
          description: True when the cited path resolves to a regular file under the project jail; false when it is a directory, missing, or outside the jail; absent when the host did not resolve the path. Den links only when openable is not false.
    CitationVerification:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - method
        - reason
      description: The validation scope the closeout declared. Retained on the grounding audit because the projected prose omits the trailer.
      properties:
        method:
          type: string
          enum:
            - inspection
            - targeted
            - project
            - blocked
        reason:
          type: string
    VerdictOutcome:
      x-go-generate: true
      type: object
      required:
        - verdict
      additionalProperties: false
      description: A recorded review_loop verdict with its validated citation provenance. Terminal verdicts always carry grounding.
      properties:
        verdict:
          type: string
          description: The submitted verdict enum value.
        terminal:
          type: boolean
          description: True when the gate was satisfied and the host is advancing.
        attempt:
          type: integer
          description: Review round counter after this submission.
        iteration_cap:
          type: integer
        evidence_key:
          type: string
        phase:
          type: string
        grounding:
          x-go-pointer: true
          $ref: '#/components/schemas/CitationGrounding'
          description: Host citation audit of the verdict's cited evidence.
    NavigationEntryKind:
      type: string
      description: Navigable project filesystem entry identity.
      enum:
        - file
        - folder
    NavigationTarget:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - project_id
        - root_id
        - path
        - entry_kind
      properties:
        project_id:
          type: string
          format: uuid
        root_id:
          type: string
          format: uuid
        path:
          type: string
        entry_kind:
          $ref: '#/components/schemas/NavigationEntryKind'
        worker_id:
          type: string
          format: uuid
    SourceContext:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - locations
        - truncated
      description: Host-recorded source locations supplied with this message; independent of evidence and filesystem inventory.
      properties:
        locations:
          type: array
          maxItems: 4096
          items:
            $ref: '#/components/schemas/NavigationTarget'
        truncated:
          type: boolean
    NavigationStatus:
      type: string
      enum:
        - pending
        - resolved
        - ambiguous
        - missing
        - unavailable
    NavigationReference:
      x-go-generate: true
      type: object
      required:
        - id
        - syntax
        - mention
        - project_id
        - path
        - status
        - explicit
      additionalProperties: false
      description: A producer destination and its bounded host resolution. Navigation never supplies citation grounding.
      properties:
        id:
          type: string
          pattern: ^[A-Za-z0-9_.:-]+$
          minLength: 1
          maxLength: 128
        syntax:
          type: string
          description: Markdown occurrence kind (link, code, fence, or text).
        mention:
          type: string
          minLength: 1
          maxLength: 4096
          description: Exact Markdown destination or standalone path token; independent of display label.
        project_id:
          type: string
          format: uuid
        root_id:
          type: string
          format: uuid
        path:
          type: string
          minLength: 1
        entry_kind:
          $ref: '#/components/schemas/NavigationEntryKind'
        status:
          $ref: '#/components/schemas/NavigationStatus'
        explicit:
          type: boolean
          description: True only for an authored Markdown destination, rather than an inferred prose path.
        deleted:
          type: boolean
          description: The exact path is absent and has retained deletion history. Availability is rechecked when opening; this never pins the link to a file or version.
        worker_id:
          type: string
          format: uuid
        line:
          type: integer
        end_line:
          type: integer
        candidates:
          type: array
          maxItems: 32
          items:
            $ref: '#/components/schemas/NavigationTarget'
    MessageVisibility:
      type: string
      enum:
        - transcript
        - internal
    PromptCacheTier:
      type: string
      description: 'Host-only prompt-assembly mark on a stable boundary of a request, and the tier it closes: the standing prefix (system prompt, tool schemas, and the blocks that follow them), which changes only when the host re-decides it, or stable history, which grows every call. Each provider places a prompt cache breakpoint on the row''s final content block, with the lifetime its policy gives that tier. Stamped per turn, never persisted, never accepted as input.'
      enum:
        - standing
        - history
    TurnLoadSkill:
      x-go-generate: true
      type: object
      additionalProperties: false
      description: The skill automatically read for this turn.
      properties:
        name:
          type: string
        score:
          type: number
          format: double
      required:
        - name
        - score
    SessionTranscriptPage:
      x-go-generate: true
      type: object
      required:
        - messages
        - turn_clocks
        - turn_loads
        - watermark
      additionalProperties: false
      properties:
        messages:
          type: array
          items:
            $ref: '#/components/schemas/Message'
          description: One contiguous Ord window in ascending order (default = newest page / live tail)
        turn_clocks:
          type: object
          additionalProperties:
            $ref: '#/components/schemas/TurnClock'
          description: Durable clocks of the visible user turns opened by messages in this page, keyed by opening message id. Worker transcript pages carry none.
        turn_loads:
          type: object
          additionalProperties:
            type: array
            items:
              $ref: '#/components/schemas/TurnLoad'
          description: Decision-engine receipts of the turns opened by messages in this page, keyed by opening message id, each list in recorded order.
        watermark:
          type: integer
          format: int64
          description: Transcript mutation clock at read time, taken in the same transaction as the rows; Den installs the page as a baseline and keeps rows whose seq exceeds it
        before_cursor:
          type: string
          description: Opaque pagination cursor to fetch the previous (older) window of messages. Omitted when no older messages exist.
        after_cursor:
          type: string
          description: Opaque pagination cursor to fetch the next (newer) window of messages. Omitted when no newer messages exist.
    WorkflowRequestState:
      x-go-generate: true
      type: object
      required:
        - cadence
        - status
      additionalProperties: false
      properties:
        cadence:
          type: string
          enum:
            - once
            - each_turn
        status:
          type: string
          enum:
            - waiting
            - pending
            - resolved
        text:
          type: string
        source:
          type: string
          enum:
            - explicit
            - default
            - answer
            - inherited
        sequence:
          type: integer
          minimum: 0
    CoordinatorBatchPhase:
      type: string
      description: Phase of the coordinator batch, as projected onto the wire by ToWirePhase.
      enum:
        - dispatch
        - integrate
        - synthesize
        - closed
    WorkerBudgetRequest:
      x-go-generate: true
      type: object
      description: A running worker's request that its coordinator raise its tool-round ceiling.
      required:
        - rounds
        - requested_max
        - remaining_work
        - tool_loops_used
        - requested_at
      additionalProperties: false
      properties:
        rounds:
          type: integer
          minimum: 1
          description: Additional tool rounds the worker asked for beyond its ceiling at request time
        requested_max:
          type: integer
          minimum: 3
          maximum: 120
          description: Ceiling the worker asked for, bounded by the host maximum
        remaining_work:
          type: array
          minItems: 1
          maxItems: 6
          items:
            type: string
          description: Worker-authored work the extra rounds would cover; data for the coordinator, not instructions
        tool_loops_used:
          type: integer
          minimum: 0
          description: Rounds the worker had completed when it asked
        requested_at:
          type: string
          format: date-time
    WorkerContextUsage:
      x-go-generate: true
      type: object
      additionalProperties: false
      description: 'Per-worker context occupancy mirrored to the Den worker-card ring: prompt tokens on the latest turn against the model window, plus the compaction trigger point. Same shape the coordinator context ring uses.'
      properties:
        prompt_tokens:
          type: integer
          minimum: 0
          description: Prompt tokens on the worker's latest turn — current window occupancy
        window:
          type: integer
          minimum: 0
          description: Model context window in tokens; the ring's denominator
        compaction_threshold:
          type: integer
          minimum: 0
          description: Prompt-token level at which compaction fires for this worker
    WorkspaceProvisionStrategy:
      type: string
      enum:
        - direct_cow
        - bridge_cow
        - direct_copy
    WorkspacePreparation:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - stage
        - files
        - bytes
      properties:
        strategy:
          $ref: '#/components/schemas/WorkspaceProvisionStrategy'
        stage:
          type: string
          enum:
            - probing
            - surveying_source
            - materializing_seed
            - snapshotting_branch
        files:
          type: integer
          format: int64
          minimum: 0
        bytes:
          type: integer
          format: int64
          minimum: 0
        total_bytes:
          type: integer
          format: int64
          minimum: 0
          description: Logical regular-file bytes when a source survey has completed
    TaskScopeMode:
      type: string
      enum:
        - read
        - write
    TaskScope:
      x-go-generate: true
      type: object
      additionalProperties: false
      description: |
        Job metadata describing one worker dispatch. Paths are optional focus
        guidance and never an access, mutation, or promotion boundary.
        BaseOverlayID, when set, declares this dispatch as a stacked overlay
        based on another pending write overlay rather than primary. Stacked
        overlays rebase when their parent is promoted, and are orphaned when
        their parent is rejected.
      properties:
        mode:
          $ref: '#/components/schemas/TaskScopeMode'
        paths:
          type: array
          items:
            type: string
          description: Optional repo-relative focus globs
        base_overlay_id:
          type: string
          pattern: ^[a-zA-Z0-9_-]+$
          description: |
            Optional. Names the parent write overlay this dispatch stacks on.
            Empty means "based on primary." Must be a non-terminal overlay
            (pending, applying, rebasing) on the same parent session, else
            spawn rejects with OVERLAY_BASE_MISSING or OVERLAY_BASE_NOT_PENDING.
    WorkerCompletionFinding:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - path
      properties:
        path:
          type: string
        evidence:
          type: string
        line:
          type: integer
        excerpt:
          type: string
        note:
          type: string
        claim:
          type: string
        adversary:
          type: string
        precondition:
          type: string
        severity:
          type: string
    WorkerEvidenceObligation:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - kind
        - status
      properties:
        kind:
          type: string
        status:
          type: string
        evidence_refs:
          type: array
          items:
            type: string
        reason:
          type: string
    WorkerCompletionReport:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - leg_status
      properties:
        leg_status:
          type: string
        files_modified:
          type: array
          items:
            type: string
        objectives_met:
          type: array
          items:
            type: string
        remaining_risk:
          type: array
          items:
            type: string
        suggested_next_task:
          type: string
        brief:
          type: string
        findings:
          type: array
          items:
            $ref: '#/components/schemas/WorkerCompletionFinding'
        cited_urls:
          x-go-name: CitedURLs
          type: array
          items:
            type: string
        evidence_obligations:
          type: array
          items:
            $ref: '#/components/schemas/WorkerEvidenceObligation'
    WorkerDiffStat:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - path
        - insertions
        - deletions
      properties:
        path:
          type: string
        insertions:
          type: integer
        deletions:
          type: integer
    WorkerChangeReport:
      x-go-generate: true
      type: object
      additionalProperties: false
      properties:
        changed_paths:
          type: array
          items:
            type: string
        workspace_dirty:
          type: boolean
        mutation_tools:
          type: array
          items:
            type: string
        survey_tools:
          type: array
          items:
            type: string
        receipt_count:
          type: integer
        diff_stat:
          type: array
          items:
            $ref: '#/components/schemas/WorkerDiffStat'
    WorkerFailure:
      x-go-generate: true
      type: object
      additionalProperties: false
      description: Structured worker execute failure for Den (SSE worker topic and GET /v1/workers). Copy templates live in config/packs/painted-wolf/platform/host/user-notices.
      properties:
        code:
          type: string
        title:
          type: string
        message:
          type: string
        suggested_action:
          type: string
        actions:
          type: array
          items:
            $ref: '#/components/schemas/NoticeAction'
        tier:
          $ref: '#/components/schemas/NoticeTier'
          description: Host-declared tier; always `non_catastrophic` on this surface.
        scope:
          $ref: '#/components/schemas/NoticeScope'
          description: Host-declared scope. A worker failure belongs to the session that spawned it.
        resolution:
          type: string
          description: Which declared resolution produced `tier`/`scope`.
    ApprovalPlanStage:
      type: string
      enum:
        - pre_spawn
        - pre_dial
        - pre_send
    ApprovalSubjectKind:
      type: string
      enum:
        - action
        - action_set
        - socket_set
        - direct_ip
        - process_control
        - host_execution
        - local_listen
        - loopback_connect
        - destination_set
        - write_root_set
        - read_path_set
        - secret
        - package_set
    ApprovalTarget:
      x-go-generate: true
      type: object
      required:
        - kind
        - label
      additionalProperties: false
      properties:
        kind:
          type: string
        label:
          type: string
        details:
          type: object
          additionalProperties: true
    ApprovalSubject:
      x-go-generate: true
      type: object
      required:
        - kind
        - title
        - targets
      additionalProperties: false
      properties:
        kind:
          $ref: '#/components/schemas/ApprovalSubjectKind'
        title:
          type: string
        summary:
          type: string
        targets:
          type: array
          minItems: 1
          items:
            $ref: '#/components/schemas/ApprovalTarget'
    ApprovalFileChange:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - path
        - operation
        - before
        - after
        - before_bytes
        - after_bytes
      properties:
        target:
          type: string
          description: File surface being changed; index identifies a Git staging change.
          enum:
            - file
            - index
        path:
          type: string
        root_id:
          type: string
          format: uuid
        from_path:
          type: string
        operation:
          type: string
        before:
          type: string
        after:
          type: string
        before_sha256:
          type: string
        after_sha256:
          type: string
        before_bytes:
          type: integer
          format: int64
          minimum: 0
        after_bytes:
          type: integer
          format: int64
          minimum: 0
        preview_note:
          type: string
          description: Explains binary, oversized, or metadata-only previews.
    ApprovalSecretDestinationKind:
      type: string
      enum:
        - model_provider
        - service
        - process
        - file
      description: |
        Who receives the value. `model_provider` is the configured AI provider and the model reads the send; `service` is an external destination the host addresses by URL; `process` is a local process whose onward destinations this app does not observe; `file` is a local file on disk.
    ApprovalSecretRecipient:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - label
        - surface
        - kind
      description: Reviewed handoff identity. It does not assert onward delivery or authentication.
      properties:
        label:
          type: string
        surface:
          type: string
        kind:
          $ref: '#/components/schemas/ApprovalSecretDestinationKind'
    ApprovalSecretOriginKind:
      type: string
      enum:
        - file
        - field
    ApprovalSecretLocation:
      type: object
      required:
        - origin
        - destination
        - origin_kind
        - destination_kind
      additionalProperties: false
      description: |
        Secret identity line under the subject. Present on every secret card: a person cannot judge a send without knowing where it goes.
      properties:
        secret_names:
          type: array
          items:
            type: string
        recipients:
          type: array
          items:
            $ref: '#/components/schemas/ApprovalSecretRecipient'
        origin:
          type: string
          description: |
            Origin face (for example `.env.local:7` or `in query`).
        destination:
          type: string
          description: Destination face (provider, host, or seam).
        origin_kind:
          $ref: '#/components/schemas/ApprovalSecretOriginKind'
        destination_kind:
          $ref: '#/components/schemas/ApprovalSecretDestinationKind'
        path:
          type: string
          description: Project-relative file path when origin_kind is file.
        line:
          type: integer
          minimum: 1
          description: 1-based line when origin_kind is file and a line is known.
        reveal_tool_call_id:
          type: string
          pattern: ^[A-Za-z0-9_.:-]+$
          description: |
            Transcript tool-call id for Show in chat. Empty when no row exists (a held model turn has no send tool).
    ApprovalGate:
      type: string
      description: The reason a human was interrupted. Closed set; every approval card names one.
      enum:
        - secret_outbound
        - consent_drift
        - remote_package_execution
        - remote_package_execution_known
        - authority_misuse
        - agent_policy_change
        - sensitive_location
        - agent_chosen_outbound
        - secret_exposed_outbound
        - user_rule
        - outside_roots_write
        - outside_roots_read
        - unobserved_channel
        - explicit_approval_request
        - capability_widening
        - first_host
        - mcp_unleased
        - incomplete_facts
    ApprovalCitedFact:
      type: object
      required:
        - key
        - value
        - source
      additionalProperties: false
      properties:
        gate:
          description: The approval gate this fact supports.
          $ref: '#/components/schemas/ApprovalGate'
        key:
          description: What was observed, as a stable machine key (for example file.path).
          type: string
        value:
          description: The observed value, rendered for a person.
          type: string
        source:
          description: Which producer observed it — containment, the ask-trigger path catalog, a detection pack, the destination or payload lens. It names the instrument, so a person can weigh how the host knows.
          type: string
    ConsequenceBand:
      type: string
      enum:
        - standard
        - high_risk
    ConsequenceCode:
      type: string
      enum:
        - secret
        - detection
        - write_root
        - local_socket
        - local_listen
        - loopback_connect
        - direct_ip
    ApprovalRuleCitation:
      x-go-generate: true
      type: object
      required:
        - category
        - pattern
        - effect
      additionalProperties: false
      properties:
        category:
          type: string
        pattern:
          type: string
        effect:
          type: string
        command:
          type: string
        unit_id:
          type: string
          pattern: ^[A-Za-z0-9_.:-]+$
        pack_id:
          type: string
          pattern: ^[A-Za-z0-9_.:-]+$
        scope:
          type: string
          enum:
            - device
            - project
    ApprovalPlanPresentation:
      x-go-generate: true
      type: object
      required:
        - action
        - impact
      additionalProperties: false
      properties:
        ignore_candidate_id:
          type: string
          pattern: ^[A-Za-z0-9_.:-]+$
          description: Ephemeral review handle for one unprotected value. Does not authorize release or contain secret bytes.
        file_changes:
          type: array
          items:
            $ref: '#/components/schemas/ApprovalFileChange'
        action:
          type: string
        tool:
          type: string
        command:
          type: string
          description: |
            Argv attributed to this card. When the subject is that same command (ordinary action approvals), it is the reviewed subject. When the subject is a write root, destination, socket, or secret, it is the causing command Den shows under the grant subject. Empty when no argv is known. Secret command cards carry a host-redacted argv here so From can name the send without leaking the match.
        location:
          x-go-pointer: true
          $ref: '#/components/schemas/ApprovalSecretLocation'
        impact:
          type: string
        who:
          type: string
        if_wrong:
          type: string
        allow_line:
          type: string
        lead:
          type: string
          description: The one distinguishing fact the card leads with — first contact with this host, what the chat has already read — lifted by the host from the primary gate's citations. Absent when the gate cited no such fact.
        gate:
          description: The primary reason this card exists. Other gates that fired on the same action are listed in the plan reasons.
          $ref: '#/components/schemas/ApprovalGate'
        cited:
          x-go-type: '[]PresentedFact'
          description: The facts that made the gates fire, grouped in gate-priority order. A card with a gate and no citations is invalid — the decision has to be able to show its work.
          type: array
          items:
            $ref: '#/components/schemas/ApprovalCitedFact'
        option_note:
          description: Why the redacted send on this card is unavailable. The face stays Send redacted and is not selectable; clients render this as host copy beside the impact line and never infer unavailability themselves.
          type: string
        grant_delta:
          type: string
        consequence_band:
          $ref: '#/components/schemas/ConsequenceBand'
        consequence_code:
          $ref: '#/components/schemas/ConsequenceCode'
        detection:
          x-go-pointer: true
          $ref: '#/components/schemas/DetectionMatch'
        approval_rules:
          type: array
          description: Extension approval-rule identities that governed this action.
          items:
            $ref: '#/components/schemas/ApprovalRuleCitation'
    ApprovalOptionKind:
      type: string
      enum:
        - current_action
        - lease
        - redacted
        - quiet
        - tracked
    ApprovalOptionRung:
      type: string
      description: 'Ladder position and ordering key. Distinct from scope (authority boundary): the day rung may carry chat or project scope.'
      enum:
        - tracked
        - redacted
        - unchanged
        - once
        - day
        - chat
        - project
        - device
    ApprovalOptionDecision:
      type: string
      enum:
        - approve
        - redact
        - track
    ApprovalOption:
      x-go-generate: true
      type: object
      required:
        - id
        - kind
        - rung
        - title
        - coverage
        - expires_when
        - reask_when
        - decision_action
      additionalProperties: false
      properties:
        id:
          type: string
          pattern: ^[A-Za-z0-9_.:-]+$
        kind:
          $ref: '#/components/schemas/ApprovalOptionKind'
        rung:
          $ref: '#/components/schemas/ApprovalOptionRung'
        scope:
          $ref: '#/components/schemas/ApprovalGrantScope'
        group:
          type: string
          description: Optional ladder heading when a card carries a second subject (absorbed predicate or host resources). Absent means the primary ladder.
        title:
          type: string
        coverage:
          type: string
        expires_when:
          type: string
        reask_when:
          type: string
        decision_action:
          $ref: '#/components/schemas/ApprovalOptionDecision'
        disabled:
          type: boolean
          description: When true, the option stays in its ladder slot but is not selectable. A slot that vanished would move every rung below it, so an unreachable rung is disabled in place and carries a note.
        note:
          type: string
          description: Host copy saying why a disabled option cannot be picked here. Present exactly when disabled is true.
    ApprovalPlan:
      x-go-generate: true
      type: object
      required:
        - id
        - action_digest
        - stage
        - subject
        - presentation
        - reasons
        - options
        - recommended_option_id
      additionalProperties: false
      properties:
        id:
          type: string
          pattern: ^[A-Za-z0-9_.:-]+$
        action_digest:
          type: string
        stage:
          $ref: '#/components/schemas/ApprovalPlanStage'
        subject:
          $ref: '#/components/schemas/ApprovalSubject'
        presentation:
          $ref: '#/components/schemas/ApprovalPlanPresentation'
        reasons:
          description: Every gate that fired, in citation-priority order. The first is the primary reason and is repeated in presentation.gate.
          type: array
          items:
            $ref: '#/components/schemas/ApprovalGate'
        options:
          type: array
          minItems: 1
          items:
            $ref: '#/components/schemas/ApprovalOption'
        elevated_effects:
          description: Host-classified elevated access that a selectable saved option on this card can install. Empty when no choice would make the composer unlock indicator appear. Allow once is excluded.
          type: array
          items:
            $ref: '#/components/schemas/ElevatedAccessEffect'
        recommended_option_id:
          type: string
          pattern: ^[A-Za-z0-9_.:-]+$
          description: Id of the face option. Exactly one; computed by the host from the subject kind and rung set. Clients must not invent a fallback. A disabled face stays in that slot; Enter does not select another send.
    ApprovalRepeat:
      x-go-generate: true
      type: object
      required:
        - reason_key
        - count
      additionalProperties: false
      properties:
        reason_key:
          type: string
        count:
          type: integer
          minimum: 1
          description: Distinct subjects asked about under this reason.
        asks:
          type: integer
          minimum: 1
          description: Cards this reason has raised since the last user turn, this one included. Presentation only; nothing counts it to decide.
        subjects:
          type: array
          items:
            type: string
        subjects_truncated:
          type: boolean
        suppressed_count:
          type: integer
          minimum: 1
    PreviewAttachment:
      x-go-generate: true
      type: object
      required:
        - session_id
        - page_id
        - assistant_message_id
        - tool_call_id
      additionalProperties: false
      description: Held page identity for restoring a preview watch; frames are omitted
      properties:
        session_id:
          type: string
          format: uuid
        page_id:
          type: string
          pattern: ^[A-Za-z0-9_.:-]+$
        assistant_message_id:
          type: string
          format: uuid
          description: Assistant transcript row that originated this preview surface.
        tool_call_id:
          type: string
          pattern: ^[A-Za-z0-9_.:-]+$
          description: Tool invocation within the containing assistant row.
        parent_session_id:
          type: string
          format: uuid
        width:
          type: integer
        height:
          type: integer
        url:
          type: string
        title:
          type: string
        idle:
          x-go-pointer: true
          type: boolean
    ResolveMessageNavigationRequest:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - message_id
        - content_sha256
      properties:
        message_id:
          type: string
          format: uuid
        content_sha256:
          type: string
          minLength: 64
          maxLength: 64
        reference_id:
          type: string
          pattern: ^[A-Za-z0-9_.:-]+$
          maxLength: 128
        candidate_index:
          x-go-pointer: true
          type: integer
          minimum: 0
          maximum: 31
          description: Validate this candidate of the stored ambiguous reference; requires reference_id.
    MessageNavigationResponse:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - content_sha256
        - references
      properties:
        content_sha256:
          type: string
        references:
          type: array
          maxItems: 128
          items:
            $ref: '#/components/schemas/NavigationReference'
    ChatContentPage:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - reference
        - text
        - offset
        - end_offset
        - complete
        - spans
      description: A positional window into one retained revision. offset and end_offset are rune coordinates; reading continues by requesting offset=end_offset.
      properties:
        reference:
          $ref: '#/components/schemas/ChatContentReference'
        text:
          type: string
        offset:
          type: integer
          minimum: 0
          description: Rune offset where this window starts.
        end_offset:
          type: integer
          minimum: 0
          description: Rune offset just past this window; reference.total_runes at the end of the content.
        complete:
          type: boolean
          description: True when this window reaches the end of the content.
        spans:
          type: array
          description: Screened provenance with rune offsets relative to this page.
          items:
            $ref: '#/components/schemas/RedactedSpan'
        rows:
          type: array
          items:
            $ref: '#/components/schemas/ChatContentRow'
    ChatContentMatch:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - offset
        - length
      properties:
        offset:
          type: integer
        length:
          type: integer
    ChatContentSearchPage:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - matches
      properties:
        matches:
          type: array
          items:
            $ref: '#/components/schemas/ChatContentMatch'
        next_cursor:
          type: string
          description: Opaque cursor for the next search page; absent when the search reached the end of the content.
    InvocationReceiptList:
      x-go-generate: true
      type: object
      required:
        - invocations
      additionalProperties: false
      properties:
        invocations:
          type: array
          items:
            $ref: '#/components/schemas/InvocationReceipt'
        next_cursor:
          type: string
    PromptReferencePathFile:
      type: object
      required:
        - kind
        - project_id
        - path
      additionalProperties: false
      properties:
        kind:
          allOf:
            - $ref: '#/components/schemas/PromptReferenceKind'
          enum:
            - path-file
        project_id:
          type: string
          format: uuid
        root_id:
          type: string
          format: uuid
          description: Attached-root id the path resolves under.
        path:
          type: string
          description: Project-relative path; never absolute; jailed under root_id.
        start_line:
          type: integer
          minimum: 1
          description: Inclusive 1-based start line for a selection range.
        end_line:
          type: integer
          minimum: 1
          description: Inclusive 1-based end line; defaults to start_line.
    PromptReferencePathFolder:
      type: object
      required:
        - kind
        - project_id
        - path
      additionalProperties: false
      properties:
        kind:
          allOf:
            - $ref: '#/components/schemas/PromptReferenceKind'
          enum:
            - path-folder
        project_id:
          type: string
          format: uuid
        root_id:
          type: string
          format: uuid
          description: Attached-root id the path resolves under.
        path:
          type: string
          description: Project-relative folder path; never absolute; jailed under root_id.
    PromptReferenceArtifact:
      type: object
      required:
        - kind
        - project_id
        - artifact_id
      additionalProperties: false
      properties:
        kind:
          allOf:
            - $ref: '#/components/schemas/PromptReferenceKind'
          enum:
            - artifact
        project_id:
          type: string
          format: uuid
        artifact_id:
          type: string
          pattern: ^[A-Za-z0-9_.:-]+$
          description: Existing VisualArtifact id.
    PromptReferenceSearchHit:
      type: object
      required:
        - kind
        - project_id
        - source_ref
      additionalProperties: false
      properties:
        kind:
          allOf:
            - $ref: '#/components/schemas/PromptReferenceKind'
          enum:
            - search-hit
        project_id:
          type: string
          format: uuid
        session_id:
          type: string
          format: uuid
          description: Session the hit came from, when it has one.
        source_ref:
          type: string
          description: Indexed row's source_ref.
        hit_kind:
          type: string
          description: Indexed row's hit_kind.
    AbortSessionRequest:
      x-go-generate: true
      type: object
      additionalProperties: false
      properties:
        reason:
          type: string
          description: Optional note for worker cancellation envelopes
    RewindPreviewRequest:
      x-go-generate: true
      type: object
      required:
        - message_id
      additionalProperties: false
      properties:
        message_id:
          type: string
          format: uuid
    RewindPreviewFile:
      x-go-generate: true
      type: object
      required:
        - root_id
        - path
        - target_path
      additionalProperties: false
      properties:
        root_id:
          type: string
          format: uuid
          x-go-name: RootID
        path:
          type: string
        target_path:
          type: string
    RewindIssue:
      x-go-generate: true
      type: object
      required:
        - root_id
        - path
        - code
      additionalProperties: false
      properties:
        root_id:
          type: string
          format: uuid
          x-go-name: RootID
        path:
          type: string
        code:
          type: string
          description: Structured reason the host cannot safely reverse a source effect.
    RewindPreviewResponse:
      x-go-generate: true
      type: object
      required:
        - plan_digest
        - files
        - issues
        - truncated_message_count
      additionalProperties: false
      properties:
        plan_digest:
          type: string
        files:
          type: array
          items:
            $ref: '#/components/schemas/RewindPreviewFile'
        issues:
          type: array
          items:
            $ref: '#/components/schemas/RewindIssue'
        truncated_message_count:
          type: integer
    RewindMode:
      type: string
      description: Restores state before the selected user turn.
      enum:
        - before_turn
    RewindSessionRequest:
      x-go-generate: true
      type: object
      required:
        - operation_id
        - message_id
        - plan_digest
      additionalProperties: false
      properties:
        operation_id:
          type: string
          format: uuid
          description: Durable identity for this rewind; exact retries replay the committed result.
        message_id:
          type: string
          format: uuid
          description: The visible user message to rewind to. Only a user ask that is not an internal host kick, workflow boundary, or continuation is eligible; anything else is rejected with rewind_anchor_ineligible.
        plan_digest:
          type: string
          description: Digest returned by the preview; a changed plan is rejected.
        mode:
          $ref: '#/components/schemas/RewindMode'
    RewindSessionResponse:
      x-go-generate: true
      type: object
      required:
        - restored_paths
        - truncated_message_count
      additionalProperties: false
      properties:
        restored_paths:
          type: array
          description: Root-relative paths changed by reversing the entire selected suffix. Missing exact history and conflicting later contributions block the whole rewind before files or transcript rows change.
          items:
            type: string
        truncated_message_count:
          type: integer
          description: Transcript rows removed, counting the anchor itself
        restored_prompt:
          type: string
          description: The anchor ask's user-authoritative prose only (never attachment fences). Den's Edit flow preloads the composer from this rather than reading the row it just asked the host to delete.
        restored_content_parts:
          type: array
          description: The anchor's mixed-origin content parts so Edit can restage attachment and reference chips after the transcript row is removed.
          items:
            $ref: '#/components/schemas/MessageContentPart'
        restored_artifact_ids:
          x-go-name: RestoredArtifactIDs
          type: array
          description: Visual artifact ids linked on the anchor ask for Edit restage.
          items:
            type: string
            format: uuid
    BackgroundProcessListResponse:
      x-go-generate: true
      type: object
      required:
        - processes
      additionalProperties: false
      properties:
        processes:
          type: array
          items:
            $ref: '#/components/schemas/BackgroundProcess'
    PreviewListResponse:
      x-go-generate: true
      type: object
      required:
        - previews
      additionalProperties: false
      properties:
        previews:
          type: array
          items:
            $ref: '#/components/schemas/PreviewAttachment'
    ManagedSecret:
      x-go-generate: true
      type: object
      description: Metadata for protected material. Ordinary reads are value-free. A person using the installed desktop app may reveal the current value through fresh, challenge-bound operating-system authentication.
      required:
        - reference
        - name
        - scope
        - origin
        - created_at
        - state
        - version
        - use_count
        - reveal_count
      additionalProperties: false
      properties:
        reference:
          type: string
          pattern: ^\{\{paintedwolf-secret:[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\}\}$
          description: Provider-safe token accepted by supported outbound agent tools.
        chat_session_id:
          type:
            - string
            - 'null'
          format: uuid
          description: 'The chat whose agents may spend chat-scoped material; absent for project scope. Scope decides who may spend a capability, never how long it lives: only an explicit revoke ends one.'
        chat_title:
          type: string
          description: Title of the owning chat while it exists and has one.
        chat_deleted:
          type: boolean
          description: True when the owning chat was deleted. The capability stays active for people to reveal, replace, promote, or revoke; no agent can spend it, because no chat can see it.
        name:
          type: string
        purpose:
          type: string
        scope:
          type: string
          enum:
            - chat
            - project
        origin:
          type: string
          description: How the capability was created. This is immutable provenance, not a claim about where the current stored value lives or whether an external credential still matches it. `file_marked` means a person marked a value in a project file; `composer_marked` means a person protected selected draft text for a chat; `settings_entered` means a person typed a value into project settings. File marks and settings entries are project acts; composer marks initially take chat scope. `cookie_jar` means the host stored the cookies an agent's own `http_request` calls received under a named jar; every cookie value in it is screened like any other managed value. `token_jar` is the same for tokens `http_request` captured from responses.
          enum:
            - generated
            - ask_user_response
            - detected
            - file_marked
            - composer_marked
            - settings_entered
            - cookie_jar
            - token_jar
        format:
          type: string
          enum:
            - base64url
            - hex
            - alphanumeric
          description: Present only for generated material.
        entropy_bits:
          x-go-type: int64
          type: integer
          minimum: 128
          maximum: 1024
          description: Present only for generated material.
        created_at:
          x-go-type: string
          type: string
          format: date-time
        agent_use_ends_at:
          x-go-type: '*string'
          type:
            - string
            - 'null'
          format: date-time
          description: After this time the agent may no longer substitute the capability. Authenticated human reveal remains available for recovery. The protected bytes remain available for screening, and this deadline does not alter an external credential or a project file.
        state:
          type: string
          description: '`revoked` is terminal and no administration reaches it. `agent_use_expired` means its agent-use deadline passed; a new deadline revives it. `unavailable` has metadata but no readable bytes, and supplying a value again restores it under the same reference.'
          enum:
            - active
            - revoked
            - agent_use_expired
            - unavailable
        version:
          x-go-type: int64
          type: integer
          minimum: 0
          description: Counts stored values, not edits. Starts at 1 and rises with each stored-value replacement while the reference stays the same. Zero means no readable value was ever recorded.
        value_replaced_at:
          x-go-type: '*string'
          type:
            - string
            - 'null'
          format: date-time
          description: When the current protected value replaced its predecessor; absent before the first replacement.
        last_used_at:
          x-go-type: '*string'
          type:
            - string
            - 'null'
          format: date-time
          description: Most recent substitution attempt, refusals included; absent when the reference has never reached a tool call.
        use_count:
          x-go-type: int64
          type: integer
          minimum: 0
          description: Substitution attempts inside the bounded history window, refusals included.
        last_revealed_at:
          x-go-type: '*string'
          type:
            - string
            - 'null'
          format: date-time
          description: Most recent successful authenticated human reveal; absent when never revealed.
        reveal_count:
          x-go-type: int64
          type: integer
          minimum: 0
          description: Successful authenticated human reveals retained for this capability.
    ArtifactReferenceKind:
      type: string
      description: |
        What holds a durable claim on an artifact. Each reference is a row written in the referrer's own transaction and cascaded by it, so retention and delete impact are the same question.
      enum:
        - message_present
        - message_attachment
        - tool_result
        - project_cover
    ArtifactReferenceCount:
      x-go-generate: true
      type: object
      required:
        - kind
        - count
      additionalProperties: false
      properties:
        kind:
          $ref: '#/components/schemas/ArtifactReferenceKind'
        count:
          type: integer
          minimum: 0
    CheckpointListResponse:
      x-go-generate: true
      type: object
      required:
        - checkpoints
      additionalProperties: false
      properties:
        checkpoints:
          type: array
          items:
            $ref: '#/components/schemas/CheckpointEvent'
    ProjectPromotion:
      x-go-generate: true
      type: object
      required:
        - destination_path
        - init_git
        - phase
        - last_error
        - updated_at
      additionalProperties: false
      properties:
        destination_path:
          type: string
        init_git:
          type: boolean
        phase:
          type: string
          enum:
            - queued
            - staged
            - installed
            - committed
        last_error:
          type: string
        updated_at:
          type: string
          format: date-time
    ProjectListResponse:
      x-go-generate: true
      type: object
      required:
        - projects
      additionalProperties: false
      properties:
        projects:
          type: array
          items:
            $ref: '#/components/schemas/Project'
        next_cursor:
          type: string
          description: Opaque cursor for the next project page; empty or omitted at the end.
    CloneProjectRequest:
      x-go-generate: true
      type: object
      required:
        - url
        - parent_dir
      additionalProperties: false
      properties:
        url:
          type: string
        parent_dir:
          type: string
          description: Existing folder the clone is created under.
        name:
          type: string
          description: Folder name for the clone; derived from the url when omitted.
    ExtensionSuggestionStatus:
      type: string
      enum:
        - available
        - installed
        - declined
        - version_mismatch
    ExtensionSuggestion:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - id
        - source
        - status
      properties:
        id:
          type: string
          pattern: ^[a-zA-Z0-9_-][a-zA-Z0-9_.-]*(/[a-zA-Z0-9_-][a-zA-Z0-9_.-]*)*$
        source:
          type: string
        version:
          type: string
          description: Version constraint from the project suggestion.
        ref:
          type: string
        contributes:
          type: string
          description: Untrusted display summary of what the pack would add.
        installed_version:
          type: string
        status:
          $ref: '#/components/schemas/ExtensionSuggestionStatus'
        configuration:
          type: object
          additionalProperties: {}
          description: Suggested configuration values. Display only; never applied from the repo.
    ExtensionSuggestionsResponse:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - project_id
        - suggestion_revision
        - suggestions
      properties:
        project_id:
          type: string
          format: uuid
        suggestion_revision:
          type: string
          description: Digest of the exact parsed project proposal returned in this response.
        suggestions:
          type: array
          items:
            $ref: '#/components/schemas/ExtensionSuggestion'
    FolderDetect:
      x-go-generate: true
      type: object
      required:
        - path
      additionalProperties: false
      properties:
        path:
          type: string
        project_id:
          type: string
          format: uuid
          description: The project that already holds this folder as a root, when there is one. Absent for a folder no project covers. Opening a folder that carries this reopens that project; minting a second one over the same tree would lose its trust state.
        extension_suggestions:
          x-go-pointer: true
          $ref: '#/components/schemas/ExtensionSuggestionsResponse'
          description: Install choices available before opening the project.
    ProjectRemovalCheck:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - project_id
        - name
        - state
        - revision
      properties:
        project_id:
          type: string
          format: uuid
        name:
          type: string
        state:
          type: string
          enum:
            - checked
            - unknown
        revision:
          type: string
        reason:
          type: string
          description: Why an unknown check could not read the project's references. Absent when checked.
          enum:
            - suggestions_disabled
            - overlay_unavailable
            - overlay_incompatible
            - root_unavailable
            - manifest_unreadable
            - manifest_invalid
    ProjectRemovalReason:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - code
        - subject_id
      properties:
        code:
          type: string
          enum:
            - project_suggestion
            - extension_dependency
            - device_selection
            - device_configuration
            - stock_extension
            - incomplete_project_evidence
        subject_id:
          type: string
          pattern: ^[A-Za-z0-9_.:-]+$
    ProjectRemovalExtension:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - pack_id
        - disposition
        - reasons
      properties:
        pack_id:
          type: string
          pattern: ^[A-Za-z0-9_.:-]+$
        disposition:
          type: string
          enum:
            - eligible
            - retained
            - unknown
        reasons:
          type: array
          items:
            $ref: '#/components/schemas/ProjectRemovalReason'
    ProjectRemovalAssessment:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - project_id
        - assessment_token
        - extension_revision
        - complete
        - checks
        - extensions
      properties:
        project_id:
          type: string
          format: uuid
        assessment_token:
          type: string
        extension_revision:
          type: string
        complete:
          type: boolean
        checks:
          type: array
          items:
            $ref: '#/components/schemas/ProjectRemovalCheck'
        extensions:
          type: array
          items:
            $ref: '#/components/schemas/ProjectRemovalExtension'
    ProjectRemovalRequest:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - operation_id
        - assessment_token
        - remove_extensions
        - force
      properties:
        operation_id:
          type: string
          format: uuid
        assessment_token:
          type: string
        remove_extensions:
          type: array
          uniqueItems: true
          maxItems: 256
          items:
            type: string
        force:
          type: boolean
    ProjectRemovalResult:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - operation_id
        - project_id
        - project_state
        - cleanup_state
        - extensions
        - reason
        - failure_code
        - documents
        - sessions
        - workers
        - overlays
      properties:
        assessment:
          x-go-type: '*ProjectRemovalAssessment'
          oneOf:
            - $ref: '#/components/schemas/ProjectRemovalAssessment'
            - type: 'null'
        operation_id:
          type: string
          format: uuid
        project_id:
          type: string
          format: uuid
        project_state:
          type: string
          enum:
            - deleted
            - retained
            - unknown
        cleanup_state:
          type: string
          enum:
            - not_requested
            - removed
            - retained
            - failed
            - interrupted
        extensions:
          type: array
          items:
            type: string
        reason:
          type: string
        failure_code:
          type: string
        documents:
          type: integer
          minimum: 0
        sessions:
          type: integer
          minimum: 0
        workers:
          type: integer
          minimum: 0
        overlays:
          type: integer
          minimum: 0
    AgentContextInstruction:
      x-go-generate: true
      type: object
      required:
        - path
      additionalProperties: false
      properties:
        path:
          type: string
          description: Root-relative AGENTS.md path in runtime precedence order.
    AgentContextSkill:
      x-go-generate: true
      type: object
      required:
        - name
        - description
        - path
      additionalProperties: false
      properties:
        name:
          type: string
        description:
          type: string
        path:
          type: string
          description: Root-relative SKILL.md path.
    ProjectAgentContext:
      x-go-generate: true
      type: object
      required:
        - project_id
        - root_id
        - path
        - instructions_enabled
        - skills_enabled
        - instructions
        - skills
      additionalProperties: false
      description: Actual repository guidance available at one root-relative path. Built by the same AGENTS.md and project-skill resolvers used for model context.
      properties:
        project_id:
          type: string
          format: uuid
        root_id:
          type: string
          format: uuid
        path:
          type: string
        instructions_enabled:
          type: boolean
        skills_enabled:
          type: boolean
        instructions:
          type: array
          items:
            $ref: '#/components/schemas/AgentContextInstruction'
        skills:
          type: array
          items:
            $ref: '#/components/schemas/AgentContextSkill'
    ManagedSecretList:
      x-go-generate: true
      type: object
      required:
        - secrets
      additionalProperties: false
      properties:
        secrets:
          type: array
          items:
            $ref: '#/components/schemas/ManagedSecret'
    CreateManagedSecretRequest:
      x-go-generate: true
      type: object
      description: 'Stores a value a person entered in project settings. This is the one managed-secret path where a credential travels in a request body: every other path either generates the bytes host-side or slices them from a document the host already holds. The response carries only the capability. Ordinary reads never return the value; authenticated reveal is the sole disclosure path.'
      required:
        - name
        - purpose
        - secret_value
        - operation_id
      additionalProperties: false
      properties:
        name:
          type: string
          minLength: 1
          maxLength: 80
        purpose:
          type: string
          minLength: 1
          maxLength: 240
          description: Required. A project-scoped capability outlives every chat, so the next chat can only tell what it is by reading this.
        secret_value:
          type: string
          minLength: 4
          writeOnly: true
          description: 'The credential. Never echoed back and never returned by an ordinary read. The field is named for what it holds so the host''s own request-capture scrubber redacts it by name; renaming it would silently stop that. The floor is the length the screen can tell apart from ordinary text: below it the host refuses rather than mint a reference nothing would ever redact.'
        agent_use_ends_at:
          x-go-type: string
          type: string
          format: date-time
          description: Optional deadline, at most one year out, after which the agent may no longer substitute this capability. Authenticated human reveal remains available for recovery. Omit for no deadline. This does not alter an external credential.
        operation_id:
          type: string
          format: uuid
          description: Idempotency key for a retried submission.
    UpdateManagedSecretRequest:
      x-go-generate: true
      type: object
      description: Administers one capability. Omitted fields are left as they stand, and nothing here reaches the stored value. A revoked capability is terminal and accepts no update.
      additionalProperties: false
      properties:
        name:
          x-go-pointer: true
          type: string
          minLength: 1
          maxLength: 80
        purpose:
          x-go-pointer: true
          type: string
          maxLength: 240
        agent_use_ends_at:
          type:
            - string
            - 'null'
          format: date-time
          description: Deadline after which the agent may no longer substitute this capability; null removes the deadline.
        scope:
          x-go-pointer: true
          type: string
          enum:
            - project
          description: Scope only widens. Promoting a chat capability to `project` lets it outlive the chat that created it, and requires a purpose. Narrowing a project capability has no honest target chat, so it is refused.
    ReplaceManagedSecretValueRequest:
      x-go-generate: true
      type: object
      description: 'Puts new bytes behind an unchanged reference, or restores a capability whose stored value went missing. Supported agent tooling already holding the reference keeps working. Only Painted Wolf''s protected copy changes: this does not modify a project file, rotate an external credential, revoke it, or activate the replacement at its provider. The replaced value is retired, not deleted: its bytes stay readable to provider screening so a credential the agent has already seen keeps being redacted, but a retired value never resolves again.'
      required:
        - secret_value
      additionalProperties: false
      properties:
        secret_value:
          type: string
          minLength: 4
          writeOnly: true
          description: 'The new credential. Never echoed back. Named so the host''s own request-capture scrubber redacts it by name. It holds the same length floor as minting: a replacement the screen could not recognize would silently retire a protection that worked.'
    ManagedSecretUse:
      x-go-generate: true
      type: object
      description: One recorded attempt to substitute a reference into a tool call. Carries what the host decided and who asked, never the value and never the argument the value was going into.
      required:
        - used_at
        - tool_name
        - outcome
        - delivery
      additionalProperties: false
      properties:
        tool_call_id:
          type: string
          pattern: ^[A-Za-z0-9_.:-]+$
          description: Invocation identity shared with authorization history; absent for non-tool resolution.
        delivery:
          type: string
          enum:
            - pending
            - not_dispatched
            - withheld
            - handed_off
            - redacted
          description: Delivery to the local executor or transport. A handoff does not imply process startup, network delivery, or successful authentication. Pending means no terminal observation was recorded.
        used_at:
          x-go-type: string
          type: string
          format: date-time
        tool_name:
          type: string
        outcome:
          type: string
          description: '`resolved` means the host substituted the value. Other outcomes are refusals.'
          enum:
            - resolved
            - revoked
            - agent_use_expired
            - unavailable
            - out_of_scope
        version:
          x-go-type: int64
          type: integer
          minimum: 1
          description: Stored value the attempt reached; absent when it was refused before one was read.
        session_id:
          type: string
          format: uuid
        chat_session_id:
          type: string
          format: uuid
    ManagedSecretUseList:
      x-go-generate: true
      type: object
      description: A bounded rolling window of recent uses, newest first. Not a permanent ledger.
      required:
        - uses
      additionalProperties: false
      properties:
        uses:
          type: array
          items:
            $ref: '#/components/schemas/ManagedSecretUse'
        next_cursor:
          type: string
          description: Opaque cursor for the next page of secret uses; empty or omitted at the end.
    BeginManagedSecretRevealRequest:
      x-go-generate: true
      type: object
      required:
        - window_label
      additionalProperties: false
      description: Starts one installed-app reveal. The native shell supplies its actual calling window label; it is bound into the proof payload and audit record, but it is not an authorization identity.
      properties:
        window_label:
          type: string
          minLength: 1
          maxLength: 120
    ManagedSecretRevealChallenge:
      x-go-generate: true
      type: object
      required:
        - challenge_id
        - proof_payload
        - prompt
        - version
        - expires_at
      additionalProperties: false
      description: One short-lived request for native user-presence authentication. The proof payload is opaque canonical bytes; the native shell signs it only after the operating system verifies the device user.
      properties:
        challenge_id:
          type: string
          format: uuid
        proof_payload:
          type: string
          pattern: ^[A-Za-z0-9_-]+$
          description: Base64url without padding of the exact bytes the native shell signs.
        prompt:
          type: string
          description: Host-authored reason shown by the operating-system authentication prompt.
        version:
          x-go-type: int64
          type: integer
          minimum: 1
        expires_at:
          x-go-type: string
          type: string
          format: date-time
    CompleteManagedSecretRevealRequest:
      x-go-generate: true
      type: object
      required:
        - authenticator
        - signature
      additionalProperties: false
      description: Challenge-bound native user-presence proof. The signature covers the challenge payload plus the authenticator identifier; the ordinary API bearer is never sufficient to reveal a value.
      properties:
        authenticator:
          type: string
          enum:
            - macos_user_presence
            - windows_user_presence
        signature:
          type: string
          pattern: ^[A-Za-z0-9_-]+$
          description: Base64url without padding Ed25519 signature from the installed desktop shell.
    ManagedSecretRevealResponse:
      x-go-generate: true
      type: object
      required:
        - secret_value
        - version
        - revealed_at
        - remask_after_ms
      additionalProperties: false
      description: The current value after successful operating-system authentication. The response body is categorically withheld from debug capture. Den does not persist the value, emit it as an event, or make it available to agent tools.
      properties:
        secret_value:
          type: string
          readOnly: true
        version:
          x-go-type: int64
          type: integer
          minimum: 1
        revealed_at:
          x-go-type: string
          type: string
          format: date-time
        remask_after_ms:
          x-go-type: int64
          type: integer
          minimum: 5000
          maximum: 60000
    PromoteProjectRequest:
      x-go-generate: true
      type: object
      required:
        - root_path
      additionalProperties: false
      properties:
        root_path:
          type: string
          description: An empty folder that receives the draft through a restart-safe save.
        init_git:
          type: boolean
          description: Initialise a git repository in the staged workspace before installation.
    UpdateProjectRootRequest:
      x-go-generate: true
      type: object
      additionalProperties: false
      properties:
        is_primary:
          x-go-pointer: true
          type: boolean
        label:
          x-go-pointer: true
          type: string
          minLength: 1
          maxLength: 64
          description: Rename the display label / @label addressing token; empty rejected, unique per project (case-insensitive).
    ProjectSourceEntryCreatedResponse:
      x-go-generate: true
      type: object
      required:
        - path
      additionalProperties: false
      description: |
        Completed in-app creation. Only the normalized path comes back — a new
        file is read through GET /source like any other, which is where the
        editor gets the sha256 its first save must echo.
      properties:
        path:
          type: string
          description: Normalized repo-relative path (slash-separated)
    ProjectSourceLifecycleResponse:
      x-go-generate: true
      type: object
      required:
        - root_id
        - path
      additionalProperties: false
      description: Canonical address a rename or copy landed on.
      properties:
        root_id:
          type: string
          format: uuid
          description: Attached root that contains the new path
        path:
          type: string
          description: Normalized root-relative path (slash-separated)
    SourceHistoryMutationResponse:
      x-go-generate: true
      type: object
      required:
        - entry_id
        - root_id
        - path
        - op
        - is_dir
      additionalProperties: false
      description: Canonical filesystem transition produced by undo or redo.
      properties:
        entry_id:
          type: string
          format: uuid
        root_id:
          type: string
          format: uuid
        path:
          type: string
        from_path:
          type: string
        op:
          $ref: '#/components/schemas/SourceChangeOp'
        is_dir:
          type: boolean
    SourceOperationResult:
      description: Typed outcome of a successfully completed source operation.
      x-go-type: any
      oneOf:
        - $ref: '#/components/schemas/ProjectSourceEntryCreatedResponse'
        - $ref: '#/components/schemas/ProjectSourceLifecycleResponse'
        - $ref: '#/components/schemas/SourceHistoryMutationResponse'
    SourceOperationStatus:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - root_id
        - path
        - from_path
        - operation_id
        - complete
        - state
        - phase
        - operation
        - cancelable
        - entries_processed
        - bytes_processed
        - created_at
      description: Durable file operation identified by its mutation operation_id. Disconnects leave accepted work running; terminal results survive restart.
      properties:
        root_id:
          type: string
          format: uuid
        path:
          type: string
        from_path:
          type: string
        expected_entry_id:
          type: string
          format: uuid
          description: Original history head for an undo or redo request; omitted for other operations.
        operation_id:
          type: string
          format: uuid
        complete:
          type: boolean
        state:
          type: string
          enum:
            - queued
            - running
            - completed
            - failed
            - canceled
            - interrupted
        phase:
          type: string
          description: Current work phase. Progress counts describe that phase and may reset on phase changes.
        operation:
          type: string
          enum:
            - createProjectSourceEntry
            - deleteProjectSource
            - renameProjectSource
            - copyProjectSource
            - undoProjectSourceHistory
            - redoProjectSourceHistory
        cancelable:
          type: boolean
          description: Cancellation is available before filesystem publication; published changes finish reconciliation and support undo through file history.
        entries_processed:
          type: integer
          format: int64
          minimum: 0
        bytes_processed:
          type: integer
          format: int64
          minimum: 0
        result:
          x-go-type: any
          $ref: '#/components/schemas/SourceOperationResult'
        error:
          x-go-pointer: true
          $ref: '#/components/schemas/Error'
        created_at:
          type: string
          format: date-time
          description: When the request was first submitted; a retry keeps it.
        completed_at:
          x-go-pointer: true
          type: string
          format: date-time
          description: When the request reached its terminal state; present exactly when complete.
    SourceOperationList:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - operations
      properties:
        operations:
          type: array
          items:
            $ref: '#/components/schemas/SourceOperationStatus'
        next_cursor:
          type: string
    SourceEncoding:
      type: string
      enum:
        - utf-8
        - utf-8-bom
        - utf-16le
        - utf-16le-bom
        - utf-16be
        - utf-16be-bom
    SecretSpanState:
      type: string
      description: 'What the host knows about the bytes under one screened span. The state is the ledger''s standing of those bytes, never the reach of whoever reads the screen. `tracked` is the current value of a live managed capability: the agent never reads it, and chats the capability admits use it by reference. `retired` is a replaced version or a revoked capability; its reference no longer resolves while its bytes remain screening evidence, so the file still holds a live credential nothing can spend. `detected` is a catalog or container-evidence hit with nothing tracking it, screened by shape rather than exact provenance.'
      enum:
        - tracked
        - retired
        - detected
    SecretSpan:
      x-go-generate: true
      type: object
      description: One screened range in an editor document. Offsets are rune positions into the document draft, matching the redaction-span convention. A span never carries the matched bytes; clients render from this list rather than by searching content, and must not infer a span from text.
      required:
        - start
        - end
        - state
        - rule_id
      additionalProperties: false
      properties:
        start:
          type: integer
          minimum: 0
          description: Rune offset of the first matched character in the draft.
        end:
          type: integer
          minimum: 0
          description: Rune offset one past the last matched character.
        state:
          $ref: '#/components/schemas/SecretSpanState'
        rule_id:
          type: string
          pattern: ^[a-zA-Z0-9_.:/-]+$
          description: Catalog rule identity, or the managed-capability evidence id.
        rule_title:
          type: string
          description: Human-readable rule name for presentation.
        reference:
          type: string
          description: Managed capability token. Present only when state is `tracked`. A project screen carries it for every live capability, whichever chat created it; a jar entry has none.
        shape:
          type: string
          description: Synthetic character classes and run lengths — a receipt for which bytes matched, never the bytes.
    SecretScreen:
      x-go-generate: true
      type: object
      description: Provenance for one completed text screen. Presence means screening ran; absence means screening was unavailable. An empty span list does not say the text is free of credentials — no pattern catalog recognizes every one. Spans use rune offsets into the exact text carried beside this screen.
      required:
        - truncated
      additionalProperties: false
      properties:
        truncated:
          type: boolean
          description: Whether the screen covered only a prefix of the document.
        screened_bytes:
          type: integer
          minimum: 0
          description: Bytes actually screened.
        screened_revision:
          type: integer
          format: int64
          description: Editor-document revision the spans were computed against. Omitted for immutable comparison content.
        catalog_version:
          type: string
          description: Vendored detection catalog the screen ran against. Empty when no vendor catalog is loaded.
        spans:
          type: array
          items:
            $ref: '#/components/schemas/SecretSpan'
    SourceReaderEndpoint:
      x-go-generate: true
      type: object
      required:
        - state
        - size_bytes
        - availability
      additionalProperties: false
      description: |
        One exact immutable endpoint. Availability is explicit because version
        identity and metadata remain explicit for metadata-only captures and
        unexpectedly unavailable content.
      properties:
        version_id:
          type: string
          format: uuid
          description: Absent for working-file, repository-object, and carried-start endpoints.
        root_id:
          type: string
          format: uuid
        path:
          type: string
        state:
          type: string
          enum:
            - content
            - directory
            - absent
            - unresolved
        sha256:
          x-go-name: Sha256
          type: string
        size_bytes:
          type: integer
          format: int64
          minimum: 0
        availability:
          type: string
          enum:
            - available
            - absent
            - directory
            - unavailable
            - not_captured
            - binary
            - unresolved
        reason:
          type: string
        secret_screen:
          x-go-type: '*SecretScreen'
          allOf:
            - $ref: '#/components/schemas/SecretScreen'
          description: Screening summary for this endpoint. Exact spans arrive with requested rows. Omitted when screening is unavailable or this side has no readable text.
    VersionComparisonSource:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - kind
        - version_id
      properties:
        kind:
          type: string
          enum:
            - version
        version_id:
          type: string
          format: uuid
    SourceDeletedFile:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - deleted_at
        - previous
      description: The last recorded deletion at this exact path in this workspace, with its preceding immutable state. Never an editable working file.
      properties:
        deleted_at:
          type: string
          format: date-time
        previous:
          $ref: '#/components/schemas/SourceReaderEndpoint'
        source:
          x-go-pointer: true
          $ref: '#/components/schemas/VersionComparisonSource'
    ProjectSourceReadResponse:
      x-go-generate: true
      type: object
      required:
        - workspace_id
        - workspace_kind
        - path
        - content
        - over_limit
        - binary
        - size_bytes
        - writable
      additionalProperties: false
      description: |
        Sandbox-validated project file content or metadata for the in-app
        source viewer. Editable ceiling is 4 MiB. When over_limit or binary is
        true, content is empty and Den opens an info card or image tab (bytes
        via GET …/source/raw). Truncated-prefix reads are not returned.
        UTF-16LE and UTF-16BE text is decoded for editing; other non-UTF-8 text
        is refused with 415 unsupported_encoding rather than reported as binary.
      properties:
        file_id:
          type: string
          format: uuid
          description: Stable identity of the logical file across moves and workspaces; absent until the source ledger has recorded this path.
        version_id:
          type: string
          format: uuid
          description: The recorded version holding exactly the content read; absent when the ledger has not yet recorded this content (an external change the watcher has not settled). A read records nothing.
        workspace_id:
          type: string
          pattern: ^ws_[0-9a-f]{32}$
          description: Physical project or worker workspace that served this read.
        workspace_kind:
          $ref: '#/components/schemas/SourceWorkspaceKind'
        path:
          type: string
          description: Normalized repo-relative path (slash-separated)
        language:
          type: string
          description: Supported host grammar for this path, matching the editor_language contribution fact. Omitted when no supported grammar applies.
        content:
          type: string
          description: |
            Full UTF-8 editor text when editable (byte-order marks stripped);
            empty for over-limit or binary, and empty when an open response
            carries the editor document, whose crdt_update holds the text
        over_limit:
          type: boolean
          description: True when size_bytes exceeds the 4 MiB editable ceiling
        binary:
          type: boolean
          description: |
            True for non-text content (including images). Content is empty;
            image tabs load bytes from GET …/source/raw.
        size_bytes:
          x-go-type: int64
          type: integer
          minimum: 0
          description: Original file size on disk in bytes
        modified_at:
          x-go-type: string
          x-go-name: ModifiedAt
          type: string
          format: date-time
          description: File modification time (UTC)
        mime:
          x-go-name: MIME
          type: string
          description: Sniffed content type (not extension-derived)
        sha256:
          type: string
          description: |
            Hex SHA-256 of on-disk bytes (BOM included when present), set only
            for full editable text reads. Echo it as base_sha256 on an editor save.
        root_id:
          type: string
          format: uuid
          description: |
            Attached root that served the read (the mirrored root for worker
            overlay reads); path is root-relative to it. Echo it as root_id
            to pin later reads and saves to the same root.
        encoding:
          $ref: '#/components/schemas/SourceEncoding'
          description: |
            Round-trip encoding for editable text (and over-limit text metadata).
            Omitted for binary and image reads. Echo on PUT /source.
        deleted:
          x-go-pointer: true
          $ref: '#/components/schemas/SourceDeletedFile'
          description: Present only for an absent path opened with include_deleted. Current content and SHA-256 remain empty and writable is false.
        writable:
          type: boolean
          description: |
            True when owner-write mode bits are set at load. Report only — the
            write path still fails honestly if the fact was stale.
    PutProjectSourceRequest:
      x-go-generate: true
      type: object
      required:
        - operation_id
        - path
        - root_id
        - content
        - encoding
        - base_sha256
      additionalProperties: false
      description: In-app editor save of one project file (full replacement content).
      properties:
        operation_id:
          type: string
          format: uuid
          description: Stable mutation identity; exact retries return the original result and conflicting reuse returns 409 idempotency_conflict.
        path:
          type: string
          minLength: 1
          description: Repo-relative path (slash-separated) of an existing file
        root_id:
          type: string
          format: uuid
          description: Attached root the save lands on; omitted searches every root primary-first
        content:
          type: string
          description: Full replacement UTF-8 editor text without a byte-order mark. Decoded text is bounded to 8 MiB to allow UTF-16 expansion; the encoded file, including its byte-order mark, must fit 4 MiB. The JSON envelope permits escaping of the full supported content.
        encoding:
          $ref: '#/components/schemas/SourceEncoding'
          description: |
            Exact encoding from the matching read. The host restores UTF-8 and
            UTF-16 byte-order marks when named by the encoding. Any missing or
            unsupported value is 400.
        base_sha256:
          type: string
          minLength: 1
          description: |
            sha256 reported by the read (or previous save) this edit was based
            on; mismatched on-disk content refuses the save with 409
            source_write_conflict.
    ProjectSourceWriteResponse:
      x-go-generate: true
      type: object
      required:
        - path
        - size_bytes
        - sha256
      additionalProperties: false
      description: Completed in-app editor save.
      properties:
        path:
          type: string
          description: Normalized repo-relative path (slash-separated)
        size_bytes:
          x-go-type: int64
          type: integer
          minimum: 0
          description: New file size in bytes
        sha256:
          type: string
          description: Hex SHA-256 of the saved content (base for the next save)
    CreateProjectSourceEntryRequest:
      x-go-generate: true
      type: object
      required:
        - operation_id
        - path
        - kind
      additionalProperties: false
      description: |
        In-app creation of one new, empty project file or folder. Missing parent
        folders are created along the way, so a nested path needs no separate
        call per level. A new file starts empty — its content arrives through
        PUT /source like any later edit.
      properties:
        operation_id:
          type: string
          format: uuid
          description: Stable mutation identity; exact retries return the original result and conflicting reuse returns 409 idempotency_conflict.
        path:
          type: string
          minLength: 1
          description: |
            Repo-relative path (slash-separated) that must not exist yet;
            every segment stays inside the selected root.
        kind:
          type: string
          enum:
            - file
            - folder
          description: Which kind of entry to create at path
        root_id:
          type: string
          format: uuid
          description: Attached root the new entry lands on; omitted uses the primary root
    SourceWatchState:
      type: string
      description: |
        Whether edits made outside the app under a root reach the host as they happen. `live` observes every directory; `partial` misses some, and changes there surface on the next inventory pass; `faulted` means the platform stream reported an error and events since then may be missing; `unwatched` means no watcher is bound.
      enum:
        - live
        - partial
        - faulted
        - unwatched
    SourceWatchCoverage:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - state
        - recursive
        - unwatched_directories
      properties:
        state:
          $ref: '#/components/schemas/SourceWatchState'
        recursive:
          type: boolean
          description: One root registration observes every descendant.
        unwatched_directories:
          type: integer
          minimum: 0
          description: Directories a per-directory watcher could not register.
    SourceWorkspaceRoot:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - id
        - path
        - watch
      properties:
        branch_id:
          type: string
          pattern: ^[a-zA-Z0-9:._-]+$
          description: Durable source branch for this root; omitted for the primary tree. Roots unaffected by a worktree binding retain their primary branch.
        id:
          type: string
          format: uuid
          description: Stable attached-root identity.
        path:
          type: string
          minLength: 1
          description: Absolute physical path for this root in the resolved workspace.
        watch:
          $ref: '#/components/schemas/SourceWatchCoverage'
    SourceInventoryState:
      x-go-generate: true
      type: object
      required:
        - status
        - complete
        - roots_generation
        - indexed_files
      additionalProperties: false
      description: Project-scoped background source inventory lifecycle. Session creation never waits for this state.
      properties:
        status:
          type: string
          enum:
            - uninitialized
            - queued
            - scanning
            - ready
            - error
        complete:
          type: boolean
          description: True only when the indexed snapshot matches the current roots generation.
        roots_generation:
          type: integer
          minimum: 0
        indexed_files:
          type: integer
          format: int64
          minimum: 0
        source_snapshot_id:
          type: string
          pattern: ^[a-zA-Z0-9_.:/-]+$
          description: Content-addressed identity of the complete published source manifest.
        worktree_epoch:
          type: string
          description: Process generation vector proven stable when the snapshot was published.
        started_at:
          x-go-pointer: true
          type: string
          format: date-time
        completed_at:
          x-go-pointer: true
          type: string
          format: date-time
    SourceWorkspace:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - workspace_id
        - session_scoped
        - roots
        - inventory
      properties:
        workspace_id:
          type: string
          pattern: ^ws_[0-9a-f]{32}$
          minLength: 1
          description: Identity derived from the project's resolved physical roots.
        session_scoped:
          type: boolean
          description: |
            True when the requested chat's worktree substitutes the project's roots, so reads and retained views of this workspace must carry that chat's id. False when the project's own checkout answers, and every chat resolving to this workspace_id shares it without one.
        roots:
          type: array
          description: Ordered physical roots used by every Files read and path action.
          items:
            $ref: '#/components/schemas/SourceWorkspaceRoot'
        inventory:
          $ref: '#/components/schemas/SourceInventoryState'
          description: |
            The ledger's pass over the tree. Outside edits to tracked files reach history by path before it completes; untracked files enter history when the pass, or an open command window, admits them.
    AgentActivityKind:
      type: string
      enum:
        - reading
        - editing
      description: What a running tool call is doing to its file target. `reading` is a read-family tool; `editing` is a write-family tool. Derived from the tool's catalog identity, never from arguments or prose.
    AgentActivity:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - tool_call_id
        - tool
        - root_id
        - path
        - kind
      description: A running tool call with one file target, from call start until its result commits.
      properties:
        tool_call_id:
          type: string
          pattern: ^[A-Za-z0-9_.:-]+$
        worker_id:
          type: string
          pattern: ^[A-Za-z0-9_.:-]+$
          description: Worker job that made the call; omitted for the chat itself.
        tool:
          type: string
          minLength: 1
        root_id:
          type: string
          format: uuid
        path:
          type: string
          minLength: 1
          description: Root-relative path.
        kind:
          $ref: '#/components/schemas/AgentActivityKind'
    AgentPresenceExtent:
      type: string
      enum:
        - range
        - whole_file
        - matches
        - insertion
      description: How the ranges of a presence item cover its file. `range` ranges are whole-line spans, with empty ranges where lines go in. `whole_file` has no ranges; the item covers the file. `matches` ranges are single-line character spans returned by a search. `insertion` ranges are all empty points where lines go in.
    AgentTextRange:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - start_line
        - end_line
      description: One span of a document. Lines are 1-based and inclusive and describe the document at the revision the item was recorded against. Characters are UTF-16 offsets within their line and appear only for search matches and for empty ranges where lines go in. Anchor and head are CRDT relative positions in the item's editor document epoch; clients resolve them in their replica so a span follows later edits, and fall back to lines only when the item has no document.
      properties:
        start_line:
          type: integer
          minimum: 1
        end_line:
          type: integer
          minimum: 1
        start_character:
          type: integer
          minimum: 0
          x-go-pointer: true
        end_character:
          type: integer
          minimum: 0
          x-go-pointer: true
        anchor:
          type: string
          format: byte
          x-go-type: '[]byte'
          maxLength: 1024
        head:
          type: string
          format: byte
          x-go-type: '[]byte'
          maxLength: 1024
    AgentRead:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - id
        - sequence
        - tool_call_id
        - tool
        - root_id
        - path
        - extent
        - ranges
        - stale
      description: Text a read-family tool call returned to the model in this turn, after host output limits. Output that spilled to a preview records no ranges.
      properties:
        id:
          type: string
          pattern: ^[A-Za-z0-9_.:-]+$
        worker_id:
          type: string
          pattern: ^[A-Za-z0-9_.:-]+$
          description: Worker job that made the read; omitted for the chat itself.
        sequence:
          type: integer
          format: int64
          minimum: 1
          description: Order within the session's turn; higher is newer.
        tool_call_id:
          type: string
          pattern: ^[A-Za-z0-9_.:-]+$
        tool:
          type: string
          minLength: 1
        root_id:
          type: string
          format: uuid
        path:
          type: string
          minLength: 1
          description: Root-relative path.
        document_id:
          type: string
          format: uuid
          description: Editor document the text came from; omitted when no document served the read.
        epoch:
          type: integer
          format: int64
          minimum: 1
          description: Document epoch the anchors belong to.
        revision:
          type: integer
          format: int64
          minimum: 1
          description: Document revision the model read.
        extent:
          $ref: '#/components/schemas/AgentPresenceExtent'
        ranges:
          type: array
          maxItems: 512
          items:
            $ref: '#/components/schemas/AgentTextRange'
        stale:
          type: boolean
          description: True when text inside a range changed after the read by someone other than this session.
    AgentIntentOperation:
      type: string
      enum:
        - edit
        - write
        - create
        - delete
        - move
      description: The mutation a pending write-family tool call will perform on its target.
    AgentIntentState:
      type: string
      enum:
        - pending
        - awaiting_approval
      description: '`pending` means the host has resolved the mutation against the current document and is applying it. `awaiting_approval` means the same resolved mutation is held by an approval checkpoint.'
    AgentIntent:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - id
        - tool_call_id
        - tool
        - operation
        - state
        - root_id
        - path
        - extent
        - ranges
      description: A write-family tool call whose mutation the host has resolved but not yet landed. Ranges are the resolved targets in the current document.
      properties:
        id:
          type: string
          pattern: ^[A-Za-z0-9_.:-]+$
        worker_id:
          type: string
          pattern: ^[A-Za-z0-9_.:-]+$
          description: Worker job that made the call; omitted for the chat itself.
        tool_call_id:
          type: string
          pattern: ^[A-Za-z0-9_.:-]+$
        tool:
          type: string
          minLength: 1
        operation:
          $ref: '#/components/schemas/AgentIntentOperation'
        state:
          $ref: '#/components/schemas/AgentIntentState'
        checkpoint_id:
          type: string
          format: uuid
          description: Approval checkpoint holding the mutation; present only while awaiting approval.
        root_id:
          type: string
          format: uuid
        path:
          type: string
          minLength: 1
          description: Root-relative target path.
        to_path:
          type: string
          description: Root-relative destination for move and copy.
        document_id:
          type: string
          format: uuid
        epoch:
          type: integer
          format: int64
          minimum: 1
        extent:
          $ref: '#/components/schemas/AgentPresenceExtent'
        ranges:
          type: array
          maxItems: 512
          items:
            $ref: '#/components/schemas/AgentTextRange'
    AgentWorkerDraftState:
      type: string
      enum:
        - reserved
        - drafting
        - ready
        - landing
      description: Where a worker's change to a primary file stands. `reserved` is a declared path reservation with no draft yet; `drafting` means the worker has written the file in its private branch; `ready` means the job completed and its draft waits to land; `landing` means the coordinator is promoting it.
    AgentWorkerDraft:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - worker_id
        - state
        - root_id
        - path
        - extent
        - ranges
      description: A worker job's pending change to one primary-tree file. Ranges are the changed lines of the current document and appear once the draft is ready.
      properties:
        worker_id:
          type: string
          pattern: ^[A-Za-z0-9_.:-]+$
        state:
          $ref: '#/components/schemas/AgentWorkerDraftState'
        root_id:
          type: string
          format: uuid
        path:
          type: string
          minLength: 1
          description: Root-relative path.
        document_id:
          type: string
          format: uuid
        epoch:
          type: integer
          format: int64
          minimum: 1
        insertions:
          type: integer
          minimum: 0
          x-go-pointer: true
        deletions:
          type: integer
          minimum: 0
          x-go-pointer: true
        extent:
          $ref: '#/components/schemas/AgentPresenceExtent'
        ranges:
          type: array
          maxItems: 512
          items:
            $ref: '#/components/schemas/AgentTextRange'
    AgentSessionPresence:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - session_id
        - turn
        - activities
        - reads
        - intents
        - worker_drafts
      description: Everything one chat is doing in the project's files right now, including the workers it dispatched. A chat is its root session; worker child sessions fold into it and are identified per item by worker_id. Every list empty means the chat has no presence.
      properties:
        session_id:
          type: string
          format: uuid
          description: Root chat session.
        title:
          type: string
          description: Chat title; omitted until set.
        turn:
          type: integer
          minimum: 0
          description: User-turn ordinal of the chat the activities, reads, and intents belong to.
        activities:
          type: array
          maxItems: 64
          items:
            $ref: '#/components/schemas/AgentActivity'
        reads:
          type: array
          maxItems: 256
          items:
            $ref: '#/components/schemas/AgentRead'
        intents:
          type: array
          maxItems: 64
          items:
            $ref: '#/components/schemas/AgentIntent'
        worker_drafts:
          type: array
          maxItems: 256
          items:
            $ref: '#/components/schemas/AgentWorkerDraft'
    AgentPresenceSnapshot:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - project_id
        - revision
        - sessions
      description: The host's ephemeral view of agent sessions in a project's files. It is held in memory, bounded per session, and empty after the host restarts.
      properties:
        project_id:
          type: string
          format: uuid
        revision:
          type: integer
          format: int64
          minimum: 0
          description: Project presence revision; events with a revision at or below it are already reflected.
        sessions:
          type: array
          items:
            $ref: '#/components/schemas/AgentSessionPresence'
    RetainedReplica:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - document_id
        - epoch
        - state_vector
        - base_sha256
      description: State the client already holds, from a live replica or from the synchronized checkpoint it preserved. When it names the current document and epoch, crdt_update carries only the state missing from state_vector and base_content is omitted for a matching base_sha256 (a client without the saved text sends an empty hash, which never matches); otherwise the response is a full snapshot.
      properties:
        document_id:
          type: string
          format: uuid
        epoch:
          type: integer
          format: int64
          minimum: 1
        state_vector:
          type: string
          format: byte
          x-go-type: '[]byte'
        base_sha256:
          type: string
    OpenEditorDocumentRequest:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - path
        - root_id
        - client_id
      properties:
        path:
          type: string
          minLength: 1
          description: Root-relative, slash-separated file path.
        root_id:
          type: string
          format: uuid
          description: Attached root that contains the file.
        client_id:
          type: string
          minLength: 1
          pattern: ^[a-zA-Z0-9:._-]+$
          description: Stable id for the client view opening the document.
        decode_as:
          type: string
          enum:
            - utf-16le
            - utf-16be
          description: Explicit decoding for BOM-less UTF-16 text.
        replica:
          x-go-pointer: true
          $ref: '#/components/schemas/RetainedReplica'
    EditorCommandHistory:
      x-go-generate: true
      type: object
      additionalProperties: false
      description: Exact text transition of an explicit editor command, replayable independently of later shared edits.
      required:
        - operation_id
        - epoch
        - before_update
        - update
      properties:
        operation_id:
          type: string
          format: uuid
        epoch:
          type: integer
          format: int64
          minimum: 1
        before_update:
          type: string
          format: byte
          x-go-type: '[]byte'
        update:
          type: string
          format: byte
          x-go-type: '[]byte'
    EditorPresenceRange:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - anchor
        - head
      properties:
        anchor:
          type: string
          format: byte
          x-go-type: '[]byte'
          maxLength: 1024
        head:
          type: string
          format: byte
          x-go-type: '[]byte'
          maxLength: 1024
    EditorParticipant:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - client_id
        - person_id
        - ranges
        - main
      properties:
        client_id:
          type: string
          pattern: ^[a-zA-Z0-9:._-]+$
        person_id:
          type: string
          format: uuid
          description: Person working in this client. A client belongs to one person.
        window_number:
          type: integer
          minimum: 1
          description: Host-assigned fallback window number, stable across documents and reconnects for this host lifetime. Desktop presentation uses its native window registry.
        ranges:
          type: array
          maxItems: 256
          items:
            $ref: '#/components/schemas/EditorPresenceRange'
        main:
          type: integer
          minimum: 0
          maximum: 255
    SecretScreenStatus:
      type: string
      description: Lifecycle of the editor highlighting pass. pending means the host is screening in the background, complete accompanies secret_screen, and unavailable means no editor screener is configured. This status never gates reading or editing document content.
      enum:
        - pending
        - complete
        - unavailable
    EditorDocument:
      x-go-generate: true
      type: object
      additionalProperties: false
      description: Accepted collaborative document for a stable logical file in the project branch.
      required:
        - id
        - project_id
        - workspace_id
        - file_id
        - root_id
        - path
        - base_sha256
        - encoding
        - size_bytes
        - eol
        - base_eol
        - mixed_eol
        - base_mixed_eol
        - revision
        - dirty
        - diverged
        - secret_screen_status
        - epoch
        - state_vector
        - crdt_update
        - published_revision
        - participants
      properties:
        save_revision:
          type: integer
          format: int64
          minimum: 1
          description: Original revision reserved for this save operation when its publication already has a durable journal. The document itself is the current accepted state.
        command_history:
          x-go-pointer: true
          $ref: '#/components/schemas/EditorCommandHistory'
        epoch:
          type: integer
          format: int64
          minimum: 1
        state_vector:
          type: string
          format: byte
          x-go-type: '[]byte'
        crdt_update:
          type: string
          format: byte
          x-go-type: '[]byte'
          x-go-name: CRDTUpdate
        replica_id:
          type: integer
          format: int64
          x-go-type: uint32
        published_revision:
          type: integer
          format: int64
          minimum: 1
        accepted_revision:
          type: integer
          format: int64
          minimum: 1
        accepted_operation_id:
          type: string
          format: uuid
        participants:
          type: array
          items:
            $ref: '#/components/schemas/EditorParticipant'
        id:
          type: string
          format: uuid
        project_id:
          type: string
          format: uuid
        workspace_id:
          type: string
          pattern: ^ws_[0-9a-f]{32}$
        file_id:
          type: string
          format: uuid
        root_id:
          type: string
          format: uuid
        path:
          type: string
        base_content:
          x-go-pointer: true
          type: string
          description: Normalized saved base; omitted when byte-identical to the accepted text, which crdt_update carries with line endings normalized to LF.
        base_sha256:
          type: string
        encoding:
          $ref: '#/components/schemas/SourceEncoding'
        size_bytes:
          type: integer
          format: int64
          minimum: 0
        eol:
          type: string
          enum:
            - lf
            - crlf
        base_eol:
          type: string
          enum:
            - lf
            - crlf
        mixed_eol:
          type: boolean
        base_mixed_eol:
          type: boolean
        revision:
          type: integer
          format: int64
          minimum: 1
        dirty:
          type: boolean
        diverged:
          type: boolean
        held_agent_version_id:
          x-go-pointer: true
          type: string
          format: uuid
          description: The retained state holding an agent edit this draft carries and disk does not, so reload and discard can say it is recoverable. Omitted once a save settles the hold.
        secret_screen_status:
          $ref: '#/components/schemas/SecretScreenStatus'
        secret_screen:
          x-go-pointer: true
          $ref: '#/components/schemas/SecretScreen'
    SecretIgnoreRule:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - id
        - value
        - reason
        - root_id
        - path
        - status
      properties:
        id:
          type: string
          pattern: ^[A-Za-z0-9_.:-]+$
        value:
          type: string
        reason:
          type: string
        expires_on:
          type: string
          format: date
        root_id:
          type: string
          format: uuid
        path:
          type: string
        status:
          type: string
          enum:
            - active
            - expired
            - protected
            - untrusted
            - invalid
    SecretIgnoreList:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - rules
      properties:
        rules:
          type: array
          items:
            $ref: '#/components/schemas/SecretIgnoreRule'
    SecretIgnoreEntry:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - id
        - value
        - reason
      properties:
        id:
          type: string
          format: uuid
        value:
          type: string
          minLength: 1
          maxLength: 65536
        reason:
          type: string
          minLength: 1
        expires_on:
          type: string
          format: date
    AddSecretIgnoreRequest:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - root_id
        - entry
      properties:
        root_id:
          type: string
          format: uuid
        entry:
          $ref: '#/components/schemas/SecretIgnoreEntry'
    SecretIgnoreCandidate:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - value
      properties:
        value:
          type: string
    ReplaceEditorDocumentRetentionRequest:
      type: object
      x-go-generate: true
      additionalProperties: false
      required:
        - client_id
        - retained_clients
        - document_ids
      properties:
        client_id:
          type: string
          minLength: 1
          maxLength: 256
          pattern: ^[a-zA-Z0-9:._-]+$
        retained_clients:
          type: array
          maxItems: 16384
          uniqueItems: true
          description: Live client identities from the native window inventory or browser window locks. Must include client_id. Only orphan references in the calling client’s window or browser namespace are swept; omit until the inventory is ready.
          items:
            type: string
            minLength: 1
            maxLength: 256
            pattern: ^[a-zA-Z0-9:._-]+$
        document_ids:
          type: array
          maxItems: 16384
          uniqueItems: true
          items:
            type: string
            format: uuid
    EditorDocumentStatusesRequest:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - document_ids
      properties:
        document_ids:
          type: array
          minItems: 1
          maxItems: 64
          uniqueItems: true
          items:
            type: string
            format: uuid
    EditorDocumentStatus:
      x-go-generate: true
      type: object
      additionalProperties: false
      description: Accepted document metadata without text, replica admission, or filesystem reconciliation.
      required:
        - id
        - file_id
        - root_id
        - path
        - branch_id
        - revision
        - epoch
        - published_revision
        - dirty
        - diverged
        - base_sha256
        - size_bytes
        - encoding
        - eol
        - base_eol
        - mixed_eol
        - base_mixed_eol
      properties:
        id:
          type: string
          format: uuid
        file_id:
          type: string
          format: uuid
        root_id:
          type: string
          format: uuid
        path:
          type: string
        branch_id:
          type: string
          pattern: ^[a-zA-Z0-9:._-]*$
        revision:
          type: integer
          format: int64
          minimum: 1
        epoch:
          type: integer
          format: int64
          minimum: 1
        published_revision:
          type: integer
          format: int64
          minimum: 1
        dirty:
          type: boolean
        diverged:
          type: boolean
        base_sha256:
          type: string
        size_bytes:
          type: integer
          format: int64
          minimum: 0
        encoding:
          $ref: '#/components/schemas/SourceEncoding'
        eol:
          type: string
          enum:
            - lf
            - crlf
        base_eol:
          type: string
          enum:
            - lf
            - crlf
        mixed_eol:
          type: boolean
        base_mixed_eol:
          type: boolean
        held_agent_version_id:
          type: string
          format: uuid
    EditorDocumentStatuses:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - documents
        - missing
      properties:
        documents:
          type: array
          maxItems: 64
          items:
            $ref: '#/components/schemas/EditorDocumentStatus'
        missing:
          type: array
          maxItems: 64
          items:
            type: string
            format: uuid
    ReplaceEditorDocumentRequest:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - client_id
        - operation_id
        - expected_revision
        - content
        - eol
        - mixed_eol
        - history_vector
        - session_id
        - turn
      properties:
        history_vector:
          type:
            - string
            - 'null'
          format: byte
          x-go-type: '[]byte'
          description: Invoking replica's state vector, which requests an exact command transition for its document undo history; null records no transition.
        session_id:
          type:
            - string
            - 'null'
          format: uuid
          description: Focused chat when this change was authored; null when unaffiliated.
        turn:
          type: integer
          minimum: 0
          description: Observed chat turn when authored; zero when no turn was observed.
        operation_id:
          type: string
          format: uuid
        client_id:
          type: string
          minLength: 1
          pattern: ^[a-zA-Z0-9:._-]+$
          description: Invoking editor client.
        expected_revision:
          type: integer
          format: int64
          minimum: 1
          description: Document revision the update was authored against.
        content:
          type: string
          description: Replacement draft text; CRLF is normalized to LF. Decoded text must fit 8 MiB and the file serialized with the document encoding and requested line endings must fit 4 MiB. Invalid or oversize content returns 400 invalid_request without changing the draft or revision. The JSON envelope permits escaping of the full supported content.
        eol:
          type: string
          enum:
            - lf
            - crlf
          description: Line ending to use on save.
        mixed_eol:
          type: boolean
          description: Whether the draft represents mixed line endings.
    EditorDocumentChange:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - revision
        - epoch
        - operation_id
        - actor_kind
        - person_id
        - client_id
        - reverted_by
        - created_at
        - session_id
        - session_title
        - turn
      properties:
        session_id:
          type: string
          format: uuid
        session_title:
          type: string
        turn:
          type: integer
        revision:
          type: integer
          x-go-type: int64
        epoch:
          type: integer
          x-go-type: int64
        operation_id:
          type: string
          format: uuid
        actor_kind:
          type: string
          enum:
            - user
            - agent
            - restore
        person_id:
          type: string
          format: uuid
          description: Person who made a user or restore change; empty for agent changes.
        client_id:
          type: string
          pattern: ^[a-zA-Z0-9:._-]+$
          description: Client the person used; empty for agent changes.
        reverted_by:
          type: string
        created_at:
          type: string
          format: date-time
    EditorDocumentChanges:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - changes
      properties:
        changes:
          type: array
          items:
            $ref: '#/components/schemas/EditorDocumentChange'
        next_cursor:
          type: string
    RevertEditorDocumentChangeRequest:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - client_id
        - operation_id
        - epoch
      properties:
        history_vector:
          type: string
          format: byte
          x-go-type: '[]byte'
          description: Invoking replica's state vector. Requests an exact command transition for its document undo history.
        session_id:
          type: string
          format: uuid
          description: Focused chat when this change was authored; omitted when unaffiliated.
        turn:
          type: integer
          minimum: 0
          description: Observed chat turn when authored; zero when no turn was observed.
        client_id:
          type: string
          minLength: 1
          pattern: ^[a-zA-Z0-9:._-]+$
        operation_id:
          type: string
          format: uuid
        epoch:
          type: integer
          minimum: 1
          x-go-type: int64
    PinEditorDocumentRequest:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - client_id
      properties:
        client_id:
          type: string
          minLength: 1
          pattern: ^[a-zA-Z0-9:._-]+$
        operation_id:
          type: string
          format: uuid
          description: Reserves this snapshot for the save with this operation identity. Omit for a bounded read snapshot.
    ResolveEditorDocumentRequest:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - client_id
        - operation_id
        - expected_revision
        - disk_sha256
        - content
        - eol
      properties:
        history_vector:
          type: string
          format: byte
          x-go-type: '[]byte'
          description: Invoking replica's state vector. Requests an exact command transition for its document undo history.
        session_id:
          type: string
          format: uuid
          description: Focused chat when this change was authored; omitted when unaffiliated.
        turn:
          type: integer
          minimum: 0
          description: Observed chat turn when authored; zero when no turn was observed.
        client_id:
          type: string
          minLength: 1
          pattern: ^[a-zA-Z0-9:._-]+$
        operation_id:
          type: string
          format: uuid
        expected_revision:
          type: integer
          format: int64
          minimum: 1
        disk_sha256:
          type: string
          minLength: 64
          maxLength: 64
        content:
          type: string
        eol:
          type: string
          enum:
            - lf
            - crlf
    SyncEditorDocumentRequest:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - client_id
        - incarnation
        - epoch
      properties:
        client_id:
          type: string
          minLength: 1
          pattern: ^[a-zA-Z0-9:._-]+$
        incarnation:
          type: string
          format: uuid
        epoch:
          type: integer
          format: int64
          minimum: 1
        base_sha256:
          type: string
        state_vector:
          type: string
          format: byte
          x-go-type: '[]byte'
    EditorReplicaFrame:
      x-go-generate: true
      type: object
      additionalProperties: false
      description: Differential accepted replica state. Text is carried by crdt_update; base_content is included only when the requested base_sha256 differs.
      required:
        - id
        - project_id
        - workspace_id
        - file_id
        - root_id
        - path
        - base_sha256
        - encoding
        - size_bytes
        - eol
        - base_eol
        - mixed_eol
        - base_mixed_eol
        - revision
        - dirty
        - diverged
        - secret_screen_status
        - epoch
        - state_vector
        - crdt_update
        - published_revision
        - participants
      properties:
        epoch:
          type: integer
          format: int64
          minimum: 1
        state_vector:
          type: string
          format: byte
          x-go-type: '[]byte'
        crdt_update:
          type: string
          format: byte
          x-go-type: '[]byte'
          x-go-name: CRDTUpdate
        replica_id:
          type: integer
          format: int64
          x-go-type: uint32
        published_revision:
          type: integer
          format: int64
          minimum: 1
        accepted_revision:
          type: integer
          format: int64
          minimum: 1
        accepted_operation_id:
          type: string
          format: uuid
        participants:
          type: array
          items:
            $ref: '#/components/schemas/EditorParticipant'
        id:
          type: string
          format: uuid
        project_id:
          type: string
          format: uuid
        workspace_id:
          type: string
          pattern: ^ws_[0-9a-f]{32}$
        file_id:
          type: string
          format: uuid
        root_id:
          type: string
          format: uuid
        path:
          type: string
        base_content:
          x-go-pointer: true
          type: string
          description: Normalized saved base; omitted when the request already has this base_sha256.
        base_sha256:
          type: string
        encoding:
          $ref: '#/components/schemas/SourceEncoding'
        size_bytes:
          type: integer
          format: int64
          minimum: 0
        eol:
          type: string
          enum:
            - lf
            - crlf
        base_eol:
          type: string
          enum:
            - lf
            - crlf
        mixed_eol:
          type: boolean
        base_mixed_eol:
          type: boolean
        revision:
          type: integer
          format: int64
          minimum: 1
        dirty:
          type: boolean
        diverged:
          type: boolean
        held_agent_version_id:
          x-go-pointer: true
          type: string
          format: uuid
          description: The retained state holding an agent edit this draft carries and disk does not, so reload and discard can say it is recoverable. Omitted once a save settles the hold.
        secret_screen_status:
          $ref: '#/components/schemas/SecretScreenStatus'
        secret_screen:
          x-go-pointer: true
          $ref: '#/components/schemas/SecretScreen'
    SubmitEditorDocumentUpdateRequest:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - client_id
        - replica_id
        - epoch
        - operation_id
        - update
      properties:
        session_id:
          type: string
          format: uuid
          description: Focused chat when this change was authored; omitted when unaffiliated.
        turn:
          type: integer
          minimum: 0
          description: Observed chat turn when authored; zero when no turn was observed.
        client_id:
          type: string
          minLength: 1
          pattern: ^[a-zA-Z0-9:._-]+$
        replica_id:
          type: integer
          format: int64
          x-go-type: uint32
          minimum: 1
          maximum: 4294967295
        epoch:
          type: integer
          format: int64
          minimum: 1
        operation_id:
          type: string
          format: uuid
        update:
          type: string
          format: byte
          x-go-type: '[]byte'
        base_sha256:
          type: string
        state_vector:
          type: string
          format: byte
          x-go-type: '[]byte'
    LeaveEditorDocumentRequest:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - client_id
        - incarnation
      properties:
        client_id:
          type: string
          minLength: 1
          pattern: ^[a-zA-Z0-9:._-]+$
        incarnation:
          type: string
          format: uuid
    UpdateEditorDocumentPresenceRequest:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - client_id
        - incarnation
        - ranges
        - main
      properties:
        client_id:
          type: string
          minLength: 1
          pattern: ^[a-zA-Z0-9:._-]+$
        incarnation:
          type: string
          format: uuid
        ranges:
          type: array
          maxItems: 256
          items:
            $ref: '#/components/schemas/EditorPresenceRange'
        main:
          type: integer
          minimum: 0
          maximum: 255
    EditorDocumentCommandRequest:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - client_id
        - operation_id
        - expected_revision
      properties:
        history_vector:
          type: string
          format: byte
          x-go-type: '[]byte'
          description: Invoking replica's state vector. Requests an exact command transition for its document undo history.
        session_id:
          type: string
          format: uuid
          description: Focused chat when this change was authored; omitted when unaffiliated.
        turn:
          type: integer
          minimum: 0
          description: Observed chat turn when authored; zero when no turn was observed.
        operation_id:
          type: string
          format: uuid
        client_id:
          type: string
          minLength: 1
          pattern: ^[a-zA-Z0-9:._-]+$
          description: Invoking editor client.
        expected_revision:
          type: integer
          format: int64
          minimum: 1
          description: Document revision the command was authored against.
    ObserveEditorDocumentRequest:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - client_id
      properties:
        client_id:
          type: string
          minLength: 1
          pattern: ^[a-zA-Z0-9:._-]+$
    SaveEditorDocumentRequest:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - client_id
        - expected_revision
        - operation_id
      properties:
        session_id:
          type: string
          format: uuid
          description: Focused chat when this change was authored; omitted when unaffiliated.
        turn:
          type: integer
          minimum: 0
          description: Observed chat turn when authored; zero when no turn was observed.
        client_id:
          type: string
          minLength: 1
          pattern: ^[a-zA-Z0-9:._-]+$
          description: Invoking editor client.
        expected_revision:
          type: integer
          format: int64
          minimum: 1
          description: Document revision the command was authored against.
        operation_id:
          type: string
          format: uuid
          description: Stable mutation identity; an exact retry returns the original saved document and reusing this id with different save input returns 409 idempotency_conflict.
    SecretMarkRange:
      x-go-generate: true
      type: object
      description: 'A rune range in one editor document. The client sends offsets, never bytes: the host slices the value from its own copy of the draft, so a credential never travels in a request body, an access log, or an error path.'
      required:
        - revision
        - start
        - end
      additionalProperties: false
      properties:
        revision:
          type: integer
          format: int64
          description: Document revision the range was selected against. A mismatch is refused.
        start:
          type: integer
          minimum: 0
        end:
          type: integer
          minimum: 0
        trim:
          x-go-pointer: true
          type: boolean
          description: 'Strip surrounding whitespace and one matched quote layer before capturing. Omitted means trim: the quote around a credential is syntax in the file, not part of the value that travels.'
    SecretMarkPreview:
      x-go-generate: true
      type: object
      description: What marking a range would capture. The host decides eligibility so the client never has to guess what will be refused after a person has already named the capability.
      required:
        - eligible
        - start
        - end
        - rune_length
        - byte_length
      additionalProperties: false
      properties:
        eligible:
          type: boolean
        reason:
          type: string
          enum:
            - out_of_range
            - empty
            - too_large
            - too_short
            - already_protected
          description: 'Present only when eligible is false. too_short is the mint floor, set at the shortest length a real credential takes so a PIN is accepted: only one-to-three character selections are refused, because those occur constantly in ordinary text and an exact match would redact unrelated prose rather than protect anything. already_protected means the selection holds bytes a managed secret in this project already protects; a second capability would duplicate that secret rather than protect anything new.'
        start:
          type: integer
          minimum: 0
        end:
          type: integer
          minimum: 0
        rune_length:
          type: integer
          minimum: 0
        byte_length:
          type: integer
          minimum: 0
        shape:
          type: string
          description: Synthetic character classes and run lengths for the captured bytes.
        trimmed_leading:
          type: integer
          minimum: 0
        trimmed_trailing:
          type: integer
          minimum: 0
    SecretMarkRequest:
      x-go-generate: true
      type: object
      description: Mint a project-scoped managed secret from bytes already present in a project file.
      required:
        - range
        - name
        - purpose
      additionalProperties: false
      properties:
        range:
          $ref: '#/components/schemas/SecretMarkRange'
        name:
          type: string
          minLength: 1
          maxLength: 80
        purpose:
          type: string
          minLength: 1
          maxLength: 240
        operation_id:
          type: string
          format: uuid
          description: Idempotency key. Repeating a mark with the same key returns the same capability.
    AttachmentVideoFacts:
      x-go-generate: true
      type: object
      description: |
        What the host's decoder read from a video at upload. Present only on video receipts; a video the decoder cannot play is refused instead.
      required:
        - duration_ms
        - width
        - height
      additionalProperties: false
      properties:
        duration_ms:
          type: integer
          minimum: 0
        width:
          type: integer
          minimum: 1
        height:
          type: integer
          minimum: 1
    SourceEditorConfig:
      x-go-generate: true
      type: object
      required:
        - path
        - root_id
      additionalProperties: false
      description: |
        EditorConfig pairs in effect for one root-addressed path. Every property
        is omitted when no `.editorconfig` declares it.
      properties:
        path:
          type: string
          description: Normalized repo-relative path (slash-separated)
        root_id:
          type: string
          format: uuid
          description: Attached root that holds the path
        indent_style:
          type: string
          enum:
            - space
            - tab
        indent_size:
          type: integer
          minimum: 1
          maximum: 64
          description: Columns per indentation level; omitted when indent_size_tab is true.
        indent_size_tab:
          type: boolean
          description: True when `indent_size = tab`, so a level follows tab_width.
        tab_width:
          type: integer
          minimum: 1
          maximum: 10000
        end_of_line:
          type: string
          enum:
            - lf
            - crlf
        trim_trailing_whitespace:
          x-go-pointer: true
          type: boolean
        insert_final_newline:
          x-go-pointer: true
          type: boolean
        charset:
          type: string
          enum:
            - latin1
            - utf-8
            - utf-8-bom
            - utf-16be
            - utf-16le
    MakeProjectSourceEditableRequest:
      x-go-generate: true
      type: object
      required:
        - path
        - root_id
        - base_sha256
      additionalProperties: false
      description: Human request to add owner-write permission to the exact file bytes shown in the editor.
      properties:
        path:
          type: string
          minLength: 1
          description: Root-relative path of the existing regular file.
        root_id:
          type: string
          minLength: 1
          format: uuid
          description: Attached root that contains the file.
        base_sha256:
          type: string
          minLength: 1
          description: SHA-256 from the matching source read; stale bytes are refused with 409 source_write_conflict.
    MakeProjectSourceEditableResponse:
      x-go-generate: true
      type: object
      required:
        - path
        - root_id
        - previous_mode
        - mode
        - writable
      additionalProperties: false
      description: Completed owner-write permission transition.
      properties:
        path:
          type: string
        root_id:
          type: string
          format: uuid
        previous_mode:
          x-go-type: uint32
          type: integer
          minimum: 0
          maximum: 511
          description: Permission bits before the transition.
        mode:
          x-go-type: uint32
          type: integer
          minimum: 0
          maximum: 511
          description: Permission bits after adding owner-write.
        writable:
          type: boolean
          description: True when the resulting mode grants owner-write.
    SourceDirEntry:
      x-go-generate: true
      type: object
      required:
        - name
        - is_dir
      additionalProperties: false
      description: One immediate child of a browsed directory.
      properties:
        name:
          type: string
        is_dir:
          type: boolean
          description: True for directories (symlinks classify by their target)
    SourceDirListing:
      x-go-generate: true
      type: object
      required:
        - workspace_id
        - root_id
        - dir
        - watch_complete
        - entries
      additionalProperties: false
      description: |
        One directory level under an attached root for the Files tree.
        Entries are sorted folders first, then case-insensitive by name.
      properties:
        workspace_id:
          type: string
          pattern: ^ws_[0-9a-f]{32}$
          description: Physical root-set identity used to produce this listing.
        root_id:
          type: string
          format: uuid
        dir:
          type: string
          description: Normalized root-relative directory; "." is the root folder
        watch_complete:
          type: boolean
          description: Whether host filesystem observation covers this directory. A truncated watch covers some directories and not others, so this is answered per listing rather than per root. False tells visible clients to revalidate this directory on a bounded timer.
        entries:
          type: array
          items:
            $ref: '#/components/schemas/SourceDirEntry'
    SourceHistoryAction:
      x-go-generate: true
      type: object
      required:
        - id
        - label
        - kind
        - root_id
        - path
        - is_dir
        - removes_path
      additionalProperties: false
      description: The host-owned lifecycle operation currently available in one history direction.
      properties:
        id:
          type: string
          format: uuid
        label:
          type: string
          description: Sentence-case action label suitable for a button tooltip or menu row.
        kind:
          type: string
          enum:
            - create
            - rename
            - move
            - copy
            - trash
            - delete
          description: trash is a Files deletion that moved the entry to the system Trash; delete is an agent deletion that removed it from disk. Both keep a complete recovery copy.
        root_id:
          type: string
          format: uuid
        path:
          type: string
          description: Root-relative path the history action will create, remove, or move.
        from_path:
          type: string
        to_path:
          type: string
        is_dir:
          type: boolean
        removes_path:
          type: boolean
          description: True when the action removes or retargets the current project path and dirty editor state must be resolved first.
    SourceHistoryState:
      x-go-generate: true
      type: object
      required:
        - undo
        - redo
      additionalProperties: false
      description: Current host-authoritative lifecycle history heads for one project.
      properties:
        undo:
          x-go-type: '*SourceHistoryAction'
          oneOf:
            - $ref: '#/components/schemas/SourceHistoryAction'
            - type: 'null'
        redo:
          x-go-type: '*SourceHistoryAction'
          oneOf:
            - $ref: '#/components/schemas/SourceHistoryAction'
            - type: 'null'
    SourceHistoryMutationRequest:
      x-go-generate: true
      type: object
      required:
        - operation_id
        - expected_entry_id
      additionalProperties: false
      properties:
        operation_id:
          type: string
          format: uuid
          description: Stable mutation identity; exact retries return the original result.
        expected_entry_id:
          type: string
          format: uuid
          description: Exact undo or redo head displayed to the user; a moved head returns 409.
    RenameProjectSourceRequest:
      x-go-generate: true
      type: object
      required:
        - operation_id
        - from
        - to
      additionalProperties: false
      description: |
        Same-root rename or move of one file or folder. Missing parent folders
        on to are created. Cross-root moves are refused.
      properties:
        operation_id:
          type: string
          format: uuid
          description: Stable mutation identity; exact retries return the original result and conflicting reuse returns 409 idempotency_conflict.
        root_id:
          type: string
          format: uuid
          description: Attached root both paths must stay under; omitted uses the primary root
        from:
          type: string
          minLength: 1
          description: Existing root-relative path to rename or move
        to:
          type: string
          minLength: 1
          description: Destination root-relative path in the same root; must not already exist
    CopyProjectSourceRequest:
      x-go-generate: true
      type: object
      required:
        - operation_id
        - from
        - to
      additionalProperties: false
      description: |
        Same-root copy of one file or folder (recursive). Modes are preserved.
        An occupied destination is refused.
      properties:
        operation_id:
          type: string
          format: uuid
          description: Stable mutation identity; exact retries return the original result and conflicting reuse returns 409 idempotency_conflict.
        root_id:
          type: string
          format: uuid
          description: Attached root both paths must stay under; omitted uses the primary root
        from:
          type: string
          minLength: 1
          description: Existing root-relative path to copy
        to:
          type: string
          minLength: 1
          description: Destination root-relative path in the same root; must not already exist
    SourceIndexState:
      type: string
      description: Readability of the selected roots. Ready means at least one root is readable, not that discovery is exhaustive; inspect per-root coverage.
      enum:
        - warming
        - ready
        - failed
    SourceIndexRootCoverage:
      x-go-generate: true
      type: object
      required:
        - root_id
        - state
        - discovery_complete
        - refreshing
        - bounded_directories
        - failed_directories
      additionalProperties: false
      description: Readability and coverage are independent; completed discovery can still have omissions.
      properties:
        root_id:
          type: string
          format: uuid
        state:
          $ref: '#/components/schemas/SourceIndexState'
        discovery_complete:
          type: boolean
        refreshing:
          type: boolean
        bounded_directories:
          type: integer
          minimum: 0
        failed_directories:
          type: integer
          minimum: 0
        error:
          type: string
    SourceIndexResource:
      x-go-generate: true
      type: object
      required:
        - kind
        - path
      additionalProperties: false
      properties:
        kind:
          type: string
          enum:
            - readme
            - agents
            - contributing
        path:
          type: string
    SourceIndexRootSummary:
      x-go-generate: true
      type: object
      required:
        - root_id
        - file_count
        - resources
      additionalProperties: false
      properties:
        root_id:
          type: string
          format: uuid
        file_count:
          type: integer
          minimum: 0
        resources:
          type: array
          items:
            $ref: '#/components/schemas/SourceIndexResource'
    SourceIndexSummary:
      x-go-generate: true
      type: object
      required:
        - state
        - revision
        - refreshing
        - file_count
        - roots
        - coverage
      additionalProperties: false
      description: Counts and resources from readable generations, with explicit omissions and pending work for every selected root.
      properties:
        state:
          $ref: '#/components/schemas/SourceIndexState'
        revision:
          x-go-type: uint64
          type: integer
          format: uint64
        refreshing:
          type: boolean
        coverage:
          type: array
          items:
            $ref: '#/components/schemas/SourceIndexRootCoverage'
        retry_after_ms:
          type: integer
          minimum: 1
          description: Milliseconds until the next poll while results are pending; increases with elapsed work time.
        file_count:
          type: integer
          minimum: 0
        roots:
          type: array
          items:
            $ref: '#/components/schemas/SourceIndexRootSummary'
    SourceSearchHighlight:
      x-go-generate: true
      type: object
      required:
        - start
        - end
      additionalProperties: false
      description: A half-open range of Unicode code point offsets into a matched path or name.
      properties:
        start:
          type: integer
          minimum: 0
        end:
          type: integer
          minimum: 1
    SourceSearchMatch:
      x-go-generate: true
      type: object
      required:
        - root_id
        - path
        - highlights
      additionalProperties: false
      properties:
        root_id:
          type: string
          format: uuid
        path:
          type: string
        highlights:
          type: array
          description: Ascending, disjoint parts of path the query matched. Empty when the query matched only the root folder's path or was empty.
          items:
            $ref: '#/components/schemas/SourceSearchHighlight'
    SourceSearchLocation:
      x-go-generate: true
      type: object
      required:
        - line
      additionalProperties: false
      description: The one-based line and column, or inclusive line range, a location on the query named. Opening a match goes there.
      properties:
        line:
          type: integer
          minimum: 1
        column:
          type: integer
          minimum: 1
        end_line:
          type: integer
          minimum: 1
    SourceSearchOutside:
      x-go-generate: true
      type: object
      required:
        - path
        - kind
      additionalProperties: false
      description: An absolute query that exists on this host outside every selected root.
      properties:
        path:
          type: string
          description: The path in the host's own spelling.
        kind:
          type: string
          enum:
            - file
            - directory
    SourceSearchResponse:
      x-go-generate: true
      type: object
      required:
        - state
        - revision
        - refreshing
        - matches
        - coverage
      additionalProperties: false
      description: Available file matches, including while other selected roots are preparing or failed. Refresh while refreshing is true; inspect coverage before treating absence as exhaustive.
      properties:
        state:
          $ref: '#/components/schemas/SourceIndexState'
        revision:
          x-go-type: uint64
          type: integer
          format: uint64
        refreshing:
          type: boolean
        coverage:
          type: array
          items:
            $ref: '#/components/schemas/SourceIndexRootCoverage'
        retry_after_ms:
          type: integer
          minimum: 1
          description: Milliseconds until the next poll while results are pending; increases with elapsed work time.
        matches:
          type: array
          description: Files in rank order. Clients render this order as-is.
          items:
            $ref: '#/components/schemas/SourceSearchMatch'
        location:
          x-go-pointer: true
          $ref: '#/components/schemas/SourceSearchLocation'
        outside:
          x-go-pointer: true
          $ref: '#/components/schemas/SourceSearchOutside'
        next_cursor:
          type: string
    SourceDefinitionRequest:
      x-go-generate: true
      type: object
      required:
        - path
        - symbol
      additionalProperties: false
      description: |
        Resolve declarations for one identifier under the active root. Den
        sends the word under the caret; the host narrows whole-word search
        hits to AST-confirmed declarations. Empty symbol is a client no-op.
      properties:
        root_id:
          type: string
          format: uuid
          description: Pin resolution to one attached root (active root)
        path:
          type: string
          description: Root-relative path of the buffer that issued the request
        symbol:
          type: string
          description: Identifier under the caret (client word grab)
        line:
          type: integer
          minimum: 1
          description: 1-based caret line (ranking origin; optional)
    SourceSymbolKind:
      type: string
      enum:
        - function
        - method
        - type
        - class
        - constant
        - heading
    SourceDefinitionCandidate:
      x-go-generate: true
      type: object
      required:
        - root_id
        - path
        - line
        - kind
        - snippet
      additionalProperties: false
      description: One AST-confirmed declaration. Mentions never appear here.
      properties:
        root_id:
          type: string
          format: uuid
        path:
          type: string
          description: Root-relative path of the declaring file
        line:
          type: integer
          minimum: 1
          description: 1-based declaration line
        kind:
          $ref: '#/components/schemas/SourceSymbolKind'
        snippet:
          type: string
          description: Trimmed declaration line text
    SourceDefinitionResponse:
      x-go-generate: true
      type: object
      required:
        - candidates
        - truncated
      additionalProperties: false
      description: |
        Ranked declaration candidates for go-to-definition. Zero candidates
        is a successful answer. truncated is true when caps, incomplete discovery,
        read failures, or unavailable roots prevent an exhaustive lookup.
      properties:
        candidates:
          type: array
          items:
            $ref: '#/components/schemas/SourceDefinitionCandidate'
        truncated:
          type: boolean
          description: True whenever the lookup could not establish exhaustive coverage
    SourceSymbol:
      x-go-generate: true
      type: object
      required:
        - name
        - kind
        - line
      additionalProperties: false
      description: One declaration in document order for in-file navigation.
      properties:
        name:
          type: string
        kind:
          $ref: '#/components/schemas/SourceSymbolKind'
        line:
          type: integer
          minimum: 1
          description: 1-based line of the declaration
    SourceSymbolsResponse:
      x-go-generate: true
      type: object
      required:
        - symbols
        - truncated
        - sha256
      additionalProperties: false
      description: |
        Per-file outline for symbol navigation. Unknown languages return an
        empty list with 200 — never an error. Capped at 2 000 symbols.
      properties:
        sha256:
          type: string
          description: Editable source revision analyzed; empty when the source is not editable.
        symbols:
          type: array
          items:
            $ref: '#/components/schemas/SourceSymbol'
        truncated:
          type: boolean
          description: True when the listing stopped at the 2 000 symbol cap
    SourceWalkTurnSummary:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - message_id
        - turn
        - steps
        - items
      properties:
        message_id:
          type: string
          format: uuid
        turn:
          type: integer
          minimum: 1
          description: Permanent turn ordinal this message opened, as the turn baseline spells it.
        steps:
          type: integer
          minimum: 0
        items:
          type: integer
          minimum: 0
    SourceWalkSummary:
      x-go-generate: true
      type: object
      required:
        - turns
      additionalProperties: false
      properties:
        turns:
          type: array
          items:
            $ref: '#/components/schemas/SourceWalkTurnSummary'
    SourceGitChangeKind:
      type: string
      enum:
        - checkout
        - commit
        - amend
        - merge
        - rebase
        - pull
        - reset
        - cherry_pick
        - revert
        - clone
        - repo_appeared
        - repo_gone
        - other
        - unknown
    SourceGitChange:
      x-go-generate: true
      type: object
      required:
        - id
        - root_id
        - kind
        - ordinal
        - observed_at
      additionalProperties: false
      description: |
        One observed git ref movement, stated from git's own records (HEAD and
        the reflog) at observation time. Effects recorded by the pass that
        observed the movement reference it as their cause; a movement that
        changed no tracked file bytes — a bare commit — stands alone. The
        invoking chat, turn, and tool call are absent for an unaffiliated
        movement.
      properties:
        session_id:
          type: string
          format: uuid
          description: Invoking chat.
        turn:
          type: integer
          minimum: 0
        tool_call_id:
          type: string
          pattern: ^[a-zA-Z0-9:._-]+$
        tool_name:
          type: string
        id:
          type: string
          format: uuid
        root_id:
          type: string
          format: uuid
        kind:
          $ref: '#/components/schemas/SourceGitChangeKind'
        from_commit:
          x-go-pointer: true
          type: string
          pattern: ^[0-9a-f]{7,64}$
        to_commit:
          x-go-pointer: true
          type: string
          pattern: ^[0-9a-f]{7,64}$
        from_ref:
          x-go-pointer: true
          type: string
          description: Short branch name before the movement; omitted when detached or unknown.
        to_ref:
          x-go-pointer: true
          type: string
          description: Short branch name after the movement; omitted when detached or unknown.
        detail:
          x-go-pointer: true
          type: string
          description: Git's own subject for the movement — a commit message subject, a checkout description, or the raw reflog action for kind other.
        ordinal:
          type: integer
          format: int64
          minimum: 1
          description: Position on the project's source-history clock, shared with effects and versions.
        observed_at:
          x-go-name: ObservedAt
          type: string
          format: date-time
    SourceGitCommitDetails:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - hash
        - parents
        - message
        - author_name
        - authored_at
        - committed_at
      properties:
        hash:
          type: string
          pattern: ^[0-9a-f]{7,64}$
        parents:
          type: array
          items:
            type: string
            pattern: ^[0-9a-f]{7,64}$
        message:
          type: string
        author_name:
          type: string
        authored_at:
          x-go-name: AuthoredAt
          type: string
          format: date-time
        committed_at:
          x-go-name: CommittedAt
          type: string
          format: date-time
    SourceGitReviewFile:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - path
        - before_path
        - op
        - before_mode
        - after_mode
        - before_oid
        - after_oid
        - insertions
        - deletions
        - binary
      properties:
        path:
          type: string
          description: Root-relative path in the comparison; deleted paths retain their old address.
        before_path:
          type: string
        op:
          $ref: '#/components/schemas/SourceChangeOp'
        before_mode:
          type: string
        after_mode:
          type: string
        before_oid:
          type: string
          pattern: ^([0-9a-f]{7,64})?$
        after_oid:
          type: string
          pattern: ^([0-9a-f]{7,64})?$
        insertions:
          type: integer
          minimum: 0
        deletions:
          type: integer
          minimum: 0
        binary:
          type: boolean
    SourceGitReview:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - change
        - commit
        - commit_comparison
        - before_commit
        - after_commit
        - files
        - files_total
        - insertions
        - deletions
      properties:
        change:
          $ref: '#/components/schemas/SourceGitChange'
        commit:
          $ref: '#/components/schemas/SourceGitCommitDetails'
        commit_comparison:
          type: boolean
          description: True compares the destination commit with its selected parent; false compares the recorded movement endpoints.
        before_commit:
          type: string
          pattern: ^([0-9a-f]{7,64})?$
          description: Empty only for an initial commit compared with the empty tree.
        after_commit:
          type: string
          pattern: ^[0-9a-f]{7,64}$
        files:
          type: array
          items:
            $ref: '#/components/schemas/SourceGitReviewFile'
        files_total:
          type: integer
          minimum: 0
          description: Complete count within the attached root.
        insertions:
          type: integer
          minimum: 0
        deletions:
          type: integer
          minimum: 0
        next_cursor:
          type: string
    SourceRevisionComparison:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - root_id
        - spec
        - kind
        - label
        - before_commit
        - after_commit
      description: |
        A typed revision resolved in one root's repository to two immutable
        commits. A commit compares with its first parent; the checked-out
        branch compares its tip with its parent; any other local branch
        compares its merge base with HEAD with HEAD; A..B compares A with B,
        and A...B compares their merge base with B.
      properties:
        root_id:
          type: string
          format: uuid
        spec:
          type: string
          description: The revision as typed.
        kind:
          type: string
          enum:
            - commit
            - branch
            - range
        label:
          type: string
          description: Short name for the comparison, such as "a1b2c3d Fix parser" or "topic...HEAD".
        before_commit:
          type: string
          pattern: ^([0-9a-f]{7,64})?$
          description: Full commit id; empty only for a root commit compared with the empty tree.
        after_commit:
          type: string
          pattern: ^[0-9a-f]{7,64}$
          description: Full commit id.
        subject:
          type: string
          description: Commit subject when the comparison is one commit against its parent.
    SourceRevisionsResponse:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - comparisons
      properties:
        comparisons:
          type: array
          description: One comparison per root where the revision resolves, primary root first. Empty when it names nothing.
          items:
            $ref: '#/components/schemas/SourceRevisionComparison'
    SourceRevisionReview:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - root_id
        - before_commit
        - after_commit
        - files
        - files_total
        - insertions
        - deletions
      properties:
        root_id:
          type: string
          format: uuid
        before_commit:
          type: string
          pattern: ^([0-9a-f]{7,64})?$
          description: Empty compares with the empty tree.
        after_commit:
          type: string
          pattern: ^[0-9a-f]{7,64}$
        files:
          type: array
          items:
            $ref: '#/components/schemas/SourceGitReviewFile'
        files_total:
          type: integer
          minimum: 0
          description: Complete count within the attached root.
        insertions:
          type: integer
          minimum: 0
        deletions:
          type: integer
          minimum: 0
        next_cursor:
          type: string
    SourceCommitRoot:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - root_id
        - head
        - available
      properties:
        root_id:
          type: string
          format: uuid
        head:
          type: string
          description: Full HEAD object id; empty before the first commit.
        available:
          type: boolean
        error:
          type: string
    SourceWalkTurn:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - session_id
        - turn
        - message_id
        - prompt
        - observed_at
      description: The opening user message and complete-transcript ordinal for a recorded turn.
      properties:
        session_id:
          type: string
          format: uuid
        turn:
          type: integer
          minimum: 1
        message_id:
          type: string
          format: uuid
        prompt:
          type: string
          description: A short excerpt of the opening user message.
        observed_at:
          type: string
          format: date-time
    SourceCommitComparison:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - head
        - status
        - op
        - availability
        - history_truncated
      description: |
        Current Git comparison addressed by root_id and path, independent of
        recorded effects. No event, timestamp or author is inferred from Git
        status. file_id on the containing row is empty without retained history.
      properties:
        head:
          type: string
          description: Full HEAD object id; empty before the first commit.
        status:
          type: string
          description: Two-column porcelain-v2 index/worktree status, or ?? for untracked.
        op:
          $ref: '#/components/schemas/SourceChangeOp'
        availability:
          type: string
          description: Availability of current content; metadata-only paths remain listed.
        history_truncated:
          type: boolean
          description: Only the latest recorded history fits this row; all retained versions remain available in the file version picker.
    SourceTipState:
      type: string
      enum:
        - content
        - absent
    SourceTip:
      x-go-generate: true
      type: object
      required:
        - state
      additionalProperties: false
      description: |
        What a file ends at. A deletion is an outcome, not a missing one: state
        absent carries no sha256, so a deleted file stays addressable.
      properties:
        state:
          $ref: '#/components/schemas/SourceTipState'
        sha256:
          x-go-name: Sha256
          type: string
          description: Content hash. Present only when state is content.
    SourceHeadMatch:
      type: string
      enum:
        - same
        - differs
        - absent
        - unknown
    SourceContributor:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - origin
        - turn
        - actor_label
      description: One author of a change. Identity fields the contribution does not have are omitted.
      properties:
        origin:
          $ref: '#/components/schemas/SourceChangeOrigin'
        session_id:
          type: string
          format: uuid
          description: Chat that made the change; omitted for external contributions.
        turn:
          type: integer
        person_id:
          type: string
          format: uuid
          description: Person who wrote user-origin text; omitted for agent and external contributions.
        actor_label:
          type: string
        tool_call_id:
          type: string
          pattern: ^[a-zA-Z0-9:._-]+$
        tool_name:
          type: string
        worker_id:
          type: string
          format: uuid
    SourceWalkEffect:
      x-go-generate: true
      x-go-field-order:
        - id
        - project_id
        - operation_id
        - file_id
        - before_version_id
        - after_version_id
        - workspace_kind
        - root_id
        - path
        - from_root_id
        - from_path
        - entry_kind
        - op
        - origin
        - session_id
        - worker_id
        - turn
        - tool_call_id
        - tool_name
        - batch_id
        - ordinal
        - cause
        - capture_quality
        - actor_label
        - git_change_id
        - command_id
        - observed_at
        - contributors
      type: object
      required:
        - id
        - project_id
        - operation_id
        - file_id
        - after_version_id
        - workspace_kind
        - root_id
        - path
        - entry_kind
        - op
        - origin
        - turn
        - ordinal
        - cause
        - capture_quality
        - observed_at
        - contributors
      additionalProperties: false
      properties:
        contributors:
          type: array
          items:
            $ref: '#/components/schemas/SourceContributor'
        id:
          type: string
          format: uuid
        project_id:
          type: string
          format: uuid
        operation_id:
          type: string
          format: uuid
        file_id:
          type: string
          format: uuid
        before_version_id:
          x-go-pointer: true
          type: string
          format: uuid
        after_version_id:
          type: string
          format: uuid
        workspace_kind:
          x-go-type: SourceWorkspaceKind
          allOf:
            - $ref: '#/components/schemas/SourceWorkspaceKind'
          description: Which kind of tree this effect landed in. A worker effect names its overlay through worker_id; the project's own tree is the default.
        root_id:
          type: string
          format: uuid
        path:
          type: string
        from_root_id:
          x-go-pointer: true
          type: string
          format: uuid
        from_path:
          x-go-pointer: true
          type: string
        op:
          $ref: '#/components/schemas/SourceChangeOp'
        entry_kind:
          type: string
        origin:
          $ref: '#/components/schemas/SourceChangeOrigin'
        session_id:
          x-go-pointer: true
          type: string
          format: uuid
        worker_id:
          x-go-pointer: true
          type: string
          format: uuid
        turn:
          type: integer
        tool_call_id:
          x-go-pointer: true
          type: string
          pattern: ^[a-zA-Z0-9:._-]+$
        tool_name:
          x-go-pointer: true
          type: string
          description: Tool that caused the source effect, when invoked through the tool plane
        batch_id:
          x-go-pointer: true
          type: string
          format: uuid
          description: Shared by every effect one observation batch or one batch write recorded together; omitted for a single write.
        ordinal:
          type: integer
          format: int64
          minimum: 1
        cause:
          type: string
        capture_quality:
          type: string
          enum:
            - exact
            - observed
            - reconciled
        actor_label:
          x-go-pointer: true
          type: string
        git_change_id:
          x-go-pointer: true
          type: string
          format: uuid
          description: Id of the entry in the response's git_changes list this effect's operation traced its changes to; omitted for operations with no git cause.
        command_id:
          x-go-pointer: true
          type: string
          format: uuid
          description: Id of the entry in the response's commands list whose window was open when this effect was observed; omitted for operations no window covered.
        observed_at:
          x-go-name: ObservedAt
          type: string
          format: date-time
    SourceWalkFile:
      x-go-generate: true
      type: object
      required:
        - file_id
        - root_id
        - path
        - changed_since_presented
        - unpresented_agent_effects
        - tip
        - head_match
        - effects
      additionalProperties: false
      properties:
        commit:
          x-go-pointer: true
          $ref: '#/components/schemas/SourceCommitComparison'
        file_id:
          type: string
          format: uuid
        root_id:
          type: string
          format: uuid
        path:
          type: string
        last_at:
          x-go-pointer: true
          x-go-name: LastAt
          type: string
          format: date-time
        changed_since_presented:
          type: boolean
        unpresented_agent_effects:
          type: integer
          format: int64
          minimum: 0
          description: Agent effects not yet completed by a file presentation; human edits never clear this count.
        presentation_effect_id:
          type: string
          format: uuid
        presentation_ordinal:
          type: integer
          format: int64
          minimum: 1
        tip:
          $ref: '#/components/schemas/SourceTip'
        head_match:
          $ref: '#/components/schemas/SourceHeadMatch'
          description: Whether the tip's bytes equal what HEAD holds at this path, by derived git blob object id. Unknown whenever the lens, the repository, or the captured bytes cannot answer.
        effects:
          type: array
          items:
            $ref: '#/components/schemas/SourceWalkEffect'
    SourceCommandWindowState:
      type: string
      enum:
        - running
        - ended
        - interrupted
    SourceCommandWindow:
      x-go-generate: true
      type: object
      required:
        - id
        - session_id
        - turn
        - tool_name
        - command_line
        - state
        - ordinal
        - started_at
      additionalProperties: false
      description: |
        One host-run command's observation window: the span of a command or
        verify tool call during which reconcile passes attribute the file
        changes they observe to that command, and admit files the window's
        start inventory did not hold. Effects reference it by command_id. It
        states when a change was observed, never that the process wrote the
        bytes: an edit made in another editor while the command ran lands
        here too, and the label says so.
      properties:
        id:
          type: string
          format: uuid
        session_id:
          type: string
          format: uuid
          description: Chat whose tool call ran the command; its effects answer that chat's turn and session lenses.
        turn:
          type: integer
        tool_call_id:
          x-go-pointer: true
          type: string
          pattern: ^[a-zA-Z0-9:._-]+$
        tool_name:
          x-go-pointer: true
          type: string
          description: The tool that ran the command — command or verify.
        command_line:
          type: string
          description: The canonical command line, with secret references rather than values.
        state:
          $ref: '#/components/schemas/SourceCommandWindowState'
        admission_mode:
          x-go-pointer: true
          type: string
          description: How the pass that ended the window selected files. `scope` means every directory the root's source scope admitted was observed; `scope_bounded` means a walk budget stopped the observation short of the scope. Omitted while running.
        ordinal:
          type: integer
          format: int64
          minimum: 1
          description: Position on the project's source-history clock, taken when the command started.
        started_at:
          x-go-name: StartedAt
          type: string
          format: date-time
        ended_at:
          x-go-pointer: true
          x-go-name: EndedAt
          type: string
          format: date-time
          description: When the window ended; omitted while running.
    SourceWalkResponse:
      x-go-generate: true
      type: object
      required:
        - baseline
        - files
        - git_changes
        - commands
        - turns
        - commit_available
      additionalProperties: false
      properties:
        baseline:
          type: string
          description: |
            The baseline this page answered, in the request's grammar. A
            chat-relative `turn:{session_id}` comes back as
            `turn:{session_id},{n}` naming the turn it read; a chat with no
            user turn yet answers `turn:{session_id},0` and lists nothing.
            Send it back for later pages and address per-file comparisons with
            it, so every read describes the same range.
        next_cursor:
          type: string
          description: Opaque cursor for the next page. Omitted on the final page.
        commit_roots:
          type: array
          items:
            $ref: '#/components/schemas/SourceCommitRoot'
        turns:
          type: array
          items:
            $ref: '#/components/schemas/SourceWalkTurn'
        files:
          type: array
          items:
            $ref: '#/components/schemas/SourceWalkFile'
        git_changes:
          type: array
          items:
            $ref: '#/components/schemas/SourceGitChange'
          description: |
            Movements selected by the same baseline and ordinal page as file
            effects, including commits with no working-file changes, newest first.
            Also includes movements referenced by this page's effects through
            git_change_id; those references do not advance the page cursor.
        commands:
          type: array
          items:
            $ref: '#/components/schemas/SourceCommandWindow'
          description: |
            Every command observation window an effect on this page references
            by command_id, newest first. A window that observed nothing has no
            row, so there is no bare command to list.
        commit_available:
          type: boolean
          description: False when the project root is not a git checkout
    SourceFileVersion:
      x-go-generate: true
      type: object
      required:
        - id
        - file_id
        - workspace_kind
        - root_id
        - path
        - state
        - size_bytes
        - capture_state
        - capture_quality
        - landing
        - ordinal
        - turn
        - created_at
      additionalProperties: false
      description: |
        One retained state of a logical file. States a recorded effect produced
        carry operation_id, effect_id, op, origin, and cause; states the ledger
        kept without a causing action — a tracked baseline, a preserved
        pre-image — omit all five. Absence is the signal that no action is
        known, never an empty action.

        An agent edit held in the person's open document is the third kind: it
        names its operation, origin, and cause, but no effect and no op,
        because nothing reached the working file. landing states that, and is
        the only place a state's bytes were never the file's own.
      properties:
        contributors:
          type: array
          items:
            $ref: '#/components/schemas/SourceContributor'
          description: Authors of the publication; omitted for retained states without a producing effect.
        id:
          type: string
          format: uuid
        file_id:
          type: string
          format: uuid
        workspace_kind:
          x-go-type: SourceWorkspaceKind
          allOf:
            - $ref: '#/components/schemas/SourceWorkspaceKind'
          description: Which kind of tree retained this state. A worker state names its overlay through worker_id.
        parent_version_id:
          x-go-pointer: true
          type: string
          format: uuid
        derived_from_version_id:
          x-go-pointer: true
          type: string
          format: uuid
        operation_id:
          x-go-pointer: true
          type: string
          format: uuid
        effect_id:
          x-go-pointer: true
          type: string
          format: uuid
        root_id:
          type: string
          format: uuid
        path:
          type: string
        state:
          type: string
          enum:
            - content
            - directory
            - absent
            - unresolved
        content_sha256:
          x-go-name: ContentSha256
          type: string
        size_bytes:
          type: integer
          format: int64
          minimum: 0
        capture_state:
          type: string
          enum:
            - stored
            - metadata_only
            - not_applicable
        capture_reason:
          type: string
        capture_quality:
          type: string
          enum:
            - exact
            - observed
            - reconciled
        landing:
          type: string
          enum:
            - working_file
            - editor_document
          description: Where these bytes landed. working_file is every state the file itself held; editor_document is an agent edit that landed in the person's open document and never reached disk, retained so they can restore it after a reload or a discard.
        op:
          x-go-pointer: true
          $ref: '#/components/schemas/SourceChangeOp'
          description: Omitted when no recorded effect produced this state.
        ordinal:
          type: integer
          format: int64
          minimum: 1
          description: Position on the project's source-history clock, and the exclusive cursor for the next page.
        origin:
          x-go-pointer: true
          $ref: '#/components/schemas/SourceChangeOrigin'
          description: Omitted when no recorded operation produced this state.
        cause:
          x-go-pointer: true
          type: string
        actor_label:
          x-go-pointer: true
          type: string
        session_id:
          x-go-pointer: true
          type: string
          format: uuid
        worker_id:
          x-go-pointer: true
          type: string
          format: uuid
        turn:
          type: integer
        tool_call_id:
          x-go-pointer: true
          type: string
          pattern: ^[a-zA-Z0-9:._-]+$
        batch_id:
          x-go-pointer: true
          type: string
          format: uuid
        git_change:
          x-go-pointer: true
          $ref: '#/components/schemas/SourceGitChange'
          description: The observed ref movement the producing operation traced this state to; omitted for states with no git cause.
        command:
          x-go-pointer: true
          $ref: '#/components/schemas/SourceCommandWindow'
          description: The command observation window open when this state was observed; omitted for states no window covered.
        created_at:
          x-go-name: CreatedAt
          type: string
          format: date-time
    SourceFileCommit:
      x-go-generate: true
      type: object
      required:
        - commit
        - subject
        - authored_at
        - committed_at
        - source_path
      additionalProperties: false
      description: |
        One commit of the file's git lineage, followed across renames. A
        commit whose blob equals a retained version's bytes names it in
        matches_version_id — one state, both identities. A commit whose bytes
        the tracked working tree never held either names the observed ref
        movement that brought it (arrival_git_change_id) or stands in the
        git-history era. Timestamps are git's own claims and are display
        metadata, never an ordering authority.
      properties:
        commit:
          type: string
          pattern: ^[0-9a-f]{7,64}$
        subject:
          type: string
        author_name:
          x-go-pointer: true
          type: string
        authored_at:
          x-go-name: AuthoredAt
          type: string
          format: date-time
        committed_at:
          x-go-name: CommittedAt
          type: string
          format: date-time
        source_path:
          type: string
          description: The file's path at this commit, relative to the repository toplevel — the address the commit resolves, whatever the file is called today.
        blob_oid:
          x-go-pointer: true
          type: string
          pattern: ^[0-9a-f]{7,64}$
          description: Full git object id of the file's bytes at this commit; omitted when the commit removed the file.
        matches_version_id:
          x-go-pointer: true
          type: string
          format: uuid
          description: Retained version holding byte-identical content, by derived blob object id; the picker renders one row carrying both identities.
        arrival_git_change_id:
          x-go-pointer: true
          type: string
          format: uuid
          description: Id of the entry in this response's arrivals list — the observed ref movement whose window first made this commit reachable. Omitted when unknown or pre-tracking.
    SourceGitHistoryState:
      type: string
      description: 'What the Git lineage lane can say about this file. The states separate an absent lane from a lane that was asked and could not answer: an empty commits list means "no history" only under available.'
      enum:
        - not_requested
        - not_tracked
        - no_repository
        - available
        - timed_out
        - failed
    SourceFileVersionsResponse:
      x-go-generate: true
      type: object
      required:
        - file_id
        - current
        - versions
        - commits
        - arrivals
        - git_history_state
      additionalProperties: false
      properties:
        file_id:
          type: string
          format: uuid
        current:
          $ref: '#/components/schemas/SourceTip'
          description: Tracked primary-workspace head for this logical file; absent also covers a worker-only file.
        versions:
          type: array
          items:
            $ref: '#/components/schemas/SourceFileVersion'
        commits:
          type: array
          items:
            $ref: '#/components/schemas/SourceFileCommit'
          description: 'One page of the file''s git lineage, newest first. Empty under every git_history_state except available, where an empty list is the answer: the file has no commits under its tracked path.'
        arrivals:
          type: array
          items:
            $ref: '#/components/schemas/SourceGitChange'
          description: The observed ref movements this page's commits name as their arrival.
        git_history_state:
          $ref: '#/components/schemas/SourceGitHistoryState'
          description: 'Whether the lane was asked and what came back. Read it before reading commits: only available makes an empty list a statement about the file, and timed_out and failed are facts about the query that must never be presented as an absence of history.'
        tracked_at:
          x-go-pointer: true
          x-go-name: TrackedAt
          type: string
          format: date-time
          description: When the ledger first retained a state of this file — the boundary between the observed timeline and history reconstructed from git.
        next_cursor:
          type: string
          description: Opaque cursor continuing every requested lane that holds more. Omitted when all are exhausted.
    SourceVersionRestoreRequest:
      x-go-generate: true
      type: object
      required:
        - operation_id
        - file_id
        - root_id
        - path
        - base
      additionalProperties: false
      properties:
        operation_id:
          type: string
          format: uuid
          description: Idempotency key for this restore attempt.
        file_id:
          type: string
          format: uuid
          description: Logical file identity shown by the version picker.
        root_id:
          type: string
          format: uuid
          description: Attached project root that contains the working-file target.
        path:
          type: string
          description: Current root-relative working-file path. Historical renames do not move it.
        base:
          $ref: '#/components/schemas/SourceTip'
    SourceVersionRestoreResponse:
      x-go-generate: true
      type: object
      required:
        - version_id
        - file_id
        - root_id
        - path
        - state
        - changed
      additionalProperties: false
      properties:
        version_id:
          type: string
          format: uuid
          description: Immutable retained version selected by the user.
        previous_version_id:
          type: string
          format: uuid
          description: Exact pre-restore state for one-click Undo, when the logical file already had a primary-workspace head.
        file_id:
          type: string
          format: uuid
        root_id:
          type: string
          format: uuid
        path:
          type: string
        state:
          $ref: '#/components/schemas/SourceTipState'
        sha256:
          x-go-name: Sha256
          type: string
          description: Exact restored-byte hash when state is content.
        changed:
          type: boolean
          description: False when the selected state already matched the working file.
    SourceCommitRestoreRequest:
      x-go-generate: true
      type: object
      required:
        - operation_id
        - file_id
        - root_id
        - path
        - source_path
        - blob_oid
        - base
      additionalProperties: false
      description: |
        Restores the file's bytes as one commit holds them. The bytes come
        from the repository object store and are verified against blob_oid
        before anything is written; the mutation lands through the ledger's
        write door as an ordinary attributed restore.
      properties:
        operation_id:
          type: string
          format: uuid
          description: Idempotency key for this restore attempt.
        file_id:
          type: string
          format: uuid
          description: Logical file identity shown by the version picker.
        root_id:
          type: string
          format: uuid
          description: Root whose repository holds the commit and receives the restore.
        path:
          type: string
          description: The file's current root-relative path — the restore target.
        source_path:
          type: string
          description: The file's path at the commit, toplevel-relative, from the listed lineage.
        blob_oid:
          type: string
          pattern: ^[0-9a-f]{7,64}$
          description: Object id of the bytes being restored; the fetched content must hash to it.
        base:
          $ref: '#/components/schemas/SourceTip'
          description: Reviewed current tip for compare-and-swap protection.
    SourceCommitRestoreResponse:
      x-go-generate: true
      type: object
      required:
        - commit
        - file_id
        - root_id
        - path
        - state
        - changed
      additionalProperties: false
      properties:
        commit:
          type: string
          pattern: ^[0-9a-f]{7,64}$
        previous_version_id:
          type: string
          format: uuid
          description: The retained pre-restore state, the exact Undo target.
        file_id:
          type: string
          format: uuid
        root_id:
          type: string
          format: uuid
        path:
          type: string
        state:
          $ref: '#/components/schemas/SourceTipState'
        sha256:
          x-go-name: Sha256
          type: string
        changed:
          type: boolean
    SourceSeenFile:
      x-go-generate: true
      type: object
      required:
        - file_id
        - root_id
        - path
        - tip
        - seen_at
        - through_ordinal
        - effects
        - effects_truncated
      additionalProperties: false
      description: A file whose latest look is still current, with the effects that look covered.
      properties:
        file_id:
          type: string
          format: uuid
        root_id:
          type: string
          format: uuid
        path:
          type: string
        tip:
          $ref: '#/components/schemas/SourceTip'
        seen_at:
          type: string
          format: date-time
          description: When the look completed.
        through_ordinal:
          type: integer
          format: int64
          minimum: 1
          description: Names the look. Withdrawing it quotes this value.
        effects:
          type: array
          items:
            $ref: '#/components/schemas/SourceWalkEffect'
          description: Effects the look covered, newest first.
        effects_truncated:
          type: boolean
          description: More effects than listed fell within the look.
    SourceSeenList:
      x-go-generate: true
      type: object
      required:
        - files
      additionalProperties: false
      properties:
        files:
          type: array
          items:
            $ref: '#/components/schemas/SourceSeenFile'
          description: Newest look first.
        next_cursor:
          type: string
          description: Opaque cursor for the next page. Omitted on the final page.
    SourcePresentationCompletion:
      x-go-generate: true
      type: object
      required:
        - file_id
        - effect_id
        - ordinal
      additionalProperties: false
      description: |
        The newest effect a Files or Review pane painted for the file. The look
        covers every visible primary-tree effect of the file up to it, whoever
        made them.
      properties:
        file_id:
          type: string
          format: uuid
        effect_id:
          type: string
          format: uuid
        ordinal:
          type: integer
          format: int64
          minimum: 1
    StorageUsageLane:
      x-go-generate: true
      type: object
      required:
        - lane
        - scope
        - used_bytes
      additionalProperties: false
      properties:
        lane:
          type: string
          enum:
            - artifacts
            - source_blobs
            - prompt_attachments
        scope:
          type: string
          enum:
            - project
            - device
        used_bytes:
          type: integer
          format: int64
          minimum: 0
    StorageUsageReport:
      x-go-generate: true
      type: object
      required:
        - lanes
      additionalProperties: false
      description: Retained-content usage, measured per lane.
      properties:
        lanes:
          type: array
          items:
            $ref: '#/components/schemas/StorageUsageLane'
    SourceStorage:
      x-go-generate: true
      type: object
      required:
        - inventory
        - storage
      additionalProperties: false
      properties:
        inventory:
          $ref: '#/components/schemas/SourceInventoryState'
        storage:
          $ref: '#/components/schemas/StorageUsageReport'
    ScanCoverageStatus:
      type: string
      description: What absence of findings proves. complete covers the whole tree; bounded covers every file the source generation admitted while a budget left directories unobserved (see source_admission_mode); partial means the engine could not read or parse part of what it was given; unavailable means the run produced no coverage.
      enum:
        - complete
        - bounded
        - partial
        - unavailable
    FullPassMemberPhase:
      type: string
      description: waiting_for_scanner means this scanner is still finishing earlier work and the pass starts after it; waiting_for_pass means this scanner is free and starts with the other members; started means scan holds the member's run; not_started means the member left the pass before it started (its scanner was deselected or could not be dispatched), so the pass covers less than was requested.
      enum:
        - waiting_for_scanner
        - waiting_for_pass
        - started
        - not_started
    ScanRuntimePolicy:
      x-go-generate: true
      type: object
      required:
        - soft_limit_ms
        - hard_limit_ms
        - cpu_units
        - parallelism
      additionalProperties: false
      properties:
        soft_limit_ms:
          type: integer
          minimum: 1000
          multipleOf: 1000
          description: Runtime after which the scan is reported as long-running but continues.
        hard_limit_ms:
          type: integer
          minimum: 0
          multipleOf: 1000
          description: Optional terminal runtime ceiling for one immutable generation. Zero allows the scan to finish without a runtime deadline; owner cancellation still applies.
        cpu_units:
          type: integer
          minimum: 1
          maximum: 8
          description: Weighted CPU admission units reserved while the scanner runs.
        parallelism:
          type: integer
          minimum: 1
          maximum: 16
          description: Scanner-internal worker limit.
    ScanTargetKind:
      type: string
      enum:
        - full
        - paths
    ScanExecutionManifest:
      x-go-generate: true
      type: object
      required:
        - schema_version
        - scanner_id
        - engine
        - driver
        - scope_kind
        - definition_fingerprint
        - fingerprint_scheme
        - runtime
      additionalProperties: false
      properties:
        schema_version:
          type: string
        scanner_id:
          type: string
          pattern: ^[a-zA-Z0-9_-]+$
        engine:
          type: string
        engine_version:
          type: string
        engine_sha256:
          type: string
        driver:
          type: string
        scope_kind:
          type: string
        parser_id:
          type: string
          pattern: ^[a-zA-Z0-9_-]+$
        mapper_id:
          type: string
          pattern: ^[a-zA-Z0-9_-]+$
        definition_fingerprint:
          type: string
        rules_sha256:
          type: string
        exclusions_sha256:
          type: string
        fingerprint_scheme:
          type: string
        runtime:
          $ref: '#/components/schemas/ScanRuntimePolicy'
    ScanProgress:
      x-go-generate: true
      type: object
      description: How far a chunked scan has come. An engine receives the generation's files in bounded invocations; `completed` counts the invocations done out of `chunks`, over `files` in total. Present only while a scan runs in more than one invocation.
      required:
        - chunks
        - completed
        - files
      additionalProperties: false
      properties:
        chunks:
          type: integer
          minimum: 0
        completed:
          type: integer
          minimum: 0
        files:
          type: integer
          minimum: 0
    SecurityFindingCallTrace:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - location
      properties:
        location:
          $ref: '#/components/schemas/SecurityFindingLocation'
        intermediates:
          type: array
          items:
            $ref: '#/components/schemas/SecurityFindingLocation'
        callee:
          x-go-pointer: true
          $ref: '#/components/schemas/SecurityFindingCallTrace'
    SecurityFindingDataflow:
      x-go-generate: true
      type: object
      additionalProperties: false
      description: Engine-reported flow evidence. Presence does not establish path feasibility or exploitability.
      properties:
        source:
          x-go-pointer: true
          $ref: '#/components/schemas/SecurityFindingCallTrace'
        intermediates:
          type: array
          items:
            $ref: '#/components/schemas/SecurityFindingLocation'
        sink:
          x-go-pointer: true
          $ref: '#/components/schemas/SecurityFindingCallTrace'
    ScanWarningSummary:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - kind
        - count
        - files
        - rules
      properties:
        kind:
          $ref: '#/components/schemas/ScanWarningKind'
        count:
          type: integer
          minimum: 1
        files:
          type: integer
          minimum: 0
        rules:
          type: integer
          minimum: 0
    SecurityFullPassMember:
      x-go-generate: true
      type: object
      description: One scanner of a full pass and how far it has come.
      required:
        - scanner_id
        - phase
      additionalProperties: false
      properties:
        scanner_id:
          type: string
          pattern: ^[a-zA-Z0-9_-]+$
        phase:
          $ref: '#/components/schemas/FullPassMemberPhase'
        scan:
          x-go-pointer: true
          $ref: '#/components/schemas/CodeScan'
    FindingLedgerState:
      type: string
      description: Whether a finding is currently reported, and when it is not, whether its absence is evidence of anything.
      enum:
        - open
        - reopened
        - fixed
        - not_observed
        - ignored
        - unverified
    FindingLedgerSort:
      type: string
      description: Which ledger column orders the page. Severity orders by normalized rank, not by label.
      enum:
        - severity
        - finding
        - location
        - state
        - first_seen
        - last_seen
    FindingLedgerQueryRequest:
      x-go-generate: true
      type: object
      additionalProperties: false
      properties:
        text:
          type: string
          description: Case-insensitive substring over the finding message, rule id, location uri, and advisory ids. A filter over stored fields, not a ranking or a classifier.
        levels:
          type: array
          items:
            $ref: '#/components/schemas/FindingLevel'
        states:
          type: array
          items:
            $ref: '#/components/schemas/FindingLedgerState'
        scanner_ids:
          x-go-name: ScannerIDs
          type: array
          items:
            type: string
            pattern: ^[a-zA-Z0-9_-]+$
        kind:
          type: string
        rule_id:
          type: string
          pattern: ^[a-zA-Z0-9_.:/-]+$
        path:
          type: string
          description: Location uri prefix.
        advisory_id:
          type: string
          pattern: ^[a-zA-Z0-9_.:/-]+$
        fingerprint:
          type: string
        code:
          type: string
          description: Hint code (properties.lycaon.hint_code).
        introduced_since_at:
          x-go-pointer: true
          type: string
          format: date-time
        sort:
          $ref: '#/components/schemas/FindingLedgerSort'
        order:
          type: string
          enum:
            - asc
            - desc
        cursor:
          type: string
          description: Opaque pagination cursor.
        limit:
          type: integer
          minimum: 1
          maximum: 500
    FindingAbsence:
      x-go-generate: true
      type: object
      description: 'What the host knows about a finding no longer being reported: the coverage the scan that stopped seeing it established, and whether the scanner''s execution identity has moved since. Facts, not a verdict — FindingLedgerState reads them into fixed, not_observed, or unverified.'
      required:
        - observed_at
        - execution_moved
      additionalProperties: false
      properties:
        observed_at:
          type: string
          format: date-time
        scan_id:
          type: string
          format: uuid
        coverage_status:
          $ref: '#/components/schemas/ScanCoverageStatus'
        execution_moved:
          type: boolean
    FindingIgnoreJustification:
      type: string
      description: The OpenVEX justification for an advisory a project has judged not to apply, carried unchanged so an ignore exports as a VEX statement.
      enum:
        - component_not_present
        - vulnerable_code_not_present
        - vulnerable_code_not_in_execute_path
        - vulnerable_code_cannot_be_controlled_by_adversary
        - inline_mitigations_already_exist
    FindingIgnore:
      x-go-generate: true
      type: object
      description: 'The project''s decision about one finding, as the ledger cached it from the ignore file. It never claims the finding is gone: the scanner still reports it, and the entry only says nobody intends to act.'
      required:
        - entry_id
      additionalProperties: false
      properties:
        entry_id:
          type: string
          pattern: ^[a-zA-Z0-9_-]+$
        reason:
          type: string
        matched_on:
          type: string
          description: The predicates the entry decided on, so a row can point at its decision without the reader opening the file.
        justification:
          $ref: '#/components/schemas/FindingIgnoreJustification'
        expires_on:
          type: string
          format: date
          description: A calendar day, exclusive.
        expired:
          type: boolean
          description: True for a decision that has lapsed. The finding has already returned to the open list; this is what says why it came back.
    FindingLedgerEntry:
      x-go-generate: true
      type: object
      description: One finding as the project holds it now.
      required:
        - finding
        - state
        - scanner_id
        - first_seen_at
        - last_seen_at
        - observations
      additionalProperties: false
      properties:
        finding:
          $ref: '#/components/schemas/SecurityFinding'
        state:
          $ref: '#/components/schemas/FindingLedgerState'
        scanner_id:
          type: string
          pattern: ^[a-zA-Z0-9_-]+$
        first_seen_at:
          type: string
          format: date-time
        last_seen_at:
          type: string
          format: date-time
          description: 'The newest observation that still stands behind the row: for an open finding, the completion of the scan whose authoritative set holds it; for one that left, the last time a scanner reported it present.'
        observations:
          type: integer
          minimum: 1
        last_scan_id:
          type: string
          format: uuid
          description: The run that recorded the row's last event, for drill-down.
        absence:
          x-go-pointer: true
          $ref: '#/components/schemas/FindingAbsence'
        ignore:
          x-go-pointer: true
          $ref: '#/components/schemas/FindingIgnore'
    FindingLedgerCounts:
      x-go-generate: true
      type: object
      description: 'The project''s whole ledger by state, independent of a query''s filters, so a filtered page never reads as the project total. The states partition the ledger exactly: every row is counted once. Fixed, not_observed, and unverified stay separate: a bounded pass is not a fix, and folding them together is how a scan gap becomes a clean bill.'
      required:
        - open
        - reopened
        - fixed
        - not_observed
        - unverified
        - ignored
      additionalProperties: false
      properties:
        open:
          type: integer
          minimum: 0
        reopened:
          type: integer
          minimum: 0
        fixed:
          type: integer
          minimum: 0
        not_observed:
          type: integer
          minimum: 0
        unverified:
          type: integer
          minimum: 0
        ignored:
          type: integer
          minimum: 0
    FindingLedgerResponse:
      x-go-generate: true
      type: object
      required:
        - project_id
        - entries
        - counts
        - by_level
        - total_match
      additionalProperties: false
      properties:
        project_id:
          type: string
          format: uuid
        entries:
          type: array
          items:
            $ref: '#/components/schemas/FindingLedgerEntry'
        counts:
          $ref: '#/components/schemas/FindingLedgerCounts'
        by_level:
          type: object
          description: The whole ledger counted by normalized level, every level present including the ones at zero.
          additionalProperties:
            type: integer
            minimum: 0
        total_match:
          type: integer
          minimum: 0
        next_cursor:
          type: string
    FindingIgnoreRule:
      x-go-generate: true
      type: object
      description: One loaded entry with the provenance a client needs to know whether it can withdraw it. Bundled entries ship with the binary and belong to no project. `id` is the host's key and is always present, so a listing and a ledger row agree about which decision they mean; an entry written by hand without an `id` of its own is keyed by its predicates and is not withdrawable through this API.
      required:
        - id
        - reason
        - source
        - matches
        - withdrawable
      additionalProperties: false
      properties:
        id:
          type: string
          pattern: ^[a-zA-Z0-9_-]+$
        root_id:
          type: string
          format: uuid
        path:
          type: string
        kind:
          $ref: '#/components/schemas/FindingKind'
        scanner:
          type: string
          pattern: ^[a-zA-Z0-9_-]+$
        rule:
          type: string
          pattern: ^[a-zA-Z0-9_.:/*?\[\]^-]+$
          description: Engine vocabulary, matched as a glob (`*`, `?`, `[...]`).
        advisory:
          type: string
          pattern: ^[a-zA-Z0-9_.:/-]+$
        fingerprint:
          type: string
        reason:
          type: string
        justification:
          $ref: '#/components/schemas/FindingIgnoreJustification'
        expires_on:
          type: string
          format: date
        source:
          type: string
          enum:
            - bundled
            - project
        expired:
          type: boolean
        withdrawable:
          type: boolean
          description: Whether this API can remove the entry. False for bundled entries and for a hand-written one with no id; those are edited in the file.
        matches:
          type: integer
          minimum: 0
          description: How many of the project's ledger rows the entry covers now.
    FindingIgnoreListResponse:
      x-go-generate: true
      type: object
      required:
        - project_id
        - path
        - rules
      additionalProperties: false
      properties:
        project_id:
          type: string
          format: uuid
        path:
          type: string
          description: The project overlay file the decisions are written to.
        rules:
          type: array
          items:
            $ref: '#/components/schemas/FindingIgnoreRule'
    FindingIgnoreEntry:
      x-go-generate: true
      type: object
      description: 'One decision in a project''s ignore file. Every predicate is optional and they conjoin: a finding is ignored when it matches all of the ones present. They are host-owned facts wherever the host has one, so a single entry covers every scanner that reports the same thing. An entry naming no predicate is refused.'
      required:
        - reason
      additionalProperties: false
      properties:
        id:
          type: string
          pattern: ^[a-zA-Z0-9_-]+$
          description: Assigned by the host; stable so a client can withdraw this entry.
        root_id:
          type: string
          format: uuid
        path:
          type: string
          description: Repo-relative glob over the finding's primary location. A bare directory covers everything under it.
        kind:
          $ref: '#/components/schemas/FindingKind'
        scanner:
          type: string
          pattern: ^[a-zA-Z0-9_-]+$
          description: Narrows the entry to one engine; empty spans all of them.
        rule:
          type: string
          pattern: ^[a-zA-Z0-9_.:/*?\[\]^-]+$
          description: Engine vocabulary, matched as a glob (`*`, `?`, `[...]`).
        advisory:
          type: string
          pattern: ^[a-zA-Z0-9_.:/-]+$
          description: Any published id of one vulnerability; matches every alias the host resolved for it.
        fingerprint:
          type: string
          description: Names exactly one finding.
        reason:
          type: string
        justification:
          $ref: '#/components/schemas/FindingIgnoreJustification'
        expires_on:
          type: string
          format: date
          description: A calendar day (YYYY-MM-DD), exclusive.
    FindingExportFormat:
      type: string
      enum:
        - sarif
        - openvex
    FindingExportRequest:
      x-go-generate: true
      type: object
      required:
        - format
      additionalProperties: false
      properties:
        format:
          $ref: '#/components/schemas/FindingExportFormat'
        query:
          $ref: '#/components/schemas/FindingLedgerQueryRequest'
    SourcePinGitHead:
      x-go-generate: true
      type: object
      required:
        - root_id
        - repo_state
      additionalProperties: false
      description: The git position one root held when the pin was taken, as last observed before the boundary.
      properties:
        root_id:
          type: string
          format: uuid
        repo_state:
          type: string
          enum:
            - repo
            - no_repo
            - unreadable
        head_commit:
          type: string
          pattern: ^[0-9a-f]{7,64}$
        head_ref:
          type: string
    SourceAttributedText:
      x-go-generate: true
      type: object
      description: Character range in UTF-16 code units after CRLF is normalized to LF. The comparison endpoints retain their original text and saved hashes.
      additionalProperties: false
      required:
        - index
        - length
        - contributors
        - selected
        - visible
      properties:
        index:
          type: integer
          minimum: 0
          x-go-type: uint32
        length:
          type: integer
          minimum: 1
          x-go-type: uint32
        contributors:
          type: array
          items:
            $ref: '#/components/schemas/SourceContributor'
        selected:
          type: boolean
          description: Authored in the selected chat or turn, subject to the marking choice.
        visible:
          type: boolean
          description: False when the person's edit is left unmarked.
    SourceComparisonAttribution:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - before
        - after
      description: Changed ranges in UTF-16 coordinates, using collaborative character identities or recorded file deltas. Other chats remain visible as context. Missing authors remain unknown.
      properties:
        before:
          type: array
          items:
            $ref: '#/components/schemas/SourceAttributedText'
        after:
          type: array
          items:
            $ref: '#/components/schemas/SourceAttributedText'
    SourceComparisonSide:
      x-go-generate: true
      type: object
      required:
        - state
        - size_bytes
        - availability
        - content
      additionalProperties: false
      description: |
        One exact immutable endpoint. Availability is explicit because version
        identity and metadata remain explicit for metadata-only captures and
        unexpectedly unavailable content.
      properties:
        version_id:
          type: string
          format: uuid
          description: Empty for working-file, repository-object, and carried-start endpoints.
        root_id:
          type: string
          format: uuid
        path:
          type: string
        state:
          type: string
          enum:
            - content
            - directory
            - absent
            - unresolved
        sha256:
          x-go-name: Sha256
          type: string
        size_bytes:
          type: integer
          format: int64
          minimum: 0
        availability:
          type: string
          enum:
            - available
            - absent
            - directory
            - unavailable
            - not_captured
            - binary
            - unresolved
        reason:
          type: string
        content:
          type: string
          description: Exact decoded text when availability is available; otherwise empty.
        secret_screen:
          x-go-type: '*SecretScreen'
          allOf:
            - $ref: '#/components/schemas/SecretScreen'
          description: Secret spans computed over content when availability is available. Offsets refer to the exact content string in this side. Omitted when screening is unavailable or this side has no readable text.
    SourceComparison:
      x-go-generate: true
      type: object
      required:
        - in_range
        - location_changed
        - truncated
      additionalProperties: false
      description: |
        Exact endpoints painted by a Walk effect or version selection. Both
        endpoints are present when in_range is true and omitted when it is
        false — a lens with no effect for the file has no endpoints to
        describe, and inventing empty ones would report states outside the
        values each side declares.
      properties:
        truncated:
          type: boolean
          description: True when content exceeds the inline byte cap and full diff requires paged comparison views.
        presentation_after_ordinal:
          x-go-pointer: true
          type: integer
          format: int64
          minimum: 0
          description: The unread boundary used for this presentation comparison. Open views retain it until the reader leaves.
        attribution:
          x-go-pointer: true
          $ref: '#/components/schemas/SourceComparisonAttribution'
          description: Present when the endpoints share text identities; both endpoints retain their actual saved bytes.
        in_range:
          type: boolean
          description: False only when a requested lens contains no effect for the file.
        effect_id:
          type: string
          format: uuid
        file_id:
          type: string
          format: uuid
        op:
          $ref: '#/components/schemas/SourceChangeOp'
        before:
          x-go-pointer: true
          $ref: '#/components/schemas/SourceComparisonSide'
          description: Present only when in_range is true.
        after:
          x-go-pointer: true
          $ref: '#/components/schemas/SourceComparisonSide'
          description: Present only when in_range is true.
        location_changed:
          type: boolean
          description: True when root_id or path differs between the endpoints.
        user_edits_unmarked:
          type: boolean
          description: True when attribution marks omit the person's own edits. Saved endpoints are unchanged.
    SourceAttributionInterval:
      x-go-generate: true
      type: object
      required:
        - start_line
        - end_line
        - turn
        - recorded_at
      additionalProperties: false
      description: Inclusive 1-based line span containing text attributed to an agent change. Spans may overlap when several agents contributed to the same line.
      properties:
        start_line:
          type: integer
          minimum: 1
        end_line:
          type: integer
          minimum: 1
        session_id:
          x-go-pointer: true
          type: string
          format: uuid
        turn:
          type: integer
          minimum: 0
        tool_call_id:
          x-go-pointer: true
          type: string
          pattern: ^[a-zA-Z0-9:._-]+$
        recorded_at:
          x-go-name: RecordedAt
          type: string
          format: date-time
    SourceAttributionResponse:
      x-go-generate: true
      type: object
      required:
        - head_sha256
        - intervals
      additionalProperties: false
      description: |
        Line provenance for one file. head_sha256 is the tip after hash the
        intervals describe; clients render marks only when the buffer base
        sha matches and the buffer is clean.
      properties:
        head_sha256:
          x-go-name: HeadSha256
          type: string
        intervals:
          type: array
          items:
            $ref: '#/components/schemas/SourceAttributionInterval'
    WorkerJobChangedFile:
      x-go-generate: true
      type: object
      required:
        - root_id
        - path
        - op
      additionalProperties: false
      description: One overlay-modified file, addressed by root.
      properties:
        root_id:
          type: string
          format: uuid
          description: Attached root the path belongs to; two roots may share a relative path
        path:
          type: string
          description: Root-relative path (slash-separated)
        op:
          $ref: '#/components/schemas/SourceChangeOp'
    WorkerJobChangesResponse:
      x-go-generate: true
      type: object
      required:
        - files
      additionalProperties: false
      description: |
        Files a worker job has changed on its own overlay. These have not landed
        on the project roots — the coordinator lands them with promote_overlay,
        and only then do they appear on the project's change lens.
      properties:
        files:
          type: array
          items:
            $ref: '#/components/schemas/WorkerJobChangedFile'
    BlueprintListResponse:
      x-go-generate: true
      type: object
      required:
        - blueprints
      additionalProperties: false
      properties:
        blueprints:
          type: array
          items:
            $ref: '#/components/schemas/BlueprintSummary'
        truncated:
          type: boolean
          description: Present and true when older blueprints were cut from this answer.
    DelegationLegListResponse:
      x-go-generate: true
      type: object
      required:
        - legs
      additionalProperties: false
      properties:
        legs:
          type: array
          items:
            $ref: '#/components/schemas/Leg'
    WorkflowRequestSpec:
      x-go-generate: true
      type: object
      required:
        - cadence
        - question
      additionalProperties: false
      properties:
        cadence:
          type: string
          enum:
            - once
            - each_turn
        question:
          type: string
        default:
          type: string
    WorkflowPresetSummary:
      x-go-generate: true
      type: object
      required:
        - id
        - name
      additionalProperties: false
      properties:
        id:
          type: string
          pattern: ^[a-z0-9_-]+$
        name:
          type: string
        description:
          type: string
        trigger:
          type: string
    ExcludedWorkflow:
      x-go-generate: true
      type: object
      required:
        - path
        - errors
      additionalProperties: false
      properties:
        id:
          type: string
          pattern: ^[a-z0-9_-]+$
        version:
          type: string
        path:
          type: string
        errors:
          type: array
          items:
            $ref: '#/components/schemas/ComposeValidationError'
    WorkflowListResponse:
      x-go-generate: true
      type: object
      required:
        - workflows
      additionalProperties: false
      properties:
        workflows:
          type: array
          items:
            $ref: '#/components/schemas/WorkflowSummary'
        excluded:
          type: object
          description: Rejected workflow manifests keyed by their source path, with all validation diagnostics retained.
          additionalProperties:
            $ref: '#/components/schemas/ExcludedWorkflow'
    ComposeExecutionModeTransition:
      x-go-generate: true
      type: object
      required:
        - phase
        - mode
      additionalProperties: false
      properties:
        phase:
          type: string
        mode:
          type: string
          enum:
            - investigate
            - orchestrate
    WorkflowTemplateListResponse:
      x-go-generate: true
      type: object
      required:
        - templates
      additionalProperties: false
      properties:
        templates:
          type: array
          items:
            $ref: '#/components/schemas/WorkflowTemplateSummary'
    WorkflowFailure:
      x-go-generate: true
      type: object
      required:
        - code
        - message
        - retryable
      additionalProperties: false
      properties:
        code:
          type: string
          description: Stable machine-readable terminal failure code. `REPORT_NOT_ACCEPTED` ends a run whose report still failed the host's document check when its repairs ran out; the report is stored with its defects and stays downloadable.
        message:
          type: string
        phase:
          type: string
        stage:
          type: string
        retryable:
          type: boolean
    ChoiceTransitionUi:
      x-go-generate: true
      type: object
      required:
        - id
        - label
        - armed
      additionalProperties: false
      properties:
        id:
          type: string
          pattern: ^[a-z0-9_-]+$
        label:
          type: string
        armed:
          type: boolean
    WorkflowRunObligation:
      x-go-generate: true
      type: object
      required:
        - kind
        - status
      additionalProperties: false
      properties:
        kind:
          type: string
          description: Registered obligation kind, e.g. "scan".
        status:
          type: string
          enum:
            - 'off'
            - empty
            - pending
            - complete
            - failed
          description: off, complete, and failed settle the gate; empty means no tracked work.
        error:
          type: string
        detail:
          type: object
          additionalProperties: true
          description: Kind-specific progress counters.
    WorkflowTopologyLeg:
      x-go-generate: true
      type: object
      required:
        - id
        - stage
        - phase_id
        - label
        - status
      additionalProperties: false
      properties:
        id:
          type: string
          pattern: ^[a-z0-9_-]+$
          description: Stable within the run; a pipeline stage's name, or a fan-out subtask's position as `subtask-<n>`.
        stage:
          type: string
          description: The topology stage a phase binds; every fan-out subtask belongs to stage `fan_out`.
        phase_id:
          type: string
          pattern: ^[a-z0-9_-]+$
        label:
          type: string
          description: The topology's label for the leg.
        status:
          $ref: '#/components/schemas/LegStatus'
        waits_for:
          type: array
          description: Ids of legs whose output this leg takes as input.
          items:
            type: string
    WorkflowRunUi:
      x-go-generate: true
      type: object
      required:
        - current_phase_label
      additionalProperties: false
      properties:
        current_phase_label:
          type: string
          description: Host-projected activity label from the active workflow manifest phase.
        human_approval_awaiting:
          type: boolean
        request:
          x-go-pointer: true
          $ref: '#/components/schemas/WorkflowRequestSpec'
        request_state:
          x-go-pointer: true
          $ref: '#/components/schemas/WorkflowRequestState'
        report_available:
          type: boolean
          description: 'Host-authorized document download after the workflow stored its enabled report and reached a terminal state: an accepted report completes the run, and one the host did not accept fails it with `REPORT_NOT_ACCEPTED`.'
        phase_terminal:
          type: boolean
          description: The current phase is the workflow's last. A run rests here with status still "running" until the next advance finds no target, so a client that paints activity from the phase label must stop when this is true.
        plan_revision_at:
          x-go-pointer: true
          type: string
          format: date-time
        pending_feedback:
          x-go-pointer: true
          $ref: '#/components/schemas/PendingFeedback'
        choice_transitions:
          type: array
          description: 'Human-actor choice leaves on the current phase (manifest order). Coordinator-only edges are omitted. armed reflects optional when: readiness.'
          items:
            $ref: '#/components/schemas/ChoiceTransitionUi'
        phase_obligations:
          type: array
          description: Run-scoped status for the current phase's host obligations.
          items:
            $ref: '#/components/schemas/WorkflowRunObligation'
        topology_legs:
          type: array
          description: Every leg the run's topology plans for its bound phases, in topology order, with the status of its dispatched worker; a leg not yet dispatched is pending.
          items:
            $ref: '#/components/schemas/WorkflowTopologyLeg'
    WorkflowRunPage:
      x-go-generate: true
      type: object
      required:
        - runs
      description: Stable newest-first keyset page of workflow-run history.
      additionalProperties: false
      properties:
        runs:
          type: array
          items:
            $ref: '#/components/schemas/WorkflowRun'
        next_cursor:
          type: string
          description: Opaque cursor for the next older page, bound to this session and status filter.
    WorkerListResponse:
      x-go-generate: true
      type: object
      required:
        - workers
      additionalProperties: false
      properties:
        next_cursor:
          type: string
        workers:
          type: array
          items:
            $ref: '#/components/schemas/WorkerTask'
    OverlayMergeSharedPath:
      x-go-generate: true
      type: object
      required:
        - path
        - worker_ids
      additionalProperties: false
      properties:
        path:
          type: string
        worker_ids:
          x-go-name: WorkerIDs
          type: array
          items:
            type: string
            format: uuid
    OverlayMergePlanEntry:
      x-go-generate: true
      type: object
      required:
        - worker_id
      additionalProperties: false
      properties:
        worker_id:
          type: string
          format: uuid
        agent_type:
          type: string
        changed_paths:
          type: array
          items:
            type: string
        clean_paths:
          type: array
          items:
            type: string
        conflict_paths:
          type: array
          items:
            type: string
        promote_order:
          $ref: '#/components/schemas/WorkerPromoteOrderKind'
        promote_after:
          type: array
          items:
            type: string
            format: uuid
        blocked_by:
          type: array
          items:
            type: string
            format: uuid
        preview_stale:
          type: boolean
    OverlayMergePlan:
      x-go-generate: true
      type: object
      required:
        - pending_count
      additionalProperties: false
      properties:
        pending_count:
          type: integer
          minimum: 0
        promote_sequence:
          type: array
          items:
            type: string
        shared_paths:
          type: array
          items:
            $ref: '#/components/schemas/OverlayMergeSharedPath'
        entries:
          type: array
          items:
            $ref: '#/components/schemas/OverlayMergePlanEntry'
    CacheSavings:
      x-go-generate: true
      type: object
      required:
        - estimated_nano_usd
        - unpriced_tokens
      additionalProperties: false
      description: Same-token input-price comparison, including cache-write premiums. May be negative; excludes unpriced cache tokens and is not a prediction of future savings.
      properties:
        estimated_nano_usd:
          x-go-type: int64
          type: integer
        unpriced_tokens:
          type: integer
          minimum: 0
    CostEstimateCoverage:
      type: string
      description: 'What estimated_nano_usd covers, stamped by the host from the call receipts. complete: every incurred token was reported and priced, so the figure is the whole estimate. lower_bound: some usage was reported without a price, or a charged provider call ended without a usage report, so the true estimate is at least the figure. unpriced: usage was recorded but nothing could be priced, so the figure is meaningless and only tokens are known. Den renders the amount and the coverage as separate facts; a coverage qualifier is never prefixed onto the number.'
      enum:
        - complete
        - lower_bound
        - unpriced
    CostPricingProvenance:
      x-go-generate: true
      type: object
      required:
        - source
      additionalProperties: false
      properties:
        source:
          type: string
          minLength: 1
          description: Feed id, live, local, or unknown.
        priced_at:
          x-go-pointer: true
          type: string
          format: date-time
          description: Newest source timestamp among this source's priced events.
    SearchBudget:
      type: string
      enum:
        - complete
        - interactive
      description: |
        Crawl budget. Empty is complete. `interactive` walks origin roots first
        with small caps.
    SearchInterpretedFilter:
      x-go-generate: true
      type: object
      required:
        - field
        - value
      properties:
        field:
          type: string
        value:
          type: string
        negated:
          type: boolean
      description: One recognized filter with its polarity preserved
    SearchReplacePreviewRequest:
      x-go-generate: true
      type: object
      required:
        - query
        - replacement
      additionalProperties: false
      properties:
        query:
          type: string
        replacement:
          type: string
        origin_project_id:
          type: string
          format: uuid
        regex:
          type: boolean
        case_sensitive:
          type: boolean
        whole_word:
          type: boolean
        include:
          type: array
          items:
            type: string
        exclude:
          type: array
          items:
            type: string
    SearchReplacePreviewState:
      type: string
      description: Preparing returns no reviewable files while discovery or refresh is pending. Ready permits reviewing the full scoped preview. Limited exposes known gaps and permits only explicit per-file review in Den.
      enum:
        - preparing
        - ready
        - limited
    SearchReplaceHunkContext:
      type: string
      enum:
        - code
        - comment_or_string
      description: Syntactic context of a replace match start. comment_or_string when the language grammar places it inside a comment or string literal; code otherwise, including when no grammar is available.
    SearchReplaceHunk:
      x-go-generate: true
      type: object
      required:
        - line
        - end_line
        - before
        - after
        - context
      additionalProperties: false
      properties:
        line:
          type: integer
          minimum: 1
          description: 1-based first line of the matched span
        end_line:
          type: integer
          minimum: 1
          description: 1-based last line of the matched span (equals line for single-line matches)
        before:
          type: string
          description: Full text of the spanned lines before the replacement
        after:
          type: string
          description: Full text of the spanned lines after the replacement
        context:
          $ref: '#/components/schemas/SearchReplaceHunkContext'
    SearchReplaceFilePreview:
      x-go-generate: true
      type: object
      required:
        - root_id
        - path
        - sha256
        - hunks
      additionalProperties: false
      properties:
        root_id:
          type: string
          format: uuid
        path:
          type: string
        sha256:
          type: string
        hunks:
          type: array
          items:
            $ref: '#/components/schemas/SearchReplaceHunk'
    SearchReplacePreviewResponse:
      x-go-generate: true
      type: object
      required:
        - state
        - files
        - truncated
        - issues
      additionalProperties: false
      properties:
        state:
          $ref: '#/components/schemas/SearchReplacePreviewState'
        files:
          type: array
          items:
            $ref: '#/components/schemas/SearchReplaceFilePreview'
        truncated:
          type: boolean
          description: A file or hunk limit bounded the preview.
        issues:
          type: array
          items:
            $ref: '#/components/schemas/SearchIssue'
    SearchReplaceApplyFile:
      x-go-generate: true
      type: object
      required:
        - root_id
        - path
        - sha256
        - hunks
      additionalProperties: false
      properties:
        root_id:
          type: string
          format: uuid
        path:
          type: string
        sha256:
          type: string
        hunks:
          type: array
          items:
            type: integer
            minimum: 0
          description: 0-based indices into the preview hunk list for this file
    SearchReplaceApplyRequest:
      x-go-generate: true
      type: object
      required:
        - operation_id
        - query
        - replacement
        - origin_project_id
        - files
      additionalProperties: false
      properties:
        operation_id:
          type: string
          format: uuid
          description: Client-generated idempotency key for the complete multi-file replace command; an exact retry returns the original outcome and conflicting reuse returns 409 idempotency_conflict
        query:
          type: string
        replacement:
          type: string
        origin_project_id:
          type: string
          format: uuid
        regex:
          type: boolean
        case_sensitive:
          type: boolean
        whole_word:
          type: boolean
        include:
          type: array
          items:
            type: string
        exclude:
          type: array
          items:
            type: string
        files:
          type: array
          items:
            $ref: '#/components/schemas/SearchReplaceApplyFile'
    SearchReplaceFileOutcome:
      x-go-generate: true
      type: object
      required:
        - root_id
        - path
        - applied
        - skipped
      additionalProperties: false
      properties:
        root_id:
          type: string
          format: uuid
        path:
          type: string
        applied:
          type: boolean
        skipped:
          type: boolean
        reason:
          type: string
        matches:
          type: integer
    SearchReplaceApplyResponse:
      x-go-generate: true
      type: object
      required:
        - files
        - batch_id
      additionalProperties: false
      properties:
        files:
          type: array
          items:
            $ref: '#/components/schemas/SearchReplaceFileOutcome'
        batch_id:
          type: string
          format: uuid
          description: Source-ledger batch shared by every applied file; used for one-click revert
    AttentionClass:
      type: string
      enum:
        - needs_you
        - finished
        - running
        - error
      description: 'What a session is asking of the human, worst first. `needs_you` means the session is parked on a human answer — a pending checkpoint or an open ask latch — and cannot advance without one. A worker escalation is not in this class: `request_decision` is answered by the coordinator, so it is work in flight, not work waiting on a person. `finished` means an idle session whose newest turn completed after the person last read it: nothing is blocked, but a result is waiting. `running` means a turn is in flight. `error` means the last turn ended in a host failure. Sessions in none of these states are absent from the view — the idle majority is not attention.'
    AttentionReason:
      type: string
      enum:
        - checkpoint
        - ask
        - turn_running
        - turn_finished
        - turn_error
      description: The machine fact the class was derived from. Present so the client can label a row without re-deriving anything; never inferred from message prose.
    AttentionRow:
      x-go-generate: true
      type: object
      required:
        - session_id
        - project_id
        - class
        - reason
        - since_at
      additionalProperties: false
      properties:
        session_id:
          type: string
          format: uuid
        project_id:
          type: string
          format: uuid
        project_name:
          type: string
          description: Project display name; omitted while the project is still unnamed.
        title:
          type: string
          description: Session display title; omitted until set.
        class:
          $ref: '#/components/schemas/AttentionClass'
        reason:
          $ref: '#/components/schemas/AttentionReason'
        since_at:
          type: string
          format: date-time
          description: When the session entered this class, as the host best knows it — checkpoint or ask creation time for `needs_you`, the turn's completion time for `finished`, last session update otherwise. Clients sort on class first and use this only to break ties and render an age.
    AttentionView:
      x-go-generate: true
      type: object
      required:
        - rows
      additionalProperties: false
      properties:
        rows:
          type: array
          description: 'Every non-idle session across every project, worst class first. A device-wide view: it tells the user about the projects they are not currently looking at.'
          items:
            $ref: '#/components/schemas/AttentionRow'
    CodeScanPage:
      x-go-generate: true
      type: object
      required:
        - scans
      additionalProperties: false
      description: Stable newest-first keyset page of project scan history.
      properties:
        scans:
          type: array
          items:
            $ref: '#/components/schemas/CodeScan'
        next_cursor:
          type: string
          description: Opaque cursor for the next older page, bound to the project root set.
    McpProviderListResponse:
      x-go-generate: true
      type: object
      required:
        - providers
      additionalProperties: false
      properties:
        providers:
          type: array
          items:
            $ref: '#/components/schemas/McpProvider'
    McpToolListResponse:
      x-go-generate: true
      type: object
      required:
        - tools
      additionalProperties: false
      properties:
        tools:
          type: array
          items:
            $ref: '#/components/schemas/McpToolInfo'
    ProviderEndpointStyle:
      type: string
      enum:
        - url
        - region
        - derived
      description: How base_url is interpreted and presented for this provider kind.
    ProviderFeatures:
      x-go-generate: true
      type: object
      description: Transport-wide controls. Model assignment uses ProviderModelMeta.capabilities instead.
      required:
        - tool_calls
        - thinking
        - prompt_cache
      additionalProperties: false
      properties:
        tool_calls:
          type: boolean
          description: True when the AI provider can make tool/function calls.
        thinking:
          type: boolean
          description: True when the AI provider exposes a reasoning/thinking control.
        prompt_cache:
          type: string
          enum:
            - explicit_breakpoints
            - automatic_prefix
            - local_kv
            - none
          description: How the driver expresses prompt prefix caching on the wire. See docs/session.md § Prompt caching.
    ThinkingBudgetRange:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - min
      properties:
        min:
          type: integer
          minimum: 1
        max:
          type: integer
          minimum: 1
    ThinkingCapabilities:
      x-go-generate: true
      type: object
      required:
        - state
      additionalProperties: false
      properties:
        state:
          type: string
          enum:
            - unknown
            - unsupported
            - supported
        efforts:
          type: array
          items:
            type: string
        can_enable:
          type: boolean
        can_disable:
          type: boolean
        budget:
          x-go-pointer: true
          $ref: '#/components/schemas/ThinkingBudgetRange'
        default_effort:
          type: string
        source:
          type: string
      description: Native thinking controls resolved by the host from provider configuration, model discovery, and model-family rules, constrained by transport support.
    ProviderCapabilityEvidence:
      x-go-generate: true
      type: object
      required:
        - state
      additionalProperties: false
      properties:
        state:
          type: string
          enum:
            - unknown
            - supported
            - unsupported
        sources:
          type: array
          items:
            type: string
    ProviderModelCapabilities:
      x-go-generate: true
      type: object
      required:
        - chat
        - streaming
        - tools
        - vision
        - reasoning
        - structured_output
        - prompt_caching
      additionalProperties: false
      properties:
        chat:
          $ref: '#/components/schemas/ProviderCapabilityEvidence'
        streaming:
          $ref: '#/components/schemas/ProviderCapabilityEvidence'
        tools:
          $ref: '#/components/schemas/ProviderCapabilityEvidence'
        vision:
          $ref: '#/components/schemas/ProviderCapabilityEvidence'
        reasoning:
          $ref: '#/components/schemas/ProviderCapabilityEvidence'
        structured_output:
          $ref: '#/components/schemas/ProviderCapabilityEvidence'
        prompt_caching:
          description: Whether the route caches prompt prefixes on request markers; a proxy such as LiteLLM reports it per model
          $ref: '#/components/schemas/ProviderCapabilityEvidence'
    ModelRoleEligibility:
      x-go-generate: true
      type: object
      required:
        - state
        - code
        - reason
        - selectable
      additionalProperties: false
      properties:
        selectable:
          type: boolean
          description: Host assignment decision. Unknown tool support does not block an otherwise chat-capable model.
        state:
          type: string
          enum:
            - eligible
            - unverified
            - incompatible
        code:
          type: string
        reason:
          type: string
        rule_id:
          type: string
          pattern: ^[a-zA-Z0-9_.-]+$
    ModelRoleEligibilitySet:
      x-go-generate: true
      type: object
      required:
        - coordinator
        - agent_pool
        - lite
      additionalProperties: false
      properties:
        coordinator:
          $ref: '#/components/schemas/ModelRoleEligibility'
        agent_pool:
          $ref: '#/components/schemas/ModelRoleEligibility'
        lite:
          $ref: '#/components/schemas/ModelRoleEligibility'
    ProviderUsageQuota:
      x-go-generate: true
      type: object
      required:
        - unit
        - used
        - limit
      additionalProperties: false
      properties:
        unit:
          type: string
          description: Provider-native usage unit (e.g. neurons for Cloudflare Workers AI).
        used:
          type: number
          description: Consumption in unit for the current quota window.
        limit:
          type: number
          description: Included allowance in unit for the current quota window.
        provider_daily_allowance:
          type: boolean
          description: |
            True when the provider publishes a recurring daily included allowance (for example Cloudflare Workers AI daily neurons) rather than a hard lifetime cap.
        resets_at:
          x-go-type: string
          type: string
          format: date-time
          description: RFC3339 timestamp when the quota window resets.
        overage_nano_usd:
          x-go-pointer: true
          x-go-name: OverageNanoUSD
          type: integer
          format: int64
          description: Estimated USD overage in nano-USD for usage above the included daily allowance when applicable.
    ProviderListResponse:
      x-go-generate: true
      type: object
      required:
        - providers
      additionalProperties: false
      properties:
        providers:
          type: array
          items:
            $ref: '#/components/schemas/ProviderMeta'
    ProviderModelConfig:
      x-go-generate: true
      type: object
      required:
        - id
      additionalProperties: false
      properties:
        id:
          type: string
          pattern: ^[a-zA-Z0-9_.-]+$
        input_per_1k_nano_usd:
          x-go-pointer: true
          x-go-name: InputPer1KNanoUSD
          type: integer
          format: int64
        output_per_1k_nano_usd:
          x-go-pointer: true
          x-go-name: OutputPer1KNanoUSD
          type: integer
          format: int64
        priced_as:
          type: string
        context_length:
          type: integer
        capabilities:
          x-go-pointer: true
          $ref: '#/components/schemas/ProviderModelCapabilities'
    ProviderKindTemplate:
      x-go-generate: true
      type: object
      required:
        - kind
        - label
        - base_url
        - requires_api_key
      additionalProperties: false
      properties:
        kind:
          type: string
        label:
          type: string
        base_url:
          type: string
        endpoint_style:
          x-go-type: string
          $ref: '#/components/schemas/ProviderEndpointStyle'
        requires_api_key:
          type: boolean
          description: |
            True when instances of this kind need a Settings-saved API key.
        ambient_auth:
          type: string
          description: |
            This kind's ambient credential chain when it has one (aws-sdk-chain, google-adc). Present with requires_api_key true means the user may choose between a stored key (default) and the ambient chain; present with requires_api_key false means the kind is ambient-only.
        platforms:
          type: array
          description: |
            Product host platforms this kind supports (macos, windows, linux). Empty or omitted means all platforms. Ship providers.yaml is SSOT.
          items:
            type: string
            enum:
              - macos
              - windows
              - linux
    ProviderKindListResponse:
      x-go-generate: true
      type: object
      required:
        - kinds
      additionalProperties: false
      properties:
        kinds:
          type: array
          items:
            $ref: '#/components/schemas/ProviderKindTemplate'
    WebResearchProviderRole:
      type: string
      enum:
        - results
        - seeds
    WebResearchIndexHealth:
      x-go-generate: true
      type: object
      required:
        - writes_applied
        - writes_dropped
        - writes_failed
        - queue_high_water
        - last_evict_ms
        - last_evict_rows
        - last_evict_bytes
        - last_search_ms
        - max_search_ms
      additionalProperties: false
      properties:
        writes_applied:
          type: integer
          format: int64
        writes_dropped:
          type: integer
          format: int64
        writes_failed:
          type: integer
          format: int64
        queue_high_water:
          type: integer
          format: int64
        last_evict_ms:
          type: integer
          format: int64
        last_evict_rows:
          type: integer
          format: int64
        last_evict_bytes:
          type: integer
          format: int64
        last_search_ms:
          type: integer
          format: int64
        max_search_ms:
          type: integer
          format: int64
    WebResearchWarmActivity:
      x-go-generate: true
      type: object
      required:
        - at
        - trigger
      additionalProperties: false
      properties:
        at:
          type: string
        trigger:
          type: string
        tier:
          type: string
        topic:
          type: string
        hosts:
          type: array
          items:
            type: string
        pages:
          type: integer
        duration_ms:
          type: integer
          format: int64
        skip_reason:
          type: string
        session_id:
          type: string
          format: uuid
    WebResearchIndexStatus:
      x-go-generate: true
      type: object
      required:
        - available
        - docs
        - hosts
        - bytes
        - verified
        - warmed
        - warm_hits
        - warming
        - health
        - activity
      additionalProperties: false
      properties:
        available:
          type: boolean
        docs:
          type: integer
          format: int64
        hosts:
          type: integer
          format: int64
        bytes:
          type: integer
          format: int64
        verified:
          type: integer
          format: int64
        warmed:
          type: integer
          format: int64
        warm_hits:
          type: integer
          format: int64
        warming:
          type: boolean
        health:
          $ref: '#/components/schemas/WebResearchIndexHealth'
        activity:
          type: array
          items:
            $ref: '#/components/schemas/WebResearchWarmActivity'
    ThinkingOverride:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - provider_id
        - model
        - mode
      properties:
        provider_id:
          type: string
          pattern: ^[a-z0-9_-]+$
        model:
          type: string
          minLength: 1
        mode:
          type: string
          enum:
            - application
            - fixed
        effort:
          type: string
          pattern: ^[a-z0-9_-]{1,32}$
        enabled:
          type: boolean
          x-go-pointer: true
        budget_tokens:
          type: integer
          minimum: 1
          x-go-pointer: true
      description: Fixed requires exactly one native effort, enabled flag, or token budget. Application carries no controls and explicitly restores application behavior.
    ApprovalRuleScope:
      type: string
      enum:
        - device
        - project
    ManagedApprovalRule:
      x-go-generate: true
      type: object
      required:
        - category
        - pattern
        - effect
        - unit_id
        - pack_id
        - scope
      additionalProperties: false
      properties:
        category:
          $ref: '#/components/schemas/ApprovalCategory'
        pattern:
          type: string
        effect:
          $ref: '#/components/schemas/ApprovalEffect'
        unit_id:
          type: string
          pattern: ^[a-z0-9_-]+$
        pack_id:
          type: string
          pattern: ^[a-z0-9_-]+$
        scope:
          $ref: '#/components/schemas/ApprovalRuleScope'
    SocketCapabilityAuthority:
      type: string
      enum:
        - outside_sandbox_daemon
    AskQuiet:
      x-go-generate: true
      type: object
      required:
        - id
        - chat_session_id
        - key
        - label
        - suppressed
        - created_at
      additionalProperties: false
      description: |
        One chat-keyed ask suppression (not authority). Listed beside grants by GET /v1/approval-grants; revoked via the same bulk revoke endpoint using quiet_ ids.
      properties:
        elevated_effects:
          description: Host-classified elevated authority; empty for ordinary approvals.
          type: array
          items:
            $ref: '#/components/schemas/ElevatedAccessEffect'
        id:
          type: string
          pattern: ^[a-z0-9_-]+$
          description: Opaque id with quiet_ prefix.
        chat_session_id:
          type: string
          format: uuid
        key:
          type: string
          description: Host quiet key (reason segment, plus seam/host for secrets).
        label:
          type: string
        expires_at:
          x-go-pointer: true
          type: string
          format: date-time
          description: Absent for the chat rung (until the chat is deleted or the quiet is revoked).
        suppressed:
          type: integer
          minimum: 0
          description: Asks skipped while this quiet was live.
        created_at:
          type: string
          format: date-time
        session_title:
          type: string
          description: Root chat title when known, for Saved approvals grouping.
    RevokeApprovalGrantsRequest:
      x-go-generate: true
      type: object
      required:
        - ids
      additionalProperties: false
      description: |
        One bulk revoke for the Saved approvals group actions (revoke all, clear expired, revoke missing). Each id resolves independently. Accepts grant_ lease ids and quiet_ ask-quiet ids.
      properties:
        ids:
          x-go-name: IDs
          type: array
          minItems: 1
          maxItems: 256
          items:
            type: string
    ApprovalGrantRevokeResult:
      x-go-generate: true
      type: object
      required:
        - id
        - revoked
      additionalProperties: false
      properties:
        id:
          type: string
          pattern: ^[a-z0-9_-]+$
        revoked:
          type: boolean
          description: True only when a grant with this id existed and was removed.
        code:
          $ref: '#/components/schemas/ApiErrorCode'
        message:
          type: string
          description: Host copy when not revoked; absent on success. Branch on code.
    RevokeApprovalGrantsResponse:
      x-go-generate: true
      type: object
      required:
        - results
      additionalProperties: false
      properties:
        results:
          type: array
          items:
            $ref: '#/components/schemas/ApprovalGrantRevokeResult'
    ApprovalRecentAskRow:
      x-go-generate: true
      type: object
      required:
        - gate
        - asks
        - allowed
        - denied
      additionalProperties: false
      properties:
        gate:
          $ref: '#/components/schemas/ApprovalGate'
        asks:
          type: integer
          minimum: 1
          description: Cards raised for this reason in the window.
        allowed:
          type: integer
          minimum: 0
        denied:
          type: integer
          minimum: 0
          description: Denied, expired, or canceled.
        last_ask_at:
          x-go-pointer: true
          type: string
          format: date-time
        subjects:
          type: array
          maxItems: 8
          items:
            type: string
          description: Redaction-safe subject titles, most recent first.
        host_pattern:
          type: string
          description: |
            When every subject in the row was a tunnel or request to hosts under one registrable site, the family pattern a Settings grant could lease. Absent otherwise.
    ApprovalRecentAsksResponse:
      x-go-generate: true
      type: object
      required:
        - window_days
        - since_at
        - asks
      additionalProperties: false
      properties:
        window_days:
          type: integer
          minimum: 1
        since_at:
          type: string
          format: date-time
        asks:
          type: array
          items:
            $ref: '#/components/schemas/ApprovalRecentAskRow'
    DetectionPackRejectedRow:
      x-go-generate: true
      type: object
      required:
        - code
      additionalProperties: false
      properties:
        id:
          type: string
          pattern: ^[a-z0-9_-]+$
          description: The pack id the row named. Absent when the row omitted one.
        code:
          type: string
          description: |
            Why the row was refused, from a closed set shared with the scanner catalogue: project_unknown_id, project_fields_forbidden, project_unreadable, duplicate_id, invalid_entry. Clients branch on this rather than on detail.
        detail:
          type: string
          description: What was wrong, for display beside the row.
    FileSummariesSettingsResponse:
      x-go-generate: true
      type: object
      required:
        - enabled
      additionalProperties: false
      properties:
        enabled:
          type: boolean
          description: |
            Device-wide File summaries switch. When false, briefing requests are rejected, running briefing work is canceled, and stored briefings are deleted.
    UpdateFileSummariesSettingsRequest:
      x-go-generate: true
      type: object
      additionalProperties: false
      properties:
        enabled:
          type: boolean
    PowerSettingsResponse:
      x-go-generate: true
      type: object
      required:
        - keep_awake_while_working
        - supported
        - inhibiting
        - active_work_count
      additionalProperties: false
      properties:
        keep_awake_while_working:
          type: boolean
          description: Device preference for preventing macOS idle sleep while authoritative host work is active.
        supported:
          type: boolean
          description: Whether this host supports an idle-sleep assertion.
        inhibiting:
          type: boolean
          description: Whether the host currently holds the assertion.
        active_work_count:
          type: integer
          minimum: 0
          description: Number of distinct active host work leases keeping the assertion requested.
        last_error:
          type: string
          description: Most recent assertion failure, omitted after a successful acquisition.
    UpdatePowerSettingsRequest:
      x-go-generate: true
      type: object
      additionalProperties: false
      properties:
        keep_awake_while_working:
          type: boolean
    VerifySuggestionState:
      type: string
      description: Derived per-project state of the "set a test command" suggestion.
      enum:
        - accepted
        - suggest
        - waiting
        - dismissed
        - unknown
    DismissVerifySettingsRequest:
      x-go-generate: true
      type: object
      required:
        - dismissed
      additionalProperties: false
      properties:
        dismissed:
          type: boolean
    SourceVerify:
      type: string
      description: |
        How much of the tree a scan's source publication re-reads. stat reuses a file's prior hash whenever its size, mode, and modification time all still match, which keeps publishing proportional to what changed. content re-reads every file, closing the one case stat cannot see — a file rewritten to the same size and mode inside a single modification timestamp — at the cost of reading the whole tree on every scan.
      enum:
        - stat
        - content
    LocalDataBucketId:
      type: string
      description: Closed set of rebuildable local-data bucket ids.
      enum:
        - web_index
        - source_observations
        - source_catalog
        - fetch_cache
        - osv_cache
        - modelfeed
        - pricing_cache
        - browser_cache
        - extension_cache
        - debug_logs
        - scan_scratch
        - worker_branches
        - session_scratch
    LocalDataBucketStatus:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - id
        - present
      properties:
        id:
          $ref: '#/components/schemas/LocalDataBucketId'
        present:
          type: boolean
        bytes:
          type: integer
          format: int64
          minimum: 0
          description: Best-effort on-disk size; may be omitted when unknown
    WorkspaceCacheStatus:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - id
        - source_root
        - logical_bytes
        - allocated_bytes
        - last_used_at
      properties:
        id:
          type: string
          pattern: ^[0-9a-f]{16}$
        source_root:
          type: string
        logical_bytes:
          type: integer
          format: int64
          minimum: 0
        allocated_bytes:
          type: integer
          format: int64
          minimum: 0
          description: Best-effort allocated bytes retained by the immutable bridge cache
        last_used_at:
          type: string
          format: date-time
    LocalDataStatus:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - buckets
        - workspace_caches
      properties:
        buckets:
          type: array
          items:
            $ref: '#/components/schemas/LocalDataBucketStatus'
        workspace_caches:
          type: array
          items:
            $ref: '#/components/schemas/WorkspaceCacheStatus'
    HistoryRetentionRule:
      type: object
      x-go-generate: true
      additionalProperties: false
      required:
        - mode
      properties:
        mode:
          type: string
          enum:
            - forever
            - max_age
            - max_bytes
        max_age_days:
          type: integer
          format: int64
          minimum: 1
        max_bytes:
          type: integer
          format: int64
          minimum: 1
    HistoryRetentionPolicy:
      type: object
      x-go-generate: true
      additionalProperties: false
      required:
        - version
        - revision
        - suspended
        - recordings
        - checkpoints
        - source_revisions
        - scan_detail
        - receipt_detail
      properties:
        version:
          type: integer
          minimum: 1
          maximum: 1
        revision:
          type: integer
          format: int64
          minimum: 1
        suspended:
          type: boolean
        recordings:
          $ref: '#/components/schemas/HistoryRetentionRule'
        checkpoints:
          $ref: '#/components/schemas/HistoryRetentionRule'
        source_revisions:
          $ref: '#/components/schemas/HistoryRetentionRule'
        scan_detail:
          $ref: '#/components/schemas/HistoryRetentionRule'
        receipt_detail:
          $ref: '#/components/schemas/HistoryRetentionRule'
    HistoryStorageLane:
      type: object
      x-go-generate: true
      additionalProperties: false
      required:
        - id
        - stored_bytes
        - logical_bytes
        - shared_bytes
      properties:
        id:
          type: string
          pattern: ^[a-z0-9_-]+$
        stored_bytes:
          type: integer
          format: int64
          description: Retained file bytes, deduplicated within the lane. This is not exclusive filesystem allocation. The upgrade-recovery lane reports zero here because its independent snapshots may share physical extents with live files.
        logical_bytes:
          type: integer
          format: int64
          description: Uncompressed content bytes where available. For upgrade-recovery, the sum of retained snapshot file lengths, which may share physical disk space and must not be added to a physical storage total.
        shared_bytes:
          type: integer
          format: int64
    HistoryProtection:
      type: object
      x-go-generate: true
      additionalProperties: false
      required:
        - scope_type
        - scope_id
        - protected
      properties:
        scope_type:
          type: string
          enum:
            - project
            - session
        scope_id:
          type: string
          format: uuid
        protected:
          type: boolean
    HistoryStorageStatus:
      type: object
      x-go-generate: true
      additionalProperties: false
      required:
        - policy
        - lanes
        - protections
      properties:
        policy:
          $ref: '#/components/schemas/HistoryRetentionPolicy'
        lanes:
          type: array
          items:
            $ref: '#/components/schemas/HistoryStorageLane'
        protections:
          type: array
          items:
            $ref: '#/components/schemas/HistoryProtection'
    HistoryRetentionRequest:
      type: object
      x-go-generate: true
      additionalProperties: false
      properties:
        policy:
          $ref: '#/components/schemas/HistoryRetentionPolicy'
        preview_token:
          type: string
          pattern: ^[a-z0-9_-]+$
    HistoryPruneCandidate:
      type: object
      x-go-generate: true
      additionalProperties: false
      required:
        - id
        - class
        - project_id
        - created_at
        - reclaimable_bytes
      properties:
        id:
          type: string
          pattern: ^[a-z0-9_-]+$
        class:
          type: string
        project_id:
          type: string
          format: uuid
        created_at:
          type: string
          format: date-time
        reclaimable_bytes:
          type: integer
          format: int64
    HistoryRetentionPreview:
      type: object
      x-go-generate: true
      additionalProperties: false
      required:
        - token
        - policy_revision
        - candidates
        - eligible_count
        - reclaimable_bytes
        - shared_protected_bytes
        - complete
      properties:
        token:
          type: string
          pattern: ^[a-z0-9_-]+$
        policy_revision:
          type: integer
          format: int64
        candidates:
          type: array
          items:
            $ref: '#/components/schemas/HistoryPruneCandidate'
        eligible_count:
          type: integer
          format: int64
        reclaimable_bytes:
          type: integer
          format: int64
        shared_protected_bytes:
          type: integer
          format: int64
        complete:
          type: boolean
    HistoryPruneResult:
      type: object
      x-go-generate: true
      additionalProperties: false
      required:
        - removed_count
        - released_bytes
        - complete
        - preview_token
      properties:
        removed_count:
          type: integer
          format: int64
        released_bytes:
          type: integer
          format: int64
          description: Encoded body bytes whose retained references were released; physical unlink and page reclamation may finish later.
        complete:
          type: boolean
        preview_token:
          type: string
          pattern: ^[a-z0-9_-]+$
    LocalDataClearRequest:
      x-go-generate: true
      type: object
      additionalProperties: false
      minProperties: 1
      properties:
        buckets:
          type: array
          minItems: 1
          items:
            $ref: '#/components/schemas/LocalDataBucketId'
        workspace_cache_ids:
          x-go-name: WorkspaceCacheIDs
          type: array
          minItems: 1
          items:
            type: string
            pattern: ^[0-9a-f]{16}$
    LocalDataClearResult:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - id
        - ok
      properties:
        id:
          $ref: '#/components/schemas/LocalDataBucketId'
        ok:
          x-go-name: OK
          type: boolean
        code:
          $ref: '#/components/schemas/ApiErrorCode'
        message:
          type: string
          description: Host copy when ok is false; branch on code.
    WorkspaceCacheClearResult:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - id
        - ok
      properties:
        id:
          type: string
          pattern: ^[0-9a-f]{16}$
        ok:
          x-go-name: OK
          type: boolean
        code:
          $ref: '#/components/schemas/ApiErrorCode'
        message:
          type: string
          description: Host copy when ok is false; branch on code.
    LocalDataClearResponse:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - results
        - workspace_cache_results
      properties:
        results:
          type: array
          items:
            $ref: '#/components/schemas/LocalDataClearResult'
        workspace_cache_results:
          type: array
          items:
            $ref: '#/components/schemas/WorkspaceCacheClearResult'
    BackupCapabilities:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - format_version
        - schema_revision
        - max_archive_bytes
        - max_expanded_bytes
      properties:
        format_version:
          type: integer
        schema_revision:
          type: integer
        max_archive_bytes:
          type: integer
          format: int64
        max_expanded_bytes:
          type: integer
          format: int64
    BackupRestoreResult:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - restart_required
        - recovery_copy_path
      properties:
        restart_required:
          type: boolean
          description: True when the staged restore must be applied on the next launch
        recovery_copy_path:
          type: string
          description: Absolute path of the restore transaction's recovery copy
    ContributionExecutor:
      type: string
      description: Which executor dispatches a command; fixed by the action kind.
      enum:
        - den
        - host
    ContributionInvocationScope:
      type: string
      description: Which invoke route a host-executed command uses. Den-executed commands are "den" and never call an invoke route.
      enum:
        - den
        - project
        - session
    ContributionActionKind:
      type: string
      enum:
        - editor_action
        - workflow_start
        - mcp_tool
        - composer_prefill
        - navigate
        - external_link
        - native_ui
        - operation
    ContributionCondition:
      x-go-generate: true
      type: object
      description: Structured predicate tree over the closed fact vocabulary. Exactly one of all, any, not, or fact is set per node.
      additionalProperties: false
      properties:
        all:
          type: array
          items:
            $ref: '#/components/schemas/ContributionCondition'
        any:
          type: array
          items:
            $ref: '#/components/schemas/ContributionCondition'
        not:
          x-go-pointer: true
          $ref: '#/components/schemas/ContributionCondition'
        fact:
          type: string
        is:
          type: string
    ContributionInputField:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - id
        - type
      properties:
        id:
          type: string
          pattern: ^[a-zA-Z0-9_.-]+$
        title:
          type: string
        type:
          type: string
          enum:
            - boolean
            - string
            - number
            - enum
            - string_list
            - project_path
        description:
          type: string
        required:
          type: boolean
        values:
          type: array
          items:
            type: string
        default: {}
        min:
          x-go-pointer: true
          type: number
        max:
          x-go-pointer: true
          type: number
    ContributionChoice:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - id
        - label
      properties:
        id:
          type: string
          pattern: ^[a-zA-Z0-9_.-]+$
        label:
          type: string
        description:
          type: string
        detail:
          type: string
    ContributionChoiceSource:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - requirement
        - tool
      properties:
        requirement:
          type: string
        tool:
          type: string
        inputs:
          type: object
          additionalProperties:
            type: string
    ContributionInteractionPredicate:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - step
        - is
      properties:
        step:
          type: string
        is: {}
    ContributionInteractionStep:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - id
        - title
        - kind
      properties:
        id:
          type: string
          pattern: ^[a-zA-Z0-9_.-]+$
        title:
          type: string
        description:
          type: string
        kind:
          type: string
          enum:
            - string
            - number
            - boolean
            - choice
            - project_path
            - confirmation
        required:
          type: boolean
        values:
          type: array
          items:
            $ref: '#/components/schemas/ContributionChoice'
        min:
          x-go-pointer: true
          type: number
        max:
          x-go-pointer: true
          type: number
        multiple:
          type: boolean
        source:
          x-go-pointer: true
          $ref: '#/components/schemas/ContributionChoiceSource'
        if:
          x-go-pointer: true
          $ref: '#/components/schemas/ContributionInteractionPredicate'
    ContributionInteraction:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - steps
      properties:
        steps:
          type: array
          items:
            $ref: '#/components/schemas/ContributionInteractionStep'
    ContributionCommand:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - id
        - provider
        - title
        - executor
        - invocation
        - action_kind
        - icon
        - result_treatment
      properties:
        id:
          type: string
          pattern: ^[a-zA-Z0-9_-][a-zA-Z0-9_.-]*(/[a-zA-Z0-9_-][a-zA-Z0-9_.-]*)+:[a-z0-9]+(-[a-z0-9]+)*$
        provider:
          type: string
        title:
          type: string
        category:
          type: string
        scope:
          type: string
        keywords:
          type: array
          items:
            type: string
        icon:
          type: string
        palette:
          x-go-pointer: true
          type: boolean
          description: Whether Crossbar offers this command. Absent means yes.
        executor:
          x-go-type: string
          $ref: '#/components/schemas/ContributionExecutor'
        invocation:
          x-go-type: string
          $ref: '#/components/schemas/ContributionInvocationScope'
        action_kind:
          x-go-type: string
          $ref: '#/components/schemas/ContributionActionKind'
        handler_id:
          type: string
          pattern: ^[a-zA-Z0-9_.-]+$
          description: Stock Den handler id; present only for native_ui.
        action_ref:
          type: string
          description: Referenced editor-action contribution id.
        when:
          x-go-pointer: true
          $ref: '#/components/schemas/ContributionCondition'
        enablement:
          x-go-pointer: true
          $ref: '#/components/schemas/ContributionCondition'
        input:
          type: array
          items:
            $ref: '#/components/schemas/ContributionInputField'
        interaction:
          x-go-pointer: true
          $ref: '#/components/schemas/ContributionInteraction'
        result_treatment:
          type: string
          enum:
            - output
            - discard
            - effect
            - receipt
    ContributionMenu:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - id
        - slot
        - command
      properties:
        id:
          type: string
          pattern: ^[a-zA-Z0-9_-][a-zA-Z0-9_.-]*(/[a-zA-Z0-9_-][a-zA-Z0-9_.-]*)+:[a-z0-9]+(-[a-z0-9]+)*$
        slot:
          type: string
        command:
          type: string
        group:
          type: string
        order:
          type: integer
        label:
          type: string
        when:
          x-go-pointer: true
          $ref: '#/components/schemas/ContributionCondition'
        state:
          description: Condition the toggle reflects. With state_label the item reads state_label while it holds; without it the item carries a checkmark.
          x-go-pointer: true
          $ref: '#/components/schemas/ContributionCondition'
        state_label:
          type: string
    ContributionKeybinding:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - id
        - command
        - scope
        - bindings
      properties:
        id:
          type: string
          pattern: ^[a-zA-Z0-9_-][a-zA-Z0-9_.-]*(/[a-zA-Z0-9_-][a-zA-Z0-9_.-]*)+:[a-z0-9]+(-[a-z0-9]+)*$
        command:
          type: string
        scope:
          type: string
        allow_in_input:
          type: boolean
        bindings:
          type: object
          additionalProperties:
            type: array
            items:
              type: string
        when:
          x-go-pointer: true
          $ref: '#/components/schemas/ContributionCondition'
    ContributionBindingDefault:
      x-go-generate: true
      type: object
      description: One resolved default chord per platform, scope, and chord identity. A missing active id means equal-provenance candidates deactivated each other.
      additionalProperties: false
      required:
        - platform
        - scope
        - chord
        - candidates
      properties:
        platform:
          type: string
        scope:
          type: string
        chord:
          type: string
        active:
          type: string
        candidates:
          type: array
          items:
            type: string
    ContributionEditorAction:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - id
        - title
        - target_kind
        - preset
      properties:
        id:
          type: string
          pattern: ^[a-zA-Z0-9_-][a-zA-Z0-9_.-]*(/[a-zA-Z0-9_-][a-zA-Z0-9_.-]*)+:[a-z0-9]+(-[a-z0-9]+)*$
        title:
          type: string
        target_kind:
          type: string
        target_required:
          type: boolean
        preset:
          type: string
    ContributionSyntaxStyle:
      x-go-generate: true
      type: object
      description: One compiled syntax scope. Colors are literal; the three flags are the complete typography surface of a theme.
      additionalProperties: false
      required:
        - color
      properties:
        color:
          type: string
        italic:
          type: boolean
        bold:
          type: boolean
        underline:
          type: boolean
    ContributionIconNode:
      x-go-generate: true
      type: object
      description: One shape in a themed glyph, already validated against the host's closed element and attribute vocabularies. Den builds an element from this and sets each attribute individually — no authored string ever becomes markup, so there is no sanitizer to get wrong.
      additionalProperties: false
      required:
        - tag
      properties:
        tag:
          type: string
          enum:
            - path
            - circle
            - rect
            - ellipse
            - line
            - polyline
            - polygon
            - g
        attrs:
          type: object
          description: Geometry attributes. Paint is currentColor or none.
          additionalProperties:
            type: string
        children:
          type: array
          description: Non-empty only for a group.
          items:
            $ref: '#/components/schemas/ContributionIconNode'
    ContributionWindowColors:
      x-go-generate: true
      type: object
      additionalProperties: false
      description: Theme-authored OKLCH color family for editor windows. Den samples this recipe by stable window identity and enforces display contrast.
      required:
        - main
        - anchors
        - hue_spread
        - chroma_min
        - chroma_max
      properties:
        main:
          type: string
          pattern: ^#[0-9a-fA-F]{6}$
        anchors:
          type: array
          minItems: 1
          maxItems: 12
          items:
            type: string
            pattern: ^#[0-9a-fA-F]{6}$
        hue_spread:
          type: number
          minimum: 0
          maximum: 180
        chroma_min:
          type: number
          minimum: 0
          maximum: 0.4
        chroma_max:
          type: number
          minimum: 0
          maximum: 0.4
    ContributionAgentColors:
      x-go-generate: true
      type: object
      additionalProperties: false
      description: Theme-authored color family for agent chats. Den gives each chat a lightness step around one muted hue and enforces display contrast.
      required:
        - main
        - lightness_step
        - chroma
      properties:
        main:
          type: string
          pattern: ^#[0-9a-fA-F]{6}$
        lightness_step:
          type: number
          minimum: 0.02
          maximum: 0.25
        chroma:
          type: number
          minimum: 0
          maximum: 0.12
    ContributionIconStroke:
      x-go-generate: true
      type: object
      description: The theme's handwriting, applied to every stroked glyph including the ones it never redrew. Weight multiplies each slot's host-defined width, so the product's relative weights survive a restyle. Stroke never contributes to layout, so none of this can move anything.
      additionalProperties: false
      required:
        - weight
        - cap
        - join
      properties:
        weight:
          type: number
          minimum: 0.5
          maximum: 2
        cap:
          type: string
          enum:
            - butt
            - round
            - square
        join:
          type: string
          enum:
            - miter
            - round
            - bevel
    ContributionTheme:
      x-go-generate: true
      x-go-field-order:
        - id
        - name
        - appearance
        - tokens
        - syntax
        - icons
        - icon_stroke
        - window_colors
        - agent_colors
        - logomark
        - provider
      type: object
      description: One compiled theme. Values are total — every base token and syntax scope resolved through host derivations and the scope fold. Den applies these, and generates window colors from the resolved palette recipe.
      additionalProperties: false
      required:
        - id
        - name
        - appearance
        - tokens
        - syntax
        - logomark
        - icon_stroke
        - window_colors
        - agent_colors
      properties:
        id:
          type: string
          pattern: ^[a-zA-Z0-9_-][a-zA-Z0-9_.-]*(/[a-zA-Z0-9_-][a-zA-Z0-9_.-]*)+:[a-z0-9]+(-[a-z0-9]+)*$
        name:
          type: string
        provider:
          type: string
          description: Pack id that declared this theme.
        appearance:
          type: string
          enum:
            - light
            - dark
        tokens:
          type: object
          description: Base-plane token id to literal color.
          additionalProperties:
            type: string
        syntax:
          type: object
          additionalProperties:
            $ref: '#/components/schemas/ContributionSyntaxStyle'
        icons:
          type: object
          description: 'Glyphs this theme overrides, keyed by host icon slot. Partial by design: an absent slot keeps the host glyph, because a glyph has no derivation. The host defines the frame — viewBox, rendered size, stroke weight, paint — so a themed glyph can never resize a control or carry its own color.'
          additionalProperties:
            type: array
            items:
              $ref: '#/components/schemas/ContributionIconNode'
        window_colors:
          $ref: '#/components/schemas/ContributionWindowColors'
        agent_colors:
          $ref: '#/components/schemas/ContributionAgentColors'
        icon_stroke:
          $ref: '#/components/schemas/ContributionIconStroke'
        logomark:
          type: string
          enum:
            - shown
            - hidden
          description: Whether the product logomark paints under this theme. A theme may quiet the mark; it can never substitute one, so this carries a visibility and never artwork. Hiding reserves the mark's box, so a theme swap does not relayout the window.
    ContributionConfigurationProperty:
      x-go-generate: true
      type: object
      description: One declared setting with its effective value. `value` is desired state when present and the declared default otherwise.
      additionalProperties: false
      required:
        - id
        - type
        - description
        - scope
        - is_default
      properties:
        id:
          type: string
          pattern: ^[a-zA-Z0-9_-][a-zA-Z0-9_.-]*(/[a-zA-Z0-9_-][a-zA-Z0-9_.-]*)+:[a-z0-9]+(-[a-z0-9]+)*$
        type:
          type: string
        description:
          type: string
        default: {}
        value:
          x-go-type: any
          description: The value in force for this property.
        is_default:
          type: boolean
          description: True when no desired-state value applied and `value` is the declaration's own default.
        scope:
          type: array
          items:
            type: string
        enum:
          type: array
          items:
            type: string
        min:
          x-go-pointer: true
          type: number
        max:
          x-go-pointer: true
          type: number
    ContributionRequirement:
      x-go-generate: true
      type: object
      description: One MCP requirement joined against the captured resource generation.
      additionalProperties: false
      required:
        - id
        - provider_id
        - ready
      properties:
        id:
          type: string
          pattern: ^[a-zA-Z0-9_-][a-zA-Z0-9_.-]*(/[a-zA-Z0-9_-][a-zA-Z0-9_.-]*)+:[a-z0-9]+(-[a-z0-9]+)*$
        provider_id:
          type: string
          pattern: ^[a-zA-Z0-9_.-]+$
        purpose:
          type: string
        ready:
          type: boolean
        reason:
          type: string
          description: provider_missing, provider_disabled, provider_not_ready, or tool_missing.
        missing_tools:
          type: array
          items:
            type: string
    ContributionSearchSource:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - id
        - provider
        - label
        - prefix
        - requirement
        - tool
        - min_query_length
        - max_results
        - ready
        - activation_command
      properties:
        id:
          type: string
          pattern: ^[a-zA-Z0-9_-][a-zA-Z0-9_.-]*(/[a-zA-Z0-9_-][a-zA-Z0-9_.-]*)+:[a-z0-9]+(-[a-z0-9]+)*$
        provider:
          type: string
        label:
          type: string
        prefix:
          type: string
        requirement:
          type: string
        tool:
          type: string
        min_query_length:
          type: integer
        max_results:
          type: integer
        ready:
          type: boolean
        disabled_reason:
          type: string
        activation_command:
          type: string
    ContributionOperation:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - id
        - provider
        - action_kind
        - result_treatment
      properties:
        id:
          type: string
          pattern: ^[a-zA-Z0-9_-][a-zA-Z0-9_.-]*(/[a-zA-Z0-9_-][a-zA-Z0-9_.-]*)+:[a-z0-9]+(-[a-z0-9]+)*$
        provider:
          type: string
        input:
          type: array
          items:
            $ref: '#/components/schemas/ContributionInputField'
        output:
          type: array
          items:
            $ref: '#/components/schemas/ContributionInputField'
        action_kind:
          x-go-type: string
          $ref: '#/components/schemas/ContributionActionKind'
        action_ref:
          type: string
        result_treatment:
          type: string
          enum:
            - output
            - discard
            - effect
            - receipt
    ContributionNote:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - code
        - message
      properties:
        code:
          type: string
        message:
          type: string
        unit_id:
          type: string
          pattern: ^[a-zA-Z0-9_./%:-]+$
        pack_id:
          type: string
          pattern: ^[a-zA-Z0-9_-][a-zA-Z0-9_.-]*(/[a-zA-Z0-9_-][a-zA-Z0-9_.-]*)*$
    ContributionFrameResponse:
      x-go-generate: true
      type: object
      description: One complete projection of a captured contribution frame. Pure projection; never an independent source of truth.
      additionalProperties: false
      required:
        - frame_revision
        - commands
        - menus
        - keybindings
        - binding_defaults
        - editor_actions
        - themes
        - configuration
        - requirements
        - search_sources
        - operations
        - notes
      properties:
        frame_revision:
          type: string
        commands:
          type: array
          items:
            $ref: '#/components/schemas/ContributionCommand'
        menus:
          type: array
          items:
            $ref: '#/components/schemas/ContributionMenu'
        keybindings:
          type: array
          items:
            $ref: '#/components/schemas/ContributionKeybinding'
        binding_defaults:
          type: array
          items:
            $ref: '#/components/schemas/ContributionBindingDefault'
        editor_actions:
          type: array
          items:
            $ref: '#/components/schemas/ContributionEditorAction'
        themes:
          type: array
          items:
            $ref: '#/components/schemas/ContributionTheme'
        configuration:
          type: array
          items:
            $ref: '#/components/schemas/ContributionConfigurationProperty'
        requirements:
          type: array
          items:
            $ref: '#/components/schemas/ContributionRequirement'
        search_sources:
          type: array
          items:
            $ref: '#/components/schemas/ContributionSearchSource'
        operations:
          type: array
          items:
            $ref: '#/components/schemas/ContributionOperation'
        notes:
          type: array
          items:
            $ref: '#/components/schemas/ContributionNote'
    CommandInvokeContext:
      x-go-generate: true
      type: object
      description: Structured invocation coordinates. The host resolves and verifies these against machine state; a Den boolean is never authority.
      additionalProperties: false
      properties:
        root_id:
          type: string
          format: uuid
        path:
          type: string
        document_revision:
          type: integer
        start_line:
          type: integer
        end_line:
          type: integer
        symbol:
          type: string
        finding_id:
          type: string
          pattern: ^[A-Za-z0-9_.:-]+$
        instruction:
          type: string
          description: Human instruction for editor actions declaring an instruction target. Prompt content only; it never widens the preset boundary.
    CommandInvokeRequest:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - operation_id
        - frame_revision
      properties:
        operation_id:
          type: string
          format: uuid
          description: Idempotency receipt; a UUID minted by the caller. An exact retry replays the stored response verbatim; reusing this id for a different invocation returns 409 idempotency_conflict.
        frame_revision:
          type: string
          description: The frame the caller resolved the command from.
        context:
          x-go-pointer: true
          $ref: '#/components/schemas/CommandInvokeContext'
        args:
          type: object
          additionalProperties: {}
          description: Values for the command's declared input schema.
    CommandUIEffect:
      x-go-generate: true
      type: object
      description: Closed typed UI effect returned by host-validated presentation actions.
      additionalProperties: false
      required:
        - kind
      properties:
        kind:
          type: string
          enum:
            - composer_prefill
            - navigate
            - external_link
        text:
          type: string
        destination:
          type: string
        url:
          type: string
    CommandInvokeError:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - code
        - message
      properties:
        code:
          $ref: '#/components/schemas/ApiErrorCode'
        message:
          type: string
    CommandInvokeResponse:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - status
        - frame_revision
      properties:
        status:
          type: string
          enum:
            - completed
            - accepted
            - failed
        frame_revision:
          type: string
          description: The frame the host executed against.
        ui_effect:
          x-go-pointer: true
          $ref: '#/components/schemas/CommandUIEffect'
        message_id:
          type: string
          format: uuid
          description: The user message an accepted editor action submitted.
        workflow_run_id:
          type: string
          format: uuid
          description: Run id for accepted workflow starts.
        output:
          type: string
          description: Tool result text for completed MCP tool calls.
        error:
          x-go-pointer: true
          $ref: '#/components/schemas/CommandInvokeError'
    ContributionChoiceRequest:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - frame_revision
      properties:
        frame_revision:
          type: string
        answers:
          type: object
          additionalProperties: {}
    ContributionChoiceResponse:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - frame_revision
        - provider
        - choices
      properties:
        frame_revision:
          type: string
        provider:
          type: string
        choices:
          type: array
          items:
            $ref: '#/components/schemas/ContributionChoice'
    ContributionSearchRequest:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - frame_revision
        - query
      properties:
        frame_revision:
          type: string
        query:
          type: string
    ContributionSearchResult:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - id
        - title
      properties:
        id:
          type: string
          pattern: ^[a-zA-Z0-9_.-]+$
        title:
          type: string
        description:
          type: string
        detail:
          type: string
        kind:
          type: string
        arguments:
          type: object
          additionalProperties: {}
    ContributionSearchResponse:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - frame_revision
        - source_id
        - provider
        - results
      properties:
        frame_revision:
          type: string
        source_id:
          type: string
          pattern: ^[a-zA-Z0-9_-][a-zA-Z0-9_.-]*(/[a-zA-Z0-9_-][a-zA-Z0-9_.-]*)+:[a-z0-9]+(-[a-z0-9]+)*$
        provider:
          type: string
        results:
          type: array
          items:
            $ref: '#/components/schemas/ContributionSearchResult'
    ExtensionPackKind:
      type: string
      enum:
        - stock
        - git
        - path
    ExtensionBlockedReason:
      type: string
      enum:
        - requires
        - requires_scanners
        - compatibility
        - disabled
        - invalid
        - integrity
    ExtensionPackSummary:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - id
        - name
        - version
        - extension_api
        - installation_state
        - installation_scope
        - dependencies
        - kind
        - enabled
        - removable
        - unit_count
        - has_profile
        - contributing
        - meta_pack_ids
      properties:
        id:
          type: string
          pattern: ^[a-zA-Z0-9_-][a-zA-Z0-9_.-]*(/[a-zA-Z0-9_-][a-zA-Z0-9_.-]*)*$
        name:
          type: string
        version:
          type: string
          description: Installed canonical SemVer from extension.yaml
        version_constraint:
          type: string
          description: Root constraint from extensions.yaml; absent for transitive and stock packages
        resolved_revision:
          type: string
          description: Exact immutable Git revision from the scope lock
        integrity:
          type: string
          description: SHA-256 package tree integrity from the scope lock
        extension_api:
          type: string
          description: Host API constraint declared by the package
        installation_state:
          type: string
          enum:
            - stock
            - release
            - pinned
            - development
        installation_scope:
          type: string
          enum:
            - stock
            - device
            - transitive
        dependencies:
          type: object
          additionalProperties:
            type: string
          description: Direct dependency package ids mapped to requested SemVer constraints
        dependency_of:
          type: array
          items:
            type: string
        source:
          type: string
        ref:
          type: string
        kind:
          $ref: '#/components/schemas/ExtensionPackKind'
        enabled:
          type: boolean
        removable:
          type: boolean
        unit_count:
          type: integer
          minimum: 0
        has_profile:
          type: boolean
        blocked_reason:
          $ref: '#/components/schemas/ExtensionBlockedReason'
        unmet_requires_scanners:
          type: array
          items:
            type: string
          description: Scanner ids that must be enabled and runnable in Scanners Settings; never auto-enabled here
        contributing:
          type: boolean
        needs_reload:
          type: boolean
          description: Linked folder changed on disk; the pack still contributes current bytes until Reload from disk
        feature:
          type: string
          description: Optional feature/capability label from extension.yaml
        meta_pack_ids:
          x-go-name: MetaPackIDs
          type: array
          items:
            type: string
          description: Discovered meta-pack ids that list this pack as a member
        installed_from:
          type: string
          description: Project id that accepted this pack as a suggestion.
        referenced_by:
          type: integer
          minimum: 0
          description: How many projects currently suggest this pack.
    ExtensionMetaPackStatus:
      type: string
      enum:
        - complete
        - partial
        - inactive
    ExtensionDiagnosticSeverity:
      type: string
      description: Whether the catalog is doing what the desired state asked. `error` means content that was asked for is not in effect; `warning` means it is in effect but not as written; `info` reports a choice back. Derived from the code, never authored.
      enum:
        - error
        - warning
        - info
    ExtensionDiagnostic:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - code
        - message
        - severity
      properties:
        code:
          type: string
        message:
          type: string
        severity:
          $ref: '#/components/schemas/ExtensionDiagnosticSeverity'
        unit_id:
          type: string
          pattern: ^[a-zA-Z0-9_./%:-]+$
        pack_id:
          type: string
          pattern: ^[a-zA-Z0-9_-][a-zA-Z0-9_.-]*(/[a-zA-Z0-9_-][a-zA-Z0-9_.-]*)*$
        mcp_provider_id:
          type: string
          pattern: ^[a-zA-Z0-9_.-]+$
        scanner_id:
          type: string
          pattern: ^[a-zA-Z0-9_.-]+$
    ExtensionMetaPackSummary:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - id
        - name
        - version
        - kind
        - status
        - members
        - conflicts_with
        - extends
        - diagnostics
        - removable
      properties:
        id:
          type: string
          pattern: ^[a-zA-Z0-9_-][a-zA-Z0-9_.-]*(/[a-zA-Z0-9_-][a-zA-Z0-9_.-]*)*$
        name:
          type: string
        version:
          type: string
        kind:
          $ref: '#/components/schemas/ExtensionPackKind'
        status:
          $ref: '#/components/schemas/ExtensionMetaPackStatus'
        members:
          type: array
          items:
            type: string
        conflicts_with:
          type: array
          items:
            type: string
        extends:
          type: array
          items:
            type: string
        diagnostics:
          type: array
          items:
            $ref: '#/components/schemas/ExtensionDiagnostic'
        removable:
          type: boolean
    ExtensionUnitStatus:
      type: string
      enum:
        - loaded
        - disabled
        - conflict
        - owned
    ExtensionUnitContribution:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - pack_id
        - path
      properties:
        pack_id:
          type: string
          pattern: ^[a-zA-Z0-9_-][a-zA-Z0-9_.-]*(/[a-zA-Z0-9_-][a-zA-Z0-9_.-]*)*$
        path:
          type: string
        content:
          type: string
          description: Contribution body (UTF-8 YAML/md) for inspect panes
    ExtensionUnitSummary:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - id
        - kind
        - status
        - contributions
        - project_disable_allowed
      properties:
        project_disable_allowed:
          type: boolean
          description: Whether project scope may disable this unit under the host catalog floor.
        id:
          type: string
          pattern: ^[a-zA-Z0-9_./%:-]+$
        kind:
          type: string
        title:
          type: string
        status:
          $ref: '#/components/schemas/ExtensionUnitStatus'
        winner_pack_id:
          type: string
          pattern: ^[a-zA-Z0-9_-][a-zA-Z0-9_.-]*(/[a-zA-Z0-9_-][a-zA-Z0-9_.-]*)*$
        contributions:
          type: array
          items:
            $ref: '#/components/schemas/ExtensionUnitContribution'
    ExtensionDesiredPack:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - id
      properties:
        id:
          type: string
          pattern: ^[a-zA-Z0-9_-][a-zA-Z0-9_.-]*(/[a-zA-Z0-9_-][a-zA-Z0-9_.-]*)*$
        source:
          type: string
        version:
          type: string
          description: SemVer constraint for release selection
        ref:
          type: string
          description: Exact Git ref pin; mutually exclusive with version
        development:
          type: boolean
          description: Link an author directory; disk changes contribute immediately and mark the pack for Reload from disk
        enabled:
          type:
            - boolean
            - 'null'
        installed_from:
          type: string
          format: uuid
          description: Project id that accepted this pack as a suggestion.
    ExtensionConfigurationValue:
      description: A non-secret configuration scalar or string list
      oneOf:
        - type: boolean
        - type: number
        - type: string
        - type: array
          items:
            type: string
    ExtensionDesiredState:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - format
        - packs
        - disabled
        - own
      properties:
        format:
          type: integer
        packs:
          type: array
          items:
            $ref: '#/components/schemas/ExtensionDesiredPack'
        disabled:
          type: array
          items:
            type: string
        own:
          type: object
          additionalProperties:
            type: string
        configuration:
          x-go-type: map[string]map[string]any
          type: object
          description: Per-pack behavior values (pack id → property → value)
          additionalProperties:
            type: object
            additionalProperties:
              $ref: '#/components/schemas/ExtensionConfigurationValue'
        declined:
          type: array
          items:
            type: string
          description: Device-wide pack ids declined from project suggestions.
        yaml:
          type: string
          description: Merged desired state as YAML for Desired state tab
        scope_note:
          type: string
          description: e.g. device vs project merge note
    ExtensionsCatalogView:
      x-go-generate: true
      type: object
      additionalProperties: false
      description: One catalog generation, whole. Packs, the units they resolve to, the diagnostics against them, and the desired state that selected them come from one resolve, so no two fields can disagree about the same pack. Every mutation returns this too — a write reports the catalog in force exactly as a read does, which is what lets a client apply the result instead of asking again.
      required:
        - revision
        - packs
        - meta_packs
        - diagnostics
        - units
        - desired
        - desired_path
        - ok
      properties:
        revision:
          type: string
          description: Extension-state revision. Identifies this generation, carries into the next mutation as expected_revision, and is the ETag on reads.
        packs:
          type: array
          items:
            $ref: '#/components/schemas/ExtensionPackSummary'
        meta_packs:
          type: array
          items:
            $ref: '#/components/schemas/ExtensionMetaPackSummary'
        diagnostics:
          type: array
          items:
            $ref: '#/components/schemas/ExtensionDiagnostic'
        units:
          type: array
          description: Effective units this generation resolved
          items:
            $ref: '#/components/schemas/ExtensionUnitSummary'
        desired:
          $ref: '#/components/schemas/ExtensionDesiredState'
        desired_path:
          type: string
          description: The intent file this generation was resolved from
        conflicts:
          type: integer
          minimum: 0
        ok:
          x-go-name: OK
          type: boolean
          description: False when any unit is in conflict
    ExtensionUnitDetail:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - id
        - kind
        - status
        - contributions
        - project_disable_allowed
      properties:
        project_disable_allowed:
          type: boolean
          description: Whether project scope may disable this unit under the host catalog floor.
        id:
          type: string
          pattern: ^[a-zA-Z0-9_./%:-]+$
        kind:
          type: string
        title:
          type: string
        status:
          $ref: '#/components/schemas/ExtensionUnitStatus'
        winner_pack_id:
          type: string
          pattern: ^[a-zA-Z0-9_-][a-zA-Z0-9_.-]*(/[a-zA-Z0-9_-][a-zA-Z0-9_.-]*)*$
        contributions:
          type: array
          items:
            $ref: '#/components/schemas/ExtensionUnitContribution'
        content:
          type: string
          description: Winner body when status is loaded or owned
    ExtensionsDesiredScope:
      type: string
      description: Desired-state write scope.
      enum:
        - project
        - device
    UpdateExtensionUnitRequest:
      x-go-generate: true
      type: object
      additionalProperties: false
      description: expected_revision and at least one change.
      minProperties: 2
      required:
        - expected_revision
      properties:
        enabled:
          x-go-pointer: true
          type: boolean
        own_pack_id:
          type:
            - string
            - 'null'
          pattern: ^[a-zA-Z0-9_-][a-zA-Z0-9_.-]*(/[a-zA-Z0-9_-][a-zA-Z0-9_.-]*)*$
          description: Selected winner pack id, or null to clear the explicit own selection
        expected_revision:
          type: string
    ExtensionMutationResponse:
      x-go-generate: true
      type: object
      additionalProperties: false
      description: The catalog generation this mutation committed, plus what only a write can report. A caller applies this and is current; it never has to read back the state it just wrote.
      required:
        - view
      properties:
        view:
          $ref: '#/components/schemas/ExtensionsCatalogView'
        warnings:
          type: array
          items:
            type: string
    ExtensionInstallRequest:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - source
        - expected_revision
      properties:
        source:
          type: string
          description: Git URL, file://, or path:…
        version:
          type: string
          description: SemVer constraint; defaults to * and is mutually exclusive with ref
        ref:
          type: string
          description: Exact Git ref pin; mutually exclusive with version
        expected_revision:
          type: string
    ExtensionInstallResponse:
      x-go-generate: true
      type: object
      additionalProperties: false
      description: An install reports the generation it committed like every other write, plus the package identity only an install resolves.
      required:
        - view
        - pack_id
        - package_root
      properties:
        view:
          $ref: '#/components/schemas/ExtensionsCatalogView'
        pack_id:
          type: string
          pattern: ^[a-zA-Z0-9_-][a-zA-Z0-9_.-]*(/[a-zA-Z0-9_-][a-zA-Z0-9_.-]*)*$
        package_root:
          type: string
        resolved_revision:
          type: string
        version:
          type: string
        warnings:
          type: array
          items:
            type: string
    UpdateExtensionPackRequest:
      x-go-generate: true
      type: object
      additionalProperties: false
      description: expected_revision and at least one change.
      minProperties: 2
      required:
        - expected_revision
      properties:
        enabled:
          x-go-pointer: true
          type: boolean
        expected_revision:
          type: string
    ExtensionConfigurationRequest:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - expected_revision
      properties:
        packs:
          x-go-type: map[string]map[string]any
          type: object
          minProperties: 1
          description: Pack id to complete replacement block; an empty block removes that pack configuration
          additionalProperties:
            type: object
            additionalProperties:
              $ref: '#/components/schemas/ExtensionConfigurationValue'
        expected_revision:
          type: string
    ExtensionPackageChange:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - pack_id
        - kind
      properties:
        pack_id:
          type: string
          pattern: ^[a-zA-Z0-9_-][a-zA-Z0-9_.-]*(/[a-zA-Z0-9_-][a-zA-Z0-9_.-]*)*$
        current_version:
          type: string
        candidate_version:
          type: string
        current_revision:
          type: string
        candidate_revision:
          type: string
        kind:
          type: string
          enum:
            - added
            - removed
            - upgraded
            - downgraded
            - revised
            - unchanged
    ExtensionPackUpdateStatus:
      x-go-generate: true
      x-go-field-order:
        - pack_id
        - available
        - current_version
        - candidate_version
        - current_revision
        - candidate_revision
        - changes
        - message
      type: object
      additionalProperties: false
      required:
        - pack_id
        - available
        - changes
      properties:
        pack_id:
          type: string
          pattern: ^[a-zA-Z0-9_-][a-zA-Z0-9_.-]*(/[a-zA-Z0-9_-][a-zA-Z0-9_.-]*)*$
        available:
          type: boolean
        current_version:
          type: string
        candidate_version:
          type: string
        current_revision:
          type: string
        candidate_revision:
          type: string
        message:
          type: string
        changes:
          type: array
          items:
            $ref: '#/components/schemas/ExtensionPackageChange'
    ExtensionRevisionRequest:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - expected_revision
      properties:
        expected_revision:
          type: string
          description: Extension-state revision the caller last observed
    ExtensionPackActionResponse:
      x-go-generate: true
      type: object
      additionalProperties: false
      description: Update and Reload report the generation they committed, plus the package identity the re-resolve settled on.
      required:
        - view
        - pack_id
      properties:
        view:
          $ref: '#/components/schemas/ExtensionsCatalogView'
        pack_id:
          type: string
          pattern: ^[a-zA-Z0-9_-][a-zA-Z0-9_.-]*(/[a-zA-Z0-9_-][a-zA-Z0-9_.-]*)*$
        resolved_revision:
          type: string
        version:
          type: string
        message:
          type: string
        warnings:
          type: array
          items:
            type: string
    ExtensionMetaPackInstallRequest:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - source
        - expected_revision
      properties:
        source:
          type: string
        version:
          type: string
          description: SemVer constraint; defaults to * and is mutually exclusive with ref
        ref:
          type: string
          description: Exact Git ref; mutually exclusive with version
        expected_revision:
          type: string
    UpdateExtensionMetaPackRequest:
      x-go-generate: true
      type: object
      additionalProperties: false
      description: expected_revision and at least one change.
      minProperties: 2
      required:
        - expected_revision
      properties:
        enabled:
          x-go-pointer: true
          type: boolean
        expected_revision:
          type: string
    ExtensionSuggestionsAcceptRequest:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - pack_ids
        - expected_revision
        - expected_suggestion_revision
      properties:
        pack_ids:
          x-go-name: PackIDs
          type: array
          uniqueItems: true
          items:
            type: string
            pattern: ^[a-zA-Z0-9_-][a-zA-Z0-9_.-]*(/[a-zA-Z0-9_-][a-zA-Z0-9_.-]*)*$
          minItems: 1
        expected_revision:
          type: string
        expected_suggestion_revision:
          type: string
          description: Must match the proposal returned by GET; changes are rejected before install.
    HostResourceAccess:
      type: string
      enum:
        - allow
        - ask
        - deny
    HostResourceAccessSetting:
      type: string
      enum:
        - inherit
        - ask
        - deny
    HostResourcePromptMode:
      type: string
      enum:
        - omit
        - advertise
        - avoid
      description: |
        Ambient prompt posture. `omit` lists the resource on write agents when it is present and host-supported. `advertise` lists it on any agent whose tool profile supplies the required surfaces. `avoid` lists it with a prefer-another-route note.
    UpdateHostResourceRequest:
      x-go-generate: true
      type: object
      properties:
        access:
          $ref: '#/components/schemas/HostResourceAccessSetting'
      additionalProperties: false
    SourceTreeAddress:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - root_id
        - path
      properties:
        root_id:
          type: string
          format: uuid
        path:
          type: string
          minLength: 1
          maxLength: 65536
    SourceTreeDisclosure:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - address
        - open
        - recursive
      properties:
        address:
          $ref: '#/components/schemas/SourceTreeAddress'
        open:
          type: boolean
        recursive:
          type: boolean
    SourceTreeReviewScope:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - baseline
      properties:
        baseline:
          type: string
        mark_user_edits:
          type: boolean
          x-go-pointer: true
    SourceTreeIntent:
      x-go-generate: true
      type: object
      additionalProperties: false
      required: []
      properties:
        disclosures:
          type: array
          items:
            $ref: '#/components/schemas/SourceTreeDisclosure'
          maxItems: 2048
        filter:
          type: string
          maxLength: 4096
        review:
          $ref: '#/components/schemas/SourceTreeReviewScope'
          x-go-pointer: true
    SourceTreeViewCreate:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - kind
        - operation_id
        - client_id
        - workspace_id
        - intent
      properties:
        kind:
          type: string
          enum:
            - tree
        operation_id:
          type: string
          format: uuid
        client_id:
          type: string
          pattern: ^[a-zA-Z0-9:._-]+$
          minLength: 1
          maxLength: 256
          description: Stable window identity shared with editor requests; grants no authority.
        session_id:
          type: string
          format: uuid
        workspace_id:
          type: string
          pattern: ^ws_[0-9a-f]{32}$
          minLength: 1
        intent:
          $ref: '#/components/schemas/SourceTreeIntent'
    CurrentComparisonSource:
      x-go-generate: true
      type: object
      additionalProperties: false
      description: A read-only, immutable snapshot of the current on-disk file. Frames and search remain bounded for files above the editing limit.
      required:
        - kind
        - root_id
        - path
      properties:
        kind:
          type: string
          enum:
            - current
        root_id:
          type: string
          format: uuid
        path:
          type: string
          minLength: 1
          maxLength: 65536
        decode_as:
          type: string
          enum:
            - utf-16le
            - utf-16be
    EffectComparisonSource:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - kind
        - effect_id
      properties:
        kind:
          type: string
          enum:
            - effect
        effect_id:
          type: string
          format: uuid
    ScopeComparisonSource:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - kind
        - file_id
        - baseline
      properties:
        kind:
          type: string
          enum:
            - scope
        file_id:
          type: string
          format: uuid
        baseline:
          type: string
        mark_user_edits:
          type: boolean
          x-go-pointer: true
        presentation_after_ordinal:
          type: integer
          minimum: 0
          format: int64
          x-go-pointer: true
    TurnComparisonSource:
      x-go-generate: true
      type: object
      additionalProperties: false
      description: |
        What one turn did to one file: the state the turn found through the
        state it left. It starts where the turn baseline's scope comparison
        starts and ends at the turn's own last state, so a later turn's work
        never lands in an earlier turn's range.
      required:
        - kind
        - file_id
        - session_id
        - turn
      properties:
        kind:
          type: string
          enum:
            - turn
        file_id:
          type: string
          format: uuid
        session_id:
          type: string
          format: uuid
        turn:
          type: integer
          minimum: 1
        mark_user_edits:
          type: boolean
          x-go-pointer: true
    ReviewedComparisonSource:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - kind
        - file_id
        - reviewed_through_ordinal
      properties:
        kind:
          type: string
          enum:
            - reviewed
        file_id:
          type: string
          format: uuid
        reviewed_through_ordinal:
          type: integer
          minimum: 1
          format: int64
    CommitComparisonSource:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - kind
        - root_id
        - path
      properties:
        kind:
          type: string
          enum:
            - commit
        root_id:
          type: string
          format: uuid
        path:
          type: string
          minLength: 1
          maxLength: 65536
        expected_head:
          type: string
          x-go-pointer: true
    BlobComparisonSource:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - kind
        - root_id
        - blob_oid
      properties:
        kind:
          type: string
          enum:
            - blob
        root_id:
          type: string
          format: uuid
        blob_oid:
          type: string
        before_blob_oid:
          type: string
        display_path:
          type: string
          minLength: 1
          maxLength: 65536
    GitChangeComparisonSource:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - kind
        - change_id
        - path
      properties:
        kind:
          type: string
          enum:
            - git_change
        change_id:
          type: string
          format: uuid
        path:
          type: string
          minLength: 1
          maxLength: 65536
        movement:
          type: boolean
          x-go-pointer: true
        parent:
          type: integer
          minimum: 1
          maximum: 1000
          x-go-pointer: true
    GitRangeComparisonSource:
      x-go-generate: true
      type: object
      additionalProperties: false
      description: |
        One file across two commits of a root's repository, read from the
        object store. An added or deleted file reads its absent side as absent.
      required:
        - kind
        - root_id
        - before_commit
        - after_commit
        - path
      properties:
        kind:
          type: string
          enum:
            - git_range
        root_id:
          type: string
          format: uuid
        before_commit:
          type: string
          description: Full commit id; empty compares with the empty tree.
        after_commit:
          type: string
          description: Full commit id.
        path:
          type: string
          minLength: 1
          maxLength: 65536
    ChatComparisonSource:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - kind
        - first
        - last
      properties:
        kind:
          type: string
          enum:
            - chat
        first:
          $ref: '#/components/schemas/FileEditReference'
        last:
          $ref: '#/components/schemas/FileEditReference'
        expected_before_sha256:
          type: string
        expected_after_sha256:
          type: string
    TextComparisonSource:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - kind
        - path
        - before
        - after
      properties:
        kind:
          type: string
          enum:
            - text
        path:
          type: string
          minLength: 1
          maxLength: 65536
        before:
          type:
            - string
            - 'null'
          description: Null means the file is absent; an empty string is a present empty file.
          maxLength: 4194304
        after:
          type:
            - string
            - 'null'
          description: Null means the file is absent; an empty string is a present empty file.
          maxLength: 4194304
    RetainedComparisonSource:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - kind
        - view_id
        - comparison
      properties:
        kind:
          type: string
          enum:
            - retained
        view_id:
          type: string
          format: uuid
        comparison:
          type: string
          enum:
            - before
            - current
        root_id:
          type: string
          format: uuid
        path:
          type: string
          minLength: 1
          maxLength: 65536
    SourceComparisonSelector:
      oneOf:
        - $ref: '#/components/schemas/CurrentComparisonSource'
        - $ref: '#/components/schemas/EffectComparisonSource'
        - $ref: '#/components/schemas/VersionComparisonSource'
        - $ref: '#/components/schemas/ScopeComparisonSource'
        - $ref: '#/components/schemas/TurnComparisonSource'
        - $ref: '#/components/schemas/ReviewedComparisonSource'
        - $ref: '#/components/schemas/CommitComparisonSource'
        - $ref: '#/components/schemas/BlobComparisonSource'
        - $ref: '#/components/schemas/GitChangeComparisonSource'
        - $ref: '#/components/schemas/GitRangeComparisonSource'
        - $ref: '#/components/schemas/ChatComparisonSource'
        - $ref: '#/components/schemas/TextComparisonSource'
        - $ref: '#/components/schemas/RetainedComparisonSource'
      discriminator:
        propertyName: kind
        mapping:
          current: '#/components/schemas/CurrentComparisonSource'
          effect: '#/components/schemas/EffectComparisonSource'
          version: '#/components/schemas/VersionComparisonSource'
          scope: '#/components/schemas/ScopeComparisonSource'
          turn: '#/components/schemas/TurnComparisonSource'
          reviewed: '#/components/schemas/ReviewedComparisonSource'
          commit: '#/components/schemas/CommitComparisonSource'
          blob: '#/components/schemas/BlobComparisonSource'
          git_change: '#/components/schemas/GitChangeComparisonSource'
          git_range: '#/components/schemas/GitRangeComparisonSource'
          chat: '#/components/schemas/ChatComparisonSource'
          text: '#/components/schemas/TextComparisonSource'
          retained: '#/components/schemas/RetainedComparisonSource'
    SourceComparisonFold:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - start
        - end
      properties:
        start:
          type: integer
          minimum: 0
        end:
          type: integer
          minimum: 0
    SourceComparisonIntent:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - mode
      properties:
        mode:
          type: string
          enum:
            - changes
            - split
            - full
            - before
            - after
        expanded:
          type: array
          items:
            $ref: '#/components/schemas/SourceComparisonFold'
          maxItems: 2048
    SourceComparisonViewCreate:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - kind
        - operation_id
        - client_id
        - source
        - intent
      properties:
        kind:
          type: string
          enum:
            - comparison
        operation_id:
          type: string
          format: uuid
        client_id:
          type: string
          pattern: ^[a-zA-Z0-9:._-]+$
          minLength: 1
          maxLength: 256
          description: Stable window identity shared with editor requests; grants no authority.
        session_id:
          type: string
          format: uuid
        source:
          $ref: '#/components/schemas/SourceComparisonSelector'
        intent:
          $ref: '#/components/schemas/SourceComparisonIntent'
    SourceViewCreate:
      oneOf:
        - $ref: '#/components/schemas/SourceTreeViewCreate'
        - $ref: '#/components/schemas/SourceComparisonViewCreate'
      discriminator:
        propertyName: kind
        mapping:
          tree: '#/components/schemas/SourceTreeViewCreate'
          comparison: '#/components/schemas/SourceComparisonViewCreate'
    SourceViewExtent:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - rows
        - complete
      properties:
        rows:
          type: integer
          minimum: 0
          format: int64
          maximum: 9007199254740991
        complete:
          type: boolean
    SourceViewFailure:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - code
        - message
      properties:
        code:
          type: string
          enum:
            - unavailable
            - resource_limit
            - preparation_failed
            - expired
        message:
          type: string
          maxLength: 4096
    SourceTreeView:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - kind
        - id
        - intent_revision
        - projection_revision
        - state
        - extent
        - expires_at
        - workspace_id
        - intent
        - roots
        - loading_directories
      properties:
        kind:
          type: string
          enum:
            - tree
        id:
          type: string
          format: uuid
        intent_revision:
          type: string
          format: uuid
        projection_revision:
          type: string
          minLength: 1
          maxLength: 128
        state:
          type: string
          enum:
            - preparing
            - ready
            - failed
        extent:
          $ref: '#/components/schemas/SourceViewExtent'
        expires_at:
          type: string
          format: date-time
        failure:
          $ref: '#/components/schemas/SourceViewFailure'
          x-go-pointer: true
        workspace_id:
          type: string
          pattern: ^ws_[0-9a-f]{32}$
          minLength: 1
        intent:
          $ref: '#/components/schemas/SourceTreeIntent'
        roots:
          type: array
          items:
            $ref: '#/components/schemas/ProjectRoot'
        loading_directories:
          type: integer
          minimum: 0
          description: Directory reads still pending for this view.
    SourceReaderSide:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - path
        - sha256
        - lines
        - availability
      properties:
        path:
          type: string
        sha256:
          type: string
          x-go-name: SHA256
        lines:
          type: integer
          minimum: 0
        availability:
          type: string
    SourceReaderChangeArea:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - after_line
        - added
        - removed
      properties:
        after_line:
          type: integer
          minimum: 1
          description: One-based insertion point or first changed line in the after document.
        added:
          type: integer
          minimum: 0
        removed:
          type: integer
          minimum: 0
    SourceComparisonSummary:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - before
        - after
        - added
        - removed
        - rows
        - change_areas
        - change_area_count
      properties:
        before:
          $ref: '#/components/schemas/SourceReaderSide'
        after:
          $ref: '#/components/schemas/SourceReaderSide'
        added:
          type: integer
          minimum: 0
        removed:
          type: integer
          minimum: 0
        rows:
          type: integer
          minimum: 0
        change_areas:
          type: array
          items:
            $ref: '#/components/schemas/SourceReaderChangeArea'
          maxItems: 6
        change_area_count:
          type: integer
          minimum: 0
    SourceComparisonDetails:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - in_range
        - location_changed
      properties:
        summary:
          $ref: '#/components/schemas/SourceComparisonSummary'
          x-go-pointer: true
        in_range:
          type: boolean
        location_changed:
          type: boolean
        effect_id:
          type: string
          format: uuid
        file_id:
          type: string
          format: uuid
        op:
          $ref: '#/components/schemas/SourceChangeOp'
        presentation_after_ordinal:
          type: integer
          minimum: 0
          format: int64
          x-go-pointer: true
        user_edits_unmarked:
          type: boolean
        before:
          $ref: '#/components/schemas/SourceReaderEndpoint'
          x-go-pointer: true
        after:
          $ref: '#/components/schemas/SourceReaderEndpoint'
          x-go-pointer: true
    SourceComparisonView:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - kind
        - id
        - intent_revision
        - projection_revision
        - state
        - extent
        - expires_at
        - intent
      properties:
        kind:
          type: string
          enum:
            - comparison
        id:
          type: string
          format: uuid
        intent_revision:
          type: string
          format: uuid
        projection_revision:
          type: string
          minLength: 1
          maxLength: 128
        state:
          type: string
          enum:
            - preparing
            - ready
            - failed
        extent:
          $ref: '#/components/schemas/SourceViewExtent'
        expires_at:
          type: string
          format: date-time
        failure:
          $ref: '#/components/schemas/SourceViewFailure'
          x-go-pointer: true
        intent:
          $ref: '#/components/schemas/SourceComparisonIntent'
        comparison:
          $ref: '#/components/schemas/SourceComparisonDetails'
          x-go-pointer: true
    SourceView:
      oneOf:
        - $ref: '#/components/schemas/SourceTreeView'
        - $ref: '#/components/schemas/SourceComparisonView'
      discriminator:
        propertyName: kind
        mapping:
          tree: '#/components/schemas/SourceTreeView'
          comparison: '#/components/schemas/SourceComparisonView'
    SourceComparisonDigestRequest:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - sources
      description: Comparisons to measure without opening a view. Retained, current and text sources need a view or carry their own text, so they are rejected.
      properties:
        session_id:
          type: string
          format: uuid
        sources:
          type: array
          minItems: 1
          maxItems: 64
          items:
            $ref: '#/components/schemas/SourceComparisonSelector'
    SourceComparisonDigest:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - in_range
      description: One comparison's measure. A failed source carries failure and nothing else.
      properties:
        in_range:
          type: boolean
          description: False when the source holds no change for the file, which is a valid answer.
        summary:
          $ref: '#/components/schemas/SourceComparisonSummary'
          x-go-pointer: true
        changes_rows:
          type: integer
          minimum: 0
          description: 'Rows a changes presentation of this comparison holds: every changed and context row, and one per folded run of unchanged rows.'
        changes_folds:
          type: integer
          minimum: 0
          description: Folded runs among changes_rows.
        failure:
          $ref: '#/components/schemas/SourceViewFailure'
          x-go-pointer: true
    SourceComparisonDigests:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - digests
      properties:
        digests:
          type: array
          description: One digest per requested source, in request order.
          items:
            $ref: '#/components/schemas/SourceComparisonDigest'
    SourceTreeToggle:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - kind
        - address
      properties:
        kind:
          type: string
          enum:
            - toggle
        address:
          $ref: '#/components/schemas/SourceTreeAddress'
        collapse_descendants:
          type: boolean
          description: When the folder is open, collapse its descendants and leave it open. A closed folder opens normally.
    SourceTreeDisclose:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - kind
        - disclosures
      properties:
        kind:
          type: string
          enum:
            - disclose
        disclosures:
          type: array
          minItems: 1
          maxItems: 2048
          description: Ordered disclosure changes applied as one intent revision.
          items:
            $ref: '#/components/schemas/SourceTreeDisclosure'
    SourceTreeReveal:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - kind
        - address
      properties:
        kind:
          type: string
          enum:
            - reveal
        address:
          $ref: '#/components/schemas/SourceTreeAddress'
    SourceTreeFilter:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - kind
        - query
      properties:
        kind:
          type: string
          enum:
            - filter
        query:
          type: string
          maxLength: 4096
    SourceTreeReview:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - kind
      properties:
        kind:
          type: string
          enum:
            - review
        scope:
          $ref: '#/components/schemas/SourceTreeReviewScope'
          x-go-pointer: true
    SourceTreeCommand:
      oneOf:
        - $ref: '#/components/schemas/SourceTreeToggle'
        - $ref: '#/components/schemas/SourceTreeDisclose'
        - $ref: '#/components/schemas/SourceTreeReveal'
        - $ref: '#/components/schemas/SourceTreeFilter'
        - $ref: '#/components/schemas/SourceTreeReview'
      discriminator:
        propertyName: kind
        mapping:
          toggle: '#/components/schemas/SourceTreeToggle'
          disclose: '#/components/schemas/SourceTreeDisclose'
          reveal: '#/components/schemas/SourceTreeReveal'
          filter: '#/components/schemas/SourceTreeFilter'
          review: '#/components/schemas/SourceTreeReview'
    SourceTreeViewUpdate:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - kind
        - operation_id
        - expected_intent_revision
        - command
      properties:
        kind:
          type: string
          enum:
            - tree
        operation_id:
          type: string
          format: uuid
        expected_intent_revision:
          type: string
          format: uuid
        base_presentation_id:
          type: string
          format: uuid
          description: Resolve a toggle against the displayed presentation while preserving current disclosure intent. A presentation this view no longer presents answers source_view_revision_changed.
        command:
          $ref: '#/components/schemas/SourceTreeCommand'
    SourceComparisonViewUpdate:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - kind
        - operation_id
        - expected_intent_revision
        - intent
      properties:
        kind:
          type: string
          enum:
            - comparison
        operation_id:
          type: string
          format: uuid
        expected_intent_revision:
          type: string
          format: uuid
        intent:
          $ref: '#/components/schemas/SourceComparisonIntent'
    SourceViewUpdate:
      oneOf:
        - $ref: '#/components/schemas/SourceTreeViewUpdate'
        - $ref: '#/components/schemas/SourceComparisonViewUpdate'
      discriminator:
        propertyName: kind
        mapping:
          tree: '#/components/schemas/SourceTreeViewUpdate'
          comparison: '#/components/schemas/SourceComparisonViewUpdate'
    SourceViewportInterest:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - presentation_id
        - sequence
        - start
        - end
        - direction
      properties:
        presentation_id:
          type: string
          format: uuid
        sequence:
          type: integer
          format: int64
          minimum: 1
          maximum: 9007199254740991
        start:
          type: integer
          format: int64
          minimum: 0
        end:
          type: integer
          format: int64
          minimum: 1
          description: Exclusive; greater than start.
        direction:
          type: integer
          minimum: -1
          maximum: 1
      description: Replaceable viewport demand in one immutable presentation. Visible rows precede symmetric nearby preparation; direction breaks ties. This resource is ephemeral and does not change disclosure intent or extent.
    SourceViewSpan:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - start
        - end
      properties:
        start:
          type: integer
          minimum: 0
          format: int64
        end:
          type: integer
          minimum: 0
          format: int64
    SourceTreeFramePrefix:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - end
        - fingerprint
      properties:
        end:
          type: integer
          format: int64
          minimum: 1
          description: Exclusive end of the validated row prefix.
        fingerprint:
          type: string
          pattern: ^[a-f0-9]{64}$
          description: Opaque fingerprint of the intent, root order, and preceding tree rows.
    SourceTreeRow:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - address
        - name
        - kind
        - depth
        - expanded
      properties:
        address:
          $ref: '#/components/schemas/SourceTreeAddress'
        name:
          type: string
        kind:
          type: string
          enum:
            - file
            - directory
            - loading
            - empty
            - error
        depth:
          type: integer
          minimum: 0
        expanded:
          type: boolean
        symlink:
          type: boolean
        deleted:
          type: boolean
        file_id:
          type: string
          format: uuid
        error:
          type: string
          maxLength: 4096
    SourceTreeAncestor:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - row
        - index
        - end
      properties:
        row:
          $ref: '#/components/schemas/SourceTreeRow'
        index:
          type: integer
          minimum: 0
          format: int64
        end:
          type: integer
          minimum: 0
          format: int64
    SourceTreeFrame:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - kind
        - view_id
        - intent_revision
        - projection_revision
        - extent
        - span
        - anchor
        - rows
        - ancestors
      properties:
        kind:
          type: string
          enum:
            - tree
        view_id:
          type: string
          format: uuid
        intent_revision:
          type: string
          format: uuid
        projection_revision:
          type: string
          minLength: 1
          maxLength: 128
        extent:
          $ref: '#/components/schemas/SourceViewExtent'
        span:
          $ref: '#/components/schemas/SourceViewSpan'
        target:
          type: integer
          format: int64
          minimum: 0
          x-go-pointer: true
          description: Resolved anchor plus offset before subtracting before, in this snapshot. Present on anchored reads, including when leading context is clamped at the beginning.
        prefix:
          $ref: '#/components/schemas/SourceTreeFramePrefix'
          x-go-pointer: true
          description: Reusable prefix proof for this frame. May be absent for filtered or review projections.
        retained_prefix:
          $ref: '#/components/schemas/SourceTreeFramePrefix'
          x-go-pointer: true
          description: Largest supplied prefix proven unchanged in this snapshot. Cached rows below end retain their positions; ancestor bounds beyond end require fresh context.
        anchor:
          $ref: '#/components/schemas/SourceTreeAddress'
        rows:
          type: array
          items:
            $ref: '#/components/schemas/SourceTreeRow'
          maxItems: 200
        ancestors:
          type: array
          description: Complete root-first ancestor chain for the frame's first row.
          items:
            $ref: '#/components/schemas/SourceTreeAncestor'
    SourceComparisonAnchor:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - row
      properties:
        row:
          type: integer
          minimum: 0
    SourceReaderRow:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - index
        - end
        - kind
        - text
        - before_line
        - after_line
        - changed
      properties:
        index:
          type: integer
          minimum: 0
        end:
          type: integer
          minimum: 0
        kind:
          type: string
          enum:
            - equal
            - insert
            - delete
            - gap
        text:
          type: string
        peer:
          $ref: '#/components/schemas/SourceReaderRow'
          x-go-pointer: true
        column:
          type: integer
          minimum: 0
          description: UTF-16 offset within the original line for a bounded fragment.
        before_line:
          type: integer
          minimum: 0
        after_line:
          type: integer
          minimum: 0
        syntax:
          type: array
          items:
            $ref: '#/components/schemas/SourceReaderToken'
        changed:
          type: array
          items:
            $ref: '#/components/schemas/SourceReaderSpan'
        secret_screen:
          $ref: '#/components/schemas/SecretScreen'
          x-go-pointer: true
        contributors:
          type: array
          items:
            $ref: '#/components/schemas/SourceContributor'
    SourceReaderToken:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - from
        - to
        - kind
      properties:
        from:
          type: integer
          minimum: 0
        to:
          type: integer
          minimum: 0
        kind:
          type: string
          enum:
            - keyword
            - string
            - number
            - comment
            - function
            - operator
            - property
            - constant
            - variable
            - type
    SourceReaderSpan:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - from
        - to
      properties:
        from:
          type: integer
          minimum: 0
        to:
          type: integer
          minimum: 0
    SourceComparisonFrame:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - kind
        - view_id
        - intent_revision
        - projection_revision
        - extent
        - span
        - anchor
        - rows
      properties:
        kind:
          type: string
          enum:
            - comparison
        view_id:
          type: string
          format: uuid
        intent_revision:
          type: string
          format: uuid
        projection_revision:
          type: string
          minLength: 1
          maxLength: 128
        extent:
          $ref: '#/components/schemas/SourceViewExtent'
        span:
          $ref: '#/components/schemas/SourceViewSpan'
        target:
          type: integer
          format: int64
          minimum: 0
          x-go-pointer: true
          description: Resolved anchor plus offset before subtracting before, in this snapshot. Present on anchored reads, including when leading context is clamped at the beginning.
        anchor:
          $ref: '#/components/schemas/SourceComparisonAnchor'
        rows:
          type: array
          items:
            $ref: '#/components/schemas/SourceReaderRow'
          maxItems: 200
    SourceViewFrame:
      oneOf:
        - $ref: '#/components/schemas/SourceTreeFrame'
        - $ref: '#/components/schemas/SourceComparisonFrame'
      discriminator:
        propertyName: kind
        mapping:
          tree: '#/components/schemas/SourceTreeFrame'
          comparison: '#/components/schemas/SourceComparisonFrame'
    SourceTreeLocation:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - kind
        - view_id
        - projection_revision
        - index
        - visible
        - pending
        - address
      properties:
        kind:
          type: string
          enum:
            - tree
        view_id:
          type: string
          format: uuid
        projection_revision:
          type: string
          minLength: 1
          maxLength: 128
        index:
          type: integer
          minimum: 0
          format: int64
        pending:
          type: boolean
          description: Directory facts on this path are not ready; reveal the path to request them and retry after the projection changes.
        visible:
          type: boolean
        address:
          $ref: '#/components/schemas/SourceTreeAddress'
    SourceComparisonLocation:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - kind
        - view_id
        - projection_revision
        - index
        - visible
        - anchor
      properties:
        kind:
          type: string
          enum:
            - comparison
        view_id:
          type: string
          format: uuid
        projection_revision:
          type: string
          minLength: 1
          maxLength: 128
        index:
          type: integer
          minimum: 0
          format: int64
        visible:
          type: boolean
        anchor:
          $ref: '#/components/schemas/SourceComparisonAnchor'
    SourceViewLocation:
      oneOf:
        - $ref: '#/components/schemas/SourceTreeLocation'
        - $ref: '#/components/schemas/SourceComparisonLocation'
      discriminator:
        propertyName: kind
        mapping:
          tree: '#/components/schemas/SourceTreeLocation'
          comparison: '#/components/schemas/SourceComparisonLocation'
    SourceTreeSearchMatch:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - address
        - index
        - from
        - to
      properties:
        address:
          $ref: '#/components/schemas/SourceTreeAddress'
        index:
          type: integer
          minimum: 0
          format: int64
        from:
          type: integer
          minimum: 0
        to:
          type: integer
          minimum: 0
    SourceTreeSearchPage:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - kind
        - view_id
        - projection_revision
        - next_cursor
        - complete
        - matches
      properties:
        kind:
          type: string
          enum:
            - tree
        view_id:
          type: string
          format: uuid
        projection_revision:
          type: string
          minLength: 1
          maxLength: 128
        next_cursor:
          type: string
        complete:
          type: boolean
        matches:
          type: array
          items:
            $ref: '#/components/schemas/SourceTreeSearchMatch'
          maxItems: 200
    SourceReaderMatch:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - row
        - from
        - to
      properties:
        row:
          type: integer
          minimum: 0
        from:
          type: integer
          minimum: 0
        to:
          type: integer
          minimum: 0
    SourceComparisonSearchPage:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - kind
        - view_id
        - projection_revision
        - next_cursor
        - complete
        - matches
      properties:
        kind:
          type: string
          enum:
            - comparison
        view_id:
          type: string
          format: uuid
        projection_revision:
          type: string
          minLength: 1
          maxLength: 128
        next_cursor:
          type: string
        complete:
          type: boolean
        matches:
          type: array
          items:
            $ref: '#/components/schemas/SourceReaderMatch'
          maxItems: 200
    SourceViewSearchPage:
      oneOf:
        - $ref: '#/components/schemas/SourceTreeSearchPage'
        - $ref: '#/components/schemas/SourceComparisonSearchPage'
      discriminator:
        propertyName: kind
        mapping:
          tree: '#/components/schemas/SourceTreeSearchPage'
          comparison: '#/components/schemas/SourceComparisonSearchPage'
    SourcePresentationCreate:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - operation_id
        - intent_revision
      properties:
        operation_id:
          type: string
          format: uuid
        intent_revision:
          type: string
          format: uuid
    SourcePresentation:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - id
        - view
      properties:
        id:
          type: string
          format: uuid
        view:
          $ref: '#/components/schemas/SourceView'
    EventScopeKind:
      type: string
      enum:
        - device
        - project
        - session
    EventScope:
      description: Closed, attributable delivery scope for an event.
      oneOf:
        - type: object
          required:
            - kind
          additionalProperties: false
          properties:
            kind:
              allOf:
                - $ref: '#/components/schemas/EventScopeKind'
                - const: device
        - type: object
          required:
            - kind
            - project_id
          additionalProperties: false
          properties:
            kind:
              allOf:
                - $ref: '#/components/schemas/EventScopeKind'
                - const: project
            project_id:
              type: string
              format: uuid
        - type: object
          required:
            - kind
            - project_id
            - session_id
          additionalProperties: false
          properties:
            kind:
              allOf:
                - $ref: '#/components/schemas/EventScopeKind'
                - const: session
            project_id:
              type: string
              format: uuid
            session_id:
              type: string
              format: uuid
    EventEnvelopeBase:
      type: object
      required:
        - v
        - event_id
        - cursor
        - topic
        - published_at
        - scope
        - data
      additionalProperties: false
      description: Shared fields of every topic-discriminated EventHub SSE envelope.
      properties:
        v:
          type: integer
          const: 1
          description: Schema version.
        event_id:
          type: string
          format: uuid
          description: Stable mutation event identity retained across delivery retries.
        entity_revision:
          type: integer
          format: int64
          minimum: 0
          description: |
            Monotonic authoritative entity revision when the topic has one, for rejecting an out-of-order delivery. Carried by workflow (the run's revision, scoped to the run in the event's own workflow_run_id), session lifecycle snapshots, and the session-scoped progress, queue, and findings topics. Absent on every other topic, which has no single monotonic entity behind it — treat a missing value as "cannot be compared", never as revision 0.
        cursor:
          type: string
          description: Opaque replay boundary for this delivered event.
        topic:
          $ref: '#/components/schemas/EventTopic'
        published_at:
          type: string
          format: date-time
          description: RFC 3339 timestamp.
        scope:
          $ref: '#/components/schemas/EventScope'
        data:
          description: Topic-specific payload, narrowed by EventEnvelope.
    SessionEventAction:
      type: string
      enum:
        - created
        - updated
        - deleted
      description: Durable session lifecycle transition represented by an event.
    NoticeCode:
      type: string
      enum:
        - attachment_scanned_no_text
        - grounding_escalated
        - host_fault
        - model_refused
        - project_mutation_in_progress
        - prompt_failed
        - provider_context_too_small
        - provider_empty_completion
        - provider_not_configured
        - provider_overloaded
        - provider_rate_limited
        - provider_request_rejected
        - provider_response_interrupted
        - provider_server_error
        - provider_silent
        - provider_tool_calls_in_prose
        - provider_tool_calls_unsupported
        - provider_unreachable
        - session_preparation_failed
        - session_spend_ceiling_reached
        - thinking_override_unavailable
        - user_image_not_visible
        - workflow_active
        - workflow_not_runnable
        - worktree_stale
    SessionIdleDisposition:
      type: string
      description: |
        Why a session went idle, stated so clients tell a stop from an error without reading message text or error strings. `interrupted` is the engine stopping under a live turn — a shutdown signal, or a session still busy at boot after process death — and does not render as an error. It is the session-level peer of the TOOL_OWNER_INTERRUPTED tool result.
      enum:
        - completed
        - user_stopped
        - turn_error
        - interrupted
    EventEnvelopeSession:
      allOf:
        - $ref: '#/components/schemas/EventEnvelopeBase'
        - type: object
          required:
            - topic
            - data
          properties:
            topic:
              const: session
            data:
              $ref: '#/components/schemas/SessionEvent'
    EventEnvelopeMessage:
      allOf:
        - $ref: '#/components/schemas/EventEnvelopeBase'
        - type: object
          required:
            - topic
            - data
          properties:
            topic:
              const: message
            data:
              $ref: '#/components/schemas/MessageEvent'
    EventEnvelopeWorker:
      allOf:
        - $ref: '#/components/schemas/EventEnvelopeBase'
        - type: object
          required:
            - topic
            - data
          properties:
            topic:
              const: worker
            data:
              $ref: '#/components/schemas/WorkerEvent'
    EventEnvelopeCheckpoint:
      allOf:
        - $ref: '#/components/schemas/EventEnvelopeBase'
        - type: object
          required:
            - topic
            - data
          properties:
            topic:
              const: checkpoint
            data:
              $ref: '#/components/schemas/CheckpointEvent'
    EventEnvelopeBoard:
      allOf:
        - $ref: '#/components/schemas/EventEnvelopeBase'
        - type: object
          required:
            - topic
            - data
          properties:
            topic:
              const: board
            data:
              $ref: '#/components/schemas/BoardEvent'
    EventEnvelopeDelegation:
      allOf:
        - $ref: '#/components/schemas/EventEnvelopeBase'
        - type: object
          required:
            - topic
            - data
          properties:
            topic:
              const: delegation
            data:
              $ref: '#/components/schemas/DelegationEvent'
    EventEnvelopeCost:
      allOf:
        - $ref: '#/components/schemas/EventEnvelopeBase'
        - type: object
          required:
            - topic
            - data
          properties:
            topic:
              const: cost
            data:
              $ref: '#/components/schemas/CostEvent'
    EventEnvelopeLlm:
      allOf:
        - $ref: '#/components/schemas/EventEnvelopeBase'
        - type: object
          required:
            - topic
            - data
          properties:
            topic:
              const: llm
            data:
              $ref: '#/components/schemas/LLMCallEvent'
    EventEnvelopeActivity:
      allOf:
        - $ref: '#/components/schemas/EventEnvelopeBase'
        - type: object
          required:
            - topic
            - data
          properties:
            topic:
              const: activity
            data:
              $ref: '#/components/schemas/ActivityEvent'
    OAROnFireEvent:
      x-go-generate: true
      type: object
      required:
        - session_id
        - rule
        - anchor
        - effect
      additionalProperties: false
      description: One Open Agent Rules publish_event record. It is host-observed telemetry, never a fact visible to another rule at the occurrence.
      properties:
        session_id:
          type: string
          format: uuid
        rule:
          type: string
          description: Qualified OAR rule identifier.
        anchor:
          type: string
        effect:
          type: string
          enum:
            - block
            - warn
            - nudge
            - allow
            - transform
    EventEnvelopeOar:
      allOf:
        - $ref: '#/components/schemas/EventEnvelopeBase'
        - type: object
          required:
            - topic
            - data
          properties:
            topic:
              const: oar
            data:
              $ref: '#/components/schemas/OAROnFireEvent'
    EventEnvelopeScan:
      allOf:
        - $ref: '#/components/schemas/EventEnvelopeBase'
        - type: object
          required:
            - topic
            - data
          properties:
            topic:
              const: scan
            data:
              $ref: '#/components/schemas/CodeScanEvent'
    EventEnvelopeProviders:
      allOf:
        - $ref: '#/components/schemas/EventEnvelopeBase'
        - type: object
          required:
            - topic
            - data
          properties:
            topic:
              const: providers
            data:
              $ref: '#/components/schemas/ProviderEvent'
    EventEnvelopeModelPolicy:
      allOf:
        - $ref: '#/components/schemas/EventEnvelopeBase'
        - type: object
          required:
            - topic
            - data
          properties:
            topic:
              const: model_policy
            data:
              $ref: '#/components/schemas/ModelPolicyEvent'
    SettingsArea:
      type: string
      description: Facets of host and project settings published on EventTopicSettings.
      enum:
        - approvals
        - extensions
        - file_summaries
        - limits
        - mcp
        - power
        - pricing
        - project_trust
        - review
        - security_scanners
        - verify
        - web_research
    EventEnvelopeSettings:
      allOf:
        - $ref: '#/components/schemas/EventEnvelopeBase'
        - type: object
          required:
            - topic
            - data
          properties:
            topic:
              const: settings
            data:
              $ref: '#/components/schemas/SettingsEvent'
    WorkflowEventKind:
      type: string
      enum:
        - run_started
        - run_updated
        - run_canceled
        - phase_advanced
        - workflow_persisted
        - gate_pending
    EventEnvelopeWorkflow:
      allOf:
        - $ref: '#/components/schemas/EventEnvelopeBase'
        - type: object
          required:
            - topic
            - data
          properties:
            topic:
              const: workflow
            data:
              $ref: '#/components/schemas/WorkflowEvent'
    EventEnvelopeGrounding:
      allOf:
        - $ref: '#/components/schemas/EventEnvelopeBase'
        - type: object
          required:
            - topic
            - data
          properties:
            topic:
              const: grounding
            data:
              $ref: '#/components/schemas/GroundingEvent'
    EventEnvelopeFindings:
      allOf:
        - $ref: '#/components/schemas/EventEnvelopeBase'
        - type: object
          required:
            - topic
            - data
          properties:
            topic:
              const: findings
            data:
              $ref: '#/components/schemas/FindingsEvent'
    EventEnvelopeProgress:
      allOf:
        - $ref: '#/components/schemas/EventEnvelopeBase'
        - type: object
          required:
            - topic
            - data
          properties:
            topic:
              const: progress
            data:
              $ref: '#/components/schemas/ProgressEvent'
    EventEnvelopeQueue:
      allOf:
        - $ref: '#/components/schemas/EventEnvelopeBase'
        - type: object
          required:
            - topic
            - data
          properties:
            topic:
              const: queue
            data:
              $ref: '#/components/schemas/QueueEvent'
    EventEnvelopeProject:
      allOf:
        - $ref: '#/components/schemas/EventEnvelopeBase'
        - type: object
          required:
            - topic
            - data
          properties:
            topic:
              const: project
            data:
              $ref: '#/components/schemas/ProjectEvent'
    EventEnvelopeProcess:
      allOf:
        - $ref: '#/components/schemas/EventEnvelopeBase'
        - type: object
          required:
            - topic
            - data
          properties:
            topic:
              const: process
            data:
              $ref: '#/components/schemas/BackgroundProcessEvent'
    PreviewEventOp:
      type: string
      enum:
        - attach
        - detach
        - frame
        - action
        - state
      description: Live preview stream message kind.
    PreviewActionOverlay:
      x-go-generate: true
      type: object
      required:
        - label
      additionalProperties: false
      properties:
        label:
          type: string
        target:
          type: string
        x:
          x-go-pointer: true
          type: number
          format: double
        'y':
          x-go-pointer: true
          type: number
          format: double
        w:
          x-go-pointer: true
          type: number
          format: double
        h:
          x-go-pointer: true
          type: number
          format: double
    EventEnvelopePreview:
      allOf:
        - $ref: '#/components/schemas/EventEnvelopeBase'
        - type: object
          required:
            - topic
            - data
          properties:
            topic:
              const: preview
            data:
              $ref: '#/components/schemas/PreviewEvent'
    EventEnvelopeAttention:
      allOf:
        - $ref: '#/components/schemas/EventEnvelopeBase'
        - type: object
          required:
            - topic
            - data
          properties:
            topic:
              const: attention
            data:
              $ref: '#/components/schemas/AttentionView'
    EventEnvelopeCliOpen:
      allOf:
        - $ref: '#/components/schemas/EventEnvelopeBase'
        - type: object
          required:
            - topic
            - data
          properties:
            topic:
              const: cli_open
            data:
              $ref: '#/components/schemas/CLIOpenEvent'
    EventEnvelopeSourceChanged:
      allOf:
        - $ref: '#/components/schemas/EventEnvelopeBase'
        - type: object
          required:
            - topic
            - data
          properties:
            topic:
              const: source_changed
            data:
              $ref: '#/components/schemas/SourceChangesEvent'
    SourceOperationEvent:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - project_id
        - operation
      description: A file operation's status after a persisted transition or progress save; the same shape the operation list returns.
      properties:
        project_id:
          type: string
          format: uuid
        operation:
          $ref: '#/components/schemas/SourceOperationStatus'
    EventEnvelopeSourceOperation:
      allOf:
        - $ref: '#/components/schemas/EventEnvelopeBase'
        - type: object
          required:
            - topic
            - data
          properties:
            topic:
              const: source_operation
            data:
              $ref: '#/components/schemas/SourceOperationEvent'
    EventEnvelopeFileBriefing:
      allOf:
        - $ref: '#/components/schemas/EventEnvelopeBase'
        - type: object
          required:
            - topic
            - data
          properties:
            topic:
              const: file_briefing
            data:
              $ref: '#/components/schemas/FileBriefingEvent'
    EventEnvelopeArtifact:
      allOf:
        - $ref: '#/components/schemas/EventEnvelopeBase'
        - type: object
          required:
            - topic
            - data
          properties:
            topic:
              const: artifact
            data:
              $ref: '#/components/schemas/ArtifactEvent'
    AgentPresenceEvent:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - project_id
        - session_id
        - revision
        - presence
      description: One session's complete presence, replacing any earlier value for that session.
      properties:
        project_id:
          type: string
          format: uuid
        session_id:
          type: string
          format: uuid
        revision:
          type: integer
          format: int64
          minimum: 1
        presence:
          $ref: '#/components/schemas/AgentSessionPresence'
    EventEnvelopeAgentPresence:
      allOf:
        - $ref: '#/components/schemas/EventEnvelopeBase'
        - type: object
          required:
            - topic
            - data
          properties:
            topic:
              const: agent_presence
            data:
              $ref: '#/components/schemas/AgentPresenceEvent'
    EventEnvelopeEditorDocument:
      allOf:
        - $ref: '#/components/schemas/EventEnvelopeBase'
        - type: object
          required:
            - topic
            - data
          properties:
            topic:
              const: editor_document
            data:
              $ref: '#/components/schemas/EditorDocumentEvent'
    EventEnvelopePreflight:
      allOf:
        - $ref: '#/components/schemas/EventEnvelopeBase'
        - type: object
          required:
            - topic
            - data
          properties:
            topic:
              const: preflight
            data:
              $ref: '#/components/schemas/PreflightEvent'
    EventEnvelopeTurnClock:
      allOf:
        - $ref: '#/components/schemas/EventEnvelopeBase'
        - type: object
          required:
            - topic
            - data
          properties:
            topic:
              const: turn_clock
            data:
              $ref: '#/components/schemas/TurnClock'
    EventEnvelopeTurnLoad:
      allOf:
        - $ref: '#/components/schemas/EventEnvelopeBase'
        - type: object
          required:
            - topic
            - data
          properties:
            topic:
              const: turn_load
            data:
              $ref: '#/components/schemas/TurnLoad'
    SourceViewEvent:
      x-go-generate: true
      type: object
      additionalProperties: false
      required:
        - view_id
        - kind
        - intent_revision
        - projection_revision
        - invalidated
        - terminal
      properties:
        view_id:
          type: string
          format: uuid
        kind:
          type: string
          enum:
            - tree
            - comparison
        intent_revision:
          type: string
          format: uuid
        projection_revision:
          type: string
          minLength: 1
          maxLength: 128
        invalidated:
          type: boolean
        terminal:
          type: boolean
    EventEnvelopeSourceView:
      allOf:
        - $ref: '#/components/schemas/EventEnvelopeBase'
        - type: object
          required:
            - topic
            - data
          properties:
            topic:
              const: source_view
            data:
              $ref: '#/components/schemas/SourceViewEvent'
    SummarizeCoverage:
      x-go-generate: true
      type: object
      description: Indexed coverage and represented material; warming views have unknown totals
      required:
        - complete
      properties:
        catalog_state:
          type: string
          description: warming | ready | failed; omitted for file, pattern, or inline summaries
        catalog_refreshing:
          type: boolean
          description: Counts describe a completed indexed generation while reconciliation runs
        complete:
          type: boolean
        catalog_revision:
          x-go-type: uint64
          type: integer
          format: int64
        files_total:
          type: integer
          description: Indexed files for directory views
        files_represented:
          type: integer
        definitions_total:
          type: integer
        anchors_returned:
          type: integer
        matching_files_observed:
          type: integer
          description: Matching files observed across pattern pages
        matches_observed:
          type: integer
          description: Matching lines observed so far within source read budgets, including prior pattern pages
        match_samples_returned:
          type: integer
        children_total:
          type: integer
        children_returned:
          type: integer
        cursor:
          type: string
          description: Cursor used for this view
        next_cursor:
          type: string
          description: Scope-bound continuation for the next directory or pattern page
    SummarizePack:
      type: object
      description: Tiered briefing material the host assembles deterministically under a token budget. The coordinator synthesizes prose from this pack inline — there is no host-side LLM summary. Anchors and next_actions are derived from this same pack.
      properties:
        identity:
          type: array
          description: Per-source orientation — path, size, language
          items:
            type: object
            required:
              - path
              - kind
            properties:
              path:
                type: string
              kind:
                type: string
                description: file | dir_map | inline
              line_count:
                type: integer
              language:
                type: string
              outline_source:
                type: string
              import_path:
                type: string
              log_digest:
                type: string
        skeleton:
          type: array
          description: Ranked definitions (or directory-map rows)
          items:
            type: object
            required:
              - path
              - kind
              - name
            properties:
              path:
                type: string
              kind:
                type: string
              name:
                type: string
              line:
                type: integer
        substance:
          type: array
          description: Symbol-anchored source windows (verbatim, line-numbered)
          items:
            type: object
            required:
              - path
              - body
            properties:
              path:
                type: string
              handle:
                type: string
                description: Evidence handle for this source window
              start_line:
                type: integer
              end_line:
                type: integer
              symbol:
                type: string
              body:
                type: string
        imports:
          type: array
          description: One-hop module edges
          items:
            type: object
            required:
              - from
              - to
            properties:
              from:
                type: string
              to:
                type: string
              kind:
                type: string
                description: outbound | inbound
        call_sites:
          type: array
          items:
            type: object
            required:
              - path
              - excerpt
            properties:
              path:
                type: string
              line:
                type: integer
              excerpt:
                type: string
        neighbors:
          type: array
          items:
            type: object
            required:
              - path
            properties:
              path:
                type: string
              why:
                type: string
