openapi: 3.0.0
paths:
  /.well-known/jwks.json:
    get:
      description: 正常运行时一个密钥；AUTH_JWT_SECRET 轮换期间两个。
      operationId: JwksController_jwks
      parameters: []
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/JwksResponseDto'
      summary: 访问令牌的公开验证密钥
      tags:
        - auth
  /auth/login:
    post:
      description: 不存在的邮箱、错误的密码和被锁定的账户都返回完全相同的 401 响应——相同的响应体，相同的状态码。
      operationId: AuthController_login_
      parameters: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/LoginDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SessaoResponseDto'
        '400':
          description: >-
            请求体无效。`ValidationPipe` 以 `whitelist` 和 `forbidNonWhitelisted`
            运行，因此未知字段也会失败。
        '401':
          description: 凭据无效。
        '403':
          description: 邮箱尚未验证。
      summary: 进行身份验证并签发 access + refresh 令牌对
      tags:
        - auth
  /auth/logout:
    post:
      description: 始终返回 204，即使令牌未知——在此返回 401 将成为令牌有效性的探测口。
      operationId: AuthController_logout_
      parameters: []
      responses:
        '204':
          description: 会话已结束。会话 cookie 已被清除。
      summary: 撤销所出示 refresh 所属的整个令牌族
      tags:
        - auth
  /auth/oauth/{provider}/callback:
    get:
      description: >-
        成功时跳转到 `WEB_ORIGIN/` 并已设置会话 cookie；失败时跳转到
        `WEB_ORIGIN/login?oauth_error=1`，不说明原因。
      operationId: AuthController_oauthCallback
      parameters:
        - name: provider
          required: true
          in: path
          schema:
            enum:
              - github
              - gitlab
            type: string
        - name: code
          required: true
          in: query
          schema:
            type: string
        - name: state
          required: true
          in: query
          schema:
            type: string
      responses:
        '302':
          description: 重定向到 Web 应用。
          headers:
            Location:
              description: Web 应用中的目标地址。
              schema:
                type: string
                example: http://localhost:5173/
      summary: 接收社交登录的 OAuth 回调
      tags:
        - auth
  /auth/oauth/{provider}/start:
    get:
      description: '`state` 以其自身专用用途经 HMAC 签名——绝不使用 git 连接到项目流程的签名（见 ADR 0084）。'
      operationId: AuthController_oauthStart
      parameters:
        - name: provider
          required: true
          in: path
          schema:
            enum:
              - github
              - gitlab
            type: string
      responses:
        '302':
          description: 重定向到提供商。
          headers:
            Location:
              description: 提供商的授权 URL。
              schema:
                type: string
                example: https://github.com/login/oauth/authorize?…
        '400':
          description: 提供商不在 `github`/`gitlab` 之列。
      summary: 重定向到 OAuth 提供商进行社交登录
      tags:
        - auth
  /auth/refresh:
    post:
      description: 所出示的 refresh 会被消耗。重复出示已轮换的令牌会撤销整个令牌族并记录一条安全事件——即使原因只是客户端重复提交。
      operationId: AuthController_refresh_
      parameters: []
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SessaoResponseDto'
        '401':
          description: refresh 无效、已过期或已被使用。
      summary: 轮换 refresh 并签发新的令牌对
      tags:
        - auth
  /auth/register:
    post:
      description: 无论是新地址还是已注册地址都返回 202——响应不会透露账户是否存在。第二种情况下不会创建任何内容，地址所有者会收到通知。
      operationId: AuthController_register
      parameters: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/RegisterDto'
      responses:
        '202':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AceiteResponseDto'
        '400':
          description: 密码不符合最低策略。
        '403':
          description: 注册已关闭（AUTH_REGISTRATION_ENABLED=false）。
      summary: 创建账户并发送验证邮件
      tags:
        - auth
  /auth/request-password-reset:
    post:
      description: 无论地址已知还是未知都返回 202。这也是从 Keycloak 导入且尚未设置密码的用户所走的路径。
      operationId: AuthController_requestPasswordReset
      parameters: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/RequestPasswordResetDto'
      responses:
        '202':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AceiteResponseDto'
        '400':
          description: >-
            请求体无效。`ValidationPipe` 以 `whitelist` 和 `forbidNonWhitelisted`
            运行，因此未知字段也会失败。
      summary: 请求密码重置链接
      tags:
        - auth
  /auth/reset-password:
    post:
      description: 撤销该用户的所有会话。不签发令牌：重置密码的人会被引导去登录，这样邮箱被攻破并不等于一步就接管了账户。
      operationId: AuthController_resetPassword
      parameters: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ResetPasswordDto'
      responses:
        '204':
          description: 密码已设置。该用户所有活跃会话随之被撤销。
        '400':
          description: 链接无效/已过期，或密码不符合策略。
      summary: 根据令牌设置新密码
      tags:
        - auth
  /auth/verify-email:
    post:
      operationId: AuthController_verifyEmail
      parameters: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/VerifyEmailDto'
      responses:
        '204':
          description: 邮箱已验证；现在即可登录。
        '400':
          description: 链接无效、已过期或已被使用——三种情况响应相同。
      summary: 使用一次性令牌确认邮箱
      tags:
        - auth
  /gates:
    get:
      description: >-
        流程 gate 的声明式索引（ADR
        0054）。界面从这里推导存在哪些步骤以及每一步由谁判定，而不是在代码中重复这份列表——过去正是这样在无人察觉中过时的。
      operationId: GatesController_listar
      parameters: []
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GatesResponseDto'
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 活跃 gate 的注册表
      tags:
        - gates
  /git/oauth/{provider}/callback:
    get:
      description: >-
        之所以公开，是因为到达这里的是用户的浏览器，从提供商跳转而来，没有 api 会话。但它并非不受限制：`state` 经 HMAC
        校验，没有有效的 state 调用会被拒绝。从不返回 JSON——始终重定向，因为在浏览器导航中显示原始错误体体验极差。
      operationId: GitController_callback
      parameters:
        - name: provider
          required: true
          in: path
          schema:
            enum:
              - github
              - gitlab
            type: string
        - name: code
          required: true
          in: query
          schema:
            type: string
        - name: state
          required: true
          in: query
          schema:
            type: string
      responses:
        '302':
          description: >-
            成功时跳转到 `WEB_ORIGIN/projects/:id?git=connected`；失败时跳转到
            `WEB_ORIGIN/git-error`。错误原因不会泄露在 URL 中。
          headers:
            Location:
              description: Web 应用中的目标地址。
              schema:
                type: string
                example: http://localhost:5173/projects/01JC…?git=connected
        '403':
          description: 在项目上的角色不足。
        '404':
          description: 项目不存在。
      summary: 接收 OAuth 返回并重定向到 Web 应用
      tags:
        - git
  /health:
    get:
      description: 执行 `select 1`。只透露数据库是否响应；不透露拓扑或版本。之所以公开，是因为调用方是 kubelet，它不携带令牌。
      operationId: HealthController_check
      parameters: []
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HealthStatusResponseDto'
        '503':
          description: 数据库未响应。响应体在 `details` 中携带消息。
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HealthStatusResponseDto'
      summary: 就绪检查——进程确实能够处理流量
      tags:
        - infrastructure
  /internal/first-account:
    post:
      description: >-
        供一行命令安装器使用，它运行在未配置邮件传输的环境中：生成的 `.env` 不含任何邮件变量，`MAIL_TRANSPORT` 回落为
        `log`，而常规注册会等待一封永远不会到达的邮件。账户一出生即已验证，因为运行安装器的人已经证明了比控制一个邮箱更强的东西——他们控制着这台机器、`.env`
        和 Docker 守护进程。常规注册不受影响。当安装中已有任何用户时以 409
        拒绝：条件针对的是安装，而非这个邮箱，因此迁移恢复会通过同一检查让这一步保持静默。密码绝不在此生成，除了 argon2id
        哈希外也绝不存储在任何地方；个人 workspace（RN-410）在同一事务中创建。
      operationId: InternalFirstAccountController_criar
      parameters: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/FirstAccountInternalDto'
      responses:
        '201':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FirstAccountInternalResponseDto'
        '400':
          description: 邮箱格式错误，或密码被领域策略拒绝——与注册调用的是同一段代码，绝无第二套规则。
        '403':
          description: 服务令牌缺失或与共享令牌不一致。
        '409':
          description: 该安装已有用户。未创建任何内容。
      security:
        - service-token: []
      summary: 创建一个安装的第一个账户，且已验证
      tags:
        - internal
  /internal/gates:
    get:
      description: >-
        注册表只做描述，不做执行：没有任何 gate 会因为它而被强制执行，在此修改字段也不会改变任何行为。每个 gate
        都说明其通过的证据存放在哪里（`evidencia`），因为并非所有证据都在事件日志中。
      operationId: InternalGatesController_ler
      parameters: []
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GateRegistryResponseDto'
        '403':
          description: 服务令牌缺失或与共享令牌不一致。
      security:
        - service-token: []
      summary: 读取声明式 gate 注册表
      tags:
        - internal
  /internal/graph/prompt-templates:
    post:
      description: >-
        如果同一 `name` 已存在哈希相同的版本，则不会创建新版本——而是返回已有版本（幂等命中不会改变 HTTP 状态：为简单起见始终为
        201——响应体与新建时完全相同）。
      operationId: InternalGraphController_upsert
      parameters: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpsertPromptTemplateInternalDto'
      responses:
        '201':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PromptTemplateResponseDto'
        '400':
          description: >-
            请求体无效。`ValidationPipe` 以 `whitelist` 和 `forbidNonWhitelisted`
            运行，因此未知字段也会失败。
        '403':
          description: 服务令牌缺失或与共享令牌不一致。
        '503':
          description: Neo4j 未配置或不可达——读写模板没有可用的回退方案。
      security:
        - service-token: []
      summary: 发布提示词模板的一个版本，按哈希幂等
      tags:
        - internal
  /internal/graph/prompt-templates/{name}:
    get:
      description: 指定 `version` 时按相等查找；省略时查找最新的 `active` 版本。模板（或所请求的版本）不存在时返回 404。
      operationId: InternalGraphController_getByName
      parameters:
        - name: name
          required: true
          in: path
          schema:
            type: string
        - name: version
          required: false
          in: query
          schema:
            example: '3'
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PromptTemplateReadResponseDto'
        '403':
          description: 服务令牌缺失或与共享令牌不一致。
        '404':
          description: 模板不存在，或缺少所请求的版本。
        '503':
          description: Neo4j 未配置或不可达——读写模板没有可用的回退方案。
      security:
        - service-token: []
      summary: 按名称查找提示词模板
      tags:
        - internal
  /internal/machine-device-keys:
    post:
      description: >-
        供一行命令安装器在创建第一个账户后立即使用：本地代理需要一份凭据，而此前唯一存在的凭据绑定在项目上——而此时安装中还没有任何项目。密钥对在机器上生成，只有公开的一半到达这里；请把返回的
        `id` 作为 `kid` 写入私有 JWK（RN-475），这是磁盘上的文件与服务器上公开一半之间唯一的关联。请求体中故意没有
        `userId`：所有者是该安装的唯一用户，由 api
        解析，因此持有服务令牌绝不意味着可以选择为谁签发凭据。注册即替换：所有者现有的机器密钥在同一事务中被撤销，因此重装的机器可以工作，也不可能存在上千把机器密钥。项目密钥（ADR
        0118）不受影响。
      operationId: InternalMachineDeviceKeysController_registrarChave
      parameters: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/MachineDeviceKeyInternalDto'
      responses:
        '201':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MachineDeviceKeyInternalResponseDto'
        '400':
          description: >-
            该 JWK 不是 Ed25519 公钥——JSON 格式错误、`kty`/`crv` 不对、缺少 `x`，或携带了
            `d`（那是私有的一半，会被明确拒绝而不是存储）。
        '403':
          description: 服务令牌缺失或与共享令牌不一致。
        '409':
          description: 该安装没有用户，或有不止一个用户。未写入任何内容。此路由属于安装那一刻，一旦安装有了团队就永久失效。
      security:
        - service-token: []
      summary: 为全新安装注册机器设备密钥
      tags:
        - internal
  /internal/models/sync:
    post:
      description: 绝不会因为某一个提供商而失败：每个提供商在报告中各自作答，附带被跳过的原因以及失败时的来源。未响应的提供商会被跳过，而不是被标记为不可用。
      operationId: InternalModelsController_sync
      parameters: []
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SyncModelCatalogResponseDto'
        '403':
          description: 服务令牌缺失或与共享令牌不一致。
      security:
        - service-token: []
      summary: 在所有提供商之间同步模型目录
      tags:
        - internal
  /internal/projects/{projectId}/backlog:
    get:
      description: >-
        与 Backlog 标签页相同的 epic → story → task 树，经由同一用例（每个项目三次读取，绝无 N+1）。PO
        借此看到孤立的 epic 和没有任务的 story，而不是重新创建已有内容（RN-164）。
      operationId: InternalProjectsController_backlog
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/EpicComHistoriasResponseDto'
        '403':
          description: 服务令牌缺失或与共享令牌不一致。
      security:
        - service-token: []
      summary: 以树形展示项目的 backlog，供 PO 阅读已写下的内容
      tags:
        - internal
  /internal/projects/{projectId}/business-rules:
    get:
      description: >-
        项目所有会话中的每一条 `artifact.business_rule`——而不仅是当前会话的，那曾是 PO 启动上下文的上限——带有完整的
        `description` 以及哪些 story 已引用每条规则。`uncoveredCount` 是待办项：没有任何 story
        覆盖的规则（RN-164）。完全没有规则的项目返回 `200` 和空列表：“尚未捕获任何规则”不是错误。
      operationId: InternalProjectsController_businessRules
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProjectBusinessRulesResponseDto'
        '403':
          description: 服务令牌缺失或与共享令牌不一致。
      security:
        - service-token: []
      summary: 项目的业务规则及其覆盖情况，供 PO 阅读
      tags:
        - internal
  /internal/projects/{projectId}/container-exec:
    post:
      description: >-
        仅由 engine 调用，当 `Engine.Actions.TerminalExecutor`
        判定命令应在真实容器内执行时（`execution_mode: container` 且有已登记的 `running` 记录）。代理到
        `ContainerBrokerPort.exec`（ADR 0130/0134）——broker 五个操作中最后一个获得真实调用方的。响应中的
        `sucesso: false`——绝不是抛出的错误——是 broker 拒绝或不可达时的正常形态：在登记的 `running`
        记录与本次调用之间，容器可能已在外部死亡或被移除（RN-486，登记状态与观测状态绝不合并），这是命令失败，而非传输错误。
      operationId: InternalProjectsController_containerExec
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ContainerExecInternalDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ContainerExecInternalResponseDto'
        '400':
          description: >-
            请求体无效。`ValidationPipe` 以 `whitelist` 和 `forbidNonWhitelisted`
            运行，因此未知字段也会失败。
        '403':
          description: 服务令牌缺失或与共享令牌不一致。
      security:
        - service-token: []
      summary: 在项目的真实容器内执行终端命令（RN-492）
      tags:
        - internal
  /internal/projects/{projectId}/container-spec:
    get:
      description: >-
        项目标识、执行模式以及 Architect 当前的镜像决定。broker
        在把任何东西交给守护进程之前都会重新校验——从这里读取并不等于信任它，拒绝时会指明字段。不返回任何绝对路径：绑定源由守护进程依据宿主机文件系统解析，而来自
        api 容器内部的路径会静默挂载一个空文件夹。传递的是 `localizacao`（RN-503）——使用 broker
        两个根目录中的哪一个，以及该根目录未覆盖的相对段。
      operationId: InternalContainersController_containerSpec
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
        - name: papel
          required: false
          in: query
          schema:
            enum:
              - qa
            type: string
        - name: task
          required: false
          in: query
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ContainerSpecInternalResponseDto'
        '403':
          description: 服务令牌缺失或与共享令牌不一致。
        '404':
          description: 未找到项目。
      security:
        - service-token: []
      summary: broker 自行组装容器规格所需的信息
      tags:
        - internal
  /internal/projects/{projectId}/git-remote:
    get:
      description: >-
        返回干净的源地址（不嵌入凭据），对于远程提供商，还返回即时解密的 workspace
        所有者令牌（RN-058）。使用方按每次调用注入令牌，绝不写入文件——`.git/config` 位于 RN-075 授予 dev agent
        自动批准读取权限的文件夹内。
      operationId: InternalProjectsController_gitRemote
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProjectGitRemoteResponseDto'
        '403':
          description: 服务令牌缺失或与共享令牌不一致。
        '404':
          description: 项目没有已置备的仓库，或 workspace 所有者没有为该仓库提供商登记凭据。
      security:
        - service-token: []
      summary: 项目的工作远程仓库，用于 fetch 和 push
      tags:
        - internal
  /internal/projects/{projectId}/mirror-sync-result:
    post:
      description: >-
        仅由 engine 调用，在 runner 通过通道推送 `mirror_sync_result` 之后——与
        `workspace_confirm` 走同一路径（runner → 通道 → engine → api），绝无第二套机制：engine
        负责报告，不写表。这是遥测，不是领域事件：绝不写入 `session_events`（一轮镜像没有会话，而 `session_id` 是
        `NOT NULL`，与把 `rag_searches` 做成表的理由相同），也绝不是
        `proposed_action`（镜像写入是用户声明的配置，不是代理请求执行操作）。决定结果的是 `ok`——绝不是计数或消息是否存在：复制了
        0 个文件的一轮是正常的，绝不能与从未运行的一轮无法区分。记录绝不会破坏它所测量的东西：调用此接口时复制已经完成，engine
        只会在此记录拒绝。目标在此期间被清除的项目仍然会记录——这一轮确实发生了，而它的错误通常正是解释问题所在的线索。
      operationId: InternalProjectsController_mirrorSyncResult
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/MirrorSyncResultInternalDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MirrorSyncResultResponseDto'
        '400':
          description: >-
            请求体无效。`ValidationPipe` 以 `whitelist` 和 `forbidNonWhitelisted`
            运行，因此未知字段也会失败。
        '403':
          description: 服务令牌缺失或与共享令牌不一致。
        '404':
          description: 项目不存在。
      security:
        - service-token: []
      summary: 本地代理报告一轮镜像的结果（RN-517）
      tags:
        - internal
  /internal/projects/{projectId}/module-contracts:
    get:
      description: >-
        当前 module_map 中每个模块一条记录：它消费什么（`dependeDe`，即映射中的
        `dependsOn`）以及它暴露什么（`expoe`，来自 Architect 当前的
        `artifact.module_contracts`，未声明时为 `null`）。dev agent 读取它，而不是打开另一个模块的
        worktree（ADR 0200）。没有契约的项目返回 `200` 和 `status: sem_contratos`。
      operationId: InternalProjectsController_moduleContracts
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ContratosDoProjetoResponseDto'
        '403':
          description: 服务令牌缺失或与共享令牌不一致。
      security:
        - service-token: []
      summary: 模块之间的契约，供 dev agent 阅读（RN-684）
      tags:
        - internal
  /internal/projects/{projectId}/product-metrics:
    get:
      description: >-
        与 `analise:funil` 脚本（ADR 0089）相同的报告——会话 → 提交 → PR →
        合并的漏斗、真实交付周期和真实部署频率——经由相同的纯函数和相同的查询（`apps/api/src/application/services/funil-metrics.ts`），因此两者对同一事实绝不会出现分歧。补上
        `docs/fluxo.yml`（角色 `po`，输入 `metricas-de-produto`，此前为 `status:
        lacuna`）（RN-407）。
      operationId: InternalProjectsController_productMetrics
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProductMetricsResponseDto'
        '403':
          description: 服务令牌缺失或与共享令牌不一致。
        '404':
          description: 项目不存在。
      security:
        - service-token: []
      summary: 项目的交付漏斗和部分 DORA 指标，供 PO 阅读
      tags:
        - internal
  /internal/projects/{projectId}/qa-environments/{taskId}/remove:
    post:
      description: >-
        Automatic, no proposal: it only reaches what carries the QA label (the
        broker guarantees it). Idempotent.
      operationId: InternalQaEnvironmentsController_remove
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
        - name: taskId
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/QaEnvironmentResponseDto'
        '403':
          description: 服务令牌缺失或与共享令牌不一致。
        '404':
          description: Project or task not found.
      security:
        - service-token: []
      summary: Removes the QA instance of a task (RN-936)
      tags:
        - internal
  /internal/projects/{projectId}/qa-environments/{taskId}/start:
    post:
      description: >-
        Records the row, then asks the broker. `sucesso: false` is the NORMAL
        shape of a refused broker. At most ONE active instance per project:
        another task active -> 409 `instancia_de_qa_ocupada`. `runner` projects
        -> 409 `qa_nao_suporta_runner`.
      operationId: InternalQaEnvironmentsController_start
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
        - name: taskId
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/QaEnvironmentResponseDto'
        '403':
          description: 服务令牌缺失或与共享令牌不一致。
        '404':
          description: Project or task (of that project) not found.
        '409':
          description: Busy instance, runner project or ambiguous task key.
      security:
        - service-token: []
      summary: Brings up the ephemeral QA instance of a task (RN-935)
      tags:
        - internal
  /internal/projects/{projectId}/workspace-verification:
    post:
      description: >-
        仅由 engine 调用，在 runner 连接并通过通道发送 `workspace_confirm` 之后。runner
        是路径的事实来源——api 在对其进行词法重新校验后（即使来自 runner，系统根目录/与 Brabo 重叠仍被禁止），用它报告的值覆盖
        `workspacePath`。幂等：以相同路径重新连接不会再次写入。
      operationId: InternalProjectsController_confirmWorkspaceVerification
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ConfirmProjectWorkspaceInternalDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ConfirmProjectWorkspaceResponseDto'
        '400':
          description: 路径在词法上无效，或项目不处于 “runner” 模式。
        '403':
          description: 服务令牌缺失或与共享令牌不一致。
        '404':
          description: 项目不存在。
      security:
        - service-token: []
      summary: runner 确认 “runner” 项目的路径（RN-423）
      tags:
        - internal
  /internal/qa-environments/sweep:
    post:
      description: >-
        The engine sends the task ids it still has in flight; every other active
        or failed instance is removed. Shared services and the QA network have
        no broker operation yet (declared gap).
      operationId: InternalQaEnvironmentsController_sweep
      parameters: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SweepQaEnvironmentsInternalDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SweepQaEnvironmentsResponseDto'
        '400':
          description: >-
            请求体无效。`ValidationPipe` 以 `whitelist` 和 `forbidNonWhitelisted`
            运行，因此未知字段也会失败。
        '403':
          description: 服务令牌缺失或与共享令牌不一致。
      security:
        - service-token: []
      summary: Removes QA instances whose gate is no longer in flight (RN-937)
      tags:
        - internal
  /internal/rag/feedback:
    post:
      description: >-
        与人工路由相同的用例——没有第二条判定路径。未知的 `searchId`/`chunkId` 返回 400，engine
        会将其转为错误的工具结果供模型纠正（RN-061），绝不会崩溃。
      operationId: InternalRagController_votar
      parameters: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/RagFeedbackInternalDto'
      responses:
        '201':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RagFeedbackInternalResponseDto'
        '400':
          description: 此项目未知的 `searchId`，或 `chunkId` 不在该次搜索的命中结果中。
        '403':
          description: 服务令牌缺失或与共享令牌不一致。
      security:
        - service-token: []
      summary: 代理对一个检索到的分块的投票（RN-480）
      tags:
        - internal
  /internal/rag/search:
    post:
      description: >-
        与 `POST /projects/:projectId/rag/search`（人工路由）相同的引擎，投射成 engine 的
        `rag_search` 工具所期望的格式。
      operationId: InternalRagController_buscar
      parameters: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/RagSearchInternalDto'
      responses:
        '201':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RagSearchInternalResponseDto'
        '400':
          description: >-
            请求体无效。`ValidationPipe` 以 `whitelist` 和 `forbidNonWhitelisted`
            运行，因此未知字段也会失败。
        '403':
          description: 服务令牌缺失或与共享令牌不一致。
      security:
        - service-token: []
      summary: 在项目的 RAG 索引上进行混合搜索，供 engine 的工具使用
      tags:
        - internal
  /internal/sessions/{sessionId}/actions:
    post:
      description: 代理接触 git、终端或花费的唯一入口。`permissions.json` 在创建时做出决定，`deny` 优先于授予代理的任何自主权。
      operationId: InternalSessionsController_createAction
      parameters:
        - name: sessionId
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateActionInternalDto'
      responses:
        '201':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProposedActionResponseDto'
        '400':
          description: 请求体无效。
        '403':
          description: 服务令牌缺失或与共享令牌不一致。
        '404':
          description: 未找到会话、项目或资源。
      security:
        - service-token: []
      summary: 代表代理提议一个具有外部效果的操作
      tags:
        - internal
  /internal/sessions/{sessionId}/anamnese-context:
    get:
      description: >-
        一次调用拿到全部，与 Psychologist
        的上下文对应。成员列表已排除选择不被画像的人，而窗口内用户的决定通过这里传递，因为它们不在事件日志中。engine 直接从 Postgres
        读取事件本身——通过 HTTP 传输会更昂贵，却不会更正确。
      operationId: InternalSessionsController_anamneseContext
      parameters:
        - name: projectId
          required: true
          in: query
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AnamneseContextResponseDto'
        '400':
          description: 请求体无效。
        '403':
          description: 服务令牌缺失或与共享令牌不一致。
        '404':
          description: 未找到会话、项目或资源。
      security:
        - service-token: []
      summary: 为一轮 Anamnesis 组装上下文
      tags:
        - internal
  /internal/sessions/{sessionId}/anamnese-window:
    post:
      description: >-
        记录一次零画像的 anamnese 运行，使下一轮从 windowTo 开始——没有它，窗口永远不会前进，每次 tick
        都要为同一批材料付费运行新一轮。
      operationId: InternalSessionsController_anamneseWindow
      parameters:
        - name: sessionId
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CloseAnamneseWindowInternalDto'
      responses:
        '201':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RecordProficiencyResponseDto'
        '400':
          description: 请求体无效。
        '403':
          description: 服务令牌缺失或与共享令牌不一致。
        '404':
          description: 未找到会话、项目或资源。
      security:
        - service-token: []
      summary: 关闭一轮未产出任何画像的 Anamnese 窗口
      tags:
        - internal
  /internal/sessions/{sessionId}/c4-diagram:
    post:
      description: >-
        制品就是 `artifact.c4_diagram` 事件：不可变、带版本、有作者，与 `artifact.module_map`
        并列。需要当前的 module_map——没有它就没有可绘制的 Container 层级（400）。
      operationId: InternalSessionsController_c4Diagram
      parameters:
        - name: sessionId
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateC4DiagramInternalDto'
      responses:
        '201':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/C4DiagramaGeradoResponseDto'
        '400':
          description: 缺少 `system_name`、参与者无效，或没有当前的 module_map。
        '403':
          description: 服务令牌缺失或与共享令牌不一致。
        '404':
          description: 未找到会话、项目或资源。
      security:
        - service-token: []
      summary: 生成 C4 图（Context + Container）的新版本
      tags:
        - internal
  /internal/sessions/{sessionId}/delegations:
    post:
      description: >-
        `completed`（附裁决）、`failed`（附来源）或 `dispensed`（附理由）——lead
        绝不会以半途的结果调用此路由：每次委派在这里诞生时就已经有了结论。
      operationId: InternalSessionsController_recordDelegationOutcome
      parameters:
        - name: sessionId
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/RecordDelegationInternalDto'
      responses:
        '201':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DelegationResponseDto'
        '400':
          description: 请求体无效。
        '403':
          description: 服务令牌缺失或与共享令牌不一致。
        '404':
          description: 未找到会话、项目或资源。
      security:
        - service-token: []
      summary: 记录一次区域委派的结果
      tags:
        - internal
  /internal/sessions/{sessionId}/dev-context:
    get:
      description: >-
        一次调用即可拿到提示词所需的一切：完整的 story、已解析的业务规则以及适用的 ADR。`module` 将 ADR 限定为该 dev
        的模块；未声明模块的 ADR 是横切的，始终包含在内。省略 `module` 会返回整个集合，这正是 QA 和 SecOps gate
        复用同一上下文时所需要的。
      operationId: InternalSessionsController_devContext
      parameters:
        - name: projectId
          required: true
          in: query
          schema:
            type: string
        - name: taskId
          required: true
          in: query
          schema:
            type: string
        - name: module
          required: false
          in: query
          schema:
            example: api
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DevTaskContextResponseDto'
        '400':
          description: 请求体无效。
        '403':
          description: 服务令牌缺失或与共享令牌不一致。
        '404':
          description: 未找到会话、项目或资源。
      security:
        - service-token: []
      summary: 为 dev agent 组装任务的完整上下文
      tags:
        - internal
  /internal/sessions/{sessionId}/epics:
    post:
      operationId: InternalSessionsController_epic
      parameters:
        - name: sessionId
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateEpicInternalDto'
      responses:
        '201':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EpicResponseDto'
        '400':
          description: 请求体无效。
        '403':
          description: 服务令牌缺失或与共享令牌不一致。
        '404':
          description: 未找到会话、项目或资源。
      security:
        - service-token: []
      summary: 创建一个 backlog epic
      tags:
        - internal
  /internal/sessions/{sessionId}/events:
    get:
      description: >-
        用于重新水合对话式代理的历史，并读取其启动所需的制品。对应的人工路由受 RBAC 保护；这一个受服务令牌保护。`latest=true`
        返回尾部（仍按 `seq` 升序）并忽略 `afterSeq`；`types` 将该页限定为这些事件类型。无论哪种情况，每页上限都是
        200（ADR 0060）。
      operationId: InternalSessionsController_listEvents
      parameters:
        - name: sessionId
          required: true
          in: path
          schema:
            type: string
        - name: projectId
          required: true
          in: query
          schema:
            type: string
        - name: afterSeq
          required: false
          in: query
          schema:
            example: 40
            type: string
        - name: limit
          required: false
          in: query
          schema:
            example: 200
            type: string
        - name: latest
          required: false
          in: query
          description: 获取日志尾部；忽略 `afterSeq`。
          schema:
            example: 'true'
            type: string
        - name: types
          required: false
          in: query
          description: 以逗号分隔的事件类型（最多 20 个）。只有这些类型的事件计入 `limit`。
          schema:
            example: artifact.product_brief,artifact.business_rule
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PaginaDeEventosResponseDto'
        '400':
          description: 请求体无效。
        '403':
          description: 服务令牌缺失或与共享令牌不一致。
        '404':
          description: 未找到会话、项目或资源。
      security:
        - service-token: []
      summary: 为 engine 分页读取会话的事件日志
      tags:
        - internal
    post:
      description: 与人工路由相同的用例和相同的原子 `seq` 分配。Psychologist 的假设不经过这里：它们有自己的路由，附带证据校验。
      operationId: InternalSessionsController_appendEvent
      parameters:
        - name: sessionId
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AppendSessionEventInternalDto'
      responses:
        '201':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SessionEventResponseDto'
        '400':
          description: 请求体无效。
        '403':
          description: 服务令牌缺失或与共享令牌不一致。
        '404':
          description: 未找到会话、项目或资源。
      security:
        - service-token: []
      summary: 代表代理向会话日志追加一个事件
      tags:
        - internal
  /internal/sessions/{sessionId}/gates/verdict:
    post:
      description: >-
        响应中的 `nextAction` 是 engine 所遵从的：`correct` 退回给 dev，`run_secops` 推进
        gate，`done` 将其交给用户，`await_user` 暂停周期直到未决问题得到回答（RN-860，不计入修正次数），`blocked`
        表示修正上限已用尽。合并绝不自动进行——`done` 只表示轮到人来操作了。
      operationId: InternalSessionsController_gateVerdict
      parameters:
        - name: sessionId
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/RecordGateVerdictInternalDto'
      responses:
        '201':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GateVerdictResponseDto'
        '400':
          description: 请求体无效。
        '403':
          description: 服务令牌缺失或与共享令牌不一致。
        '404':
          description: 未找到会话、项目或资源。
      security:
        - service-token: []
      summary: 记录 QA 或 SecOps 对任务 PR 的裁决
      tags:
        - internal
  /internal/sessions/{sessionId}/handoffs:
    post:
      description: >-
        以 `offered` 状态诞生。接受者是一个人，通过人工路由——代理不会激活代理。有两个例外。携带
        `product_brief`、由一次“我准备好了——需求已验证”点击所请求的 Creative→PO
        提议，会代表点击者被接受，以该人为参与者并在载荷中带有 `implicito`（RN-658，ADR 0185）。当 backlog
        已被覆盖（至少一条业务规则，且没有一条缺少 story）、仓库为 `local` 且项目中没有 git 凭据时，PO 到 Arquiteto 的
        handoff 由系统接受（RN-660，ADR 0186）；`aceiteAutomatico`
        说明是否发生，以及未发生的原因。无论哪种情况，响应随后都会带有 `status: accepted`。
      operationId: InternalSessionsController_handoff
      parameters:
        - name: sessionId
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateHandoffInternalDto'
      responses:
        '201':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OfertaInternaDeHandoffResponseDto'
        '400':
          description: 请求体无效。
        '403':
          description: 服务令牌缺失或与共享令牌不一致。
        '404':
          description: 未找到会话、项目或资源。
        '409':
          description: >-
            `agente_ja_ativo`（ADR 0182，RN-635）：目标已在该项目一个未关闭的会话中处于活跃状态。`message`
            是代理作为工具结果读取的文本。
      security:
        - service-token: []
      summary: 从一个代理向另一个代理提议 handoff
      tags:
        - internal
  /internal/sessions/{sessionId}/hypotheses:
    post:
      description: 每条假设都需要引用本会话中确实存在的事件——编造的证据会以 400 被拒绝，这正是假设与意见的区别。本会话的上一轮将被取代。
      operationId: InternalSessionsController_hypotheses
      parameters:
        - name: sessionId
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ProposeHypothesesInternalDto'
      responses:
        '201':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProposeHypothesesResponseDto'
        '400':
          description: 请求体无效。
        '403':
          description: 服务令牌缺失或与共享令牌不一致。
        '404':
          description: 未找到会话、项目或资源。
        '409':
          description: 本会话已存在一份当前分析。
      security:
        - service-token: []
      summary: 记录一轮分析及其假设
      tags:
        - internal
  /internal/sessions/{sessionId}/infra-artifacts/{prActionId}/files:
    get:
      description: 内容来自 `proposed_action` 自身的载荷：基础设施制品从不接触 worktree，与 ADR 相同。
      operationId: InternalSessionsController_infraPrFiles
      parameters:
        - name: projectId
          required: true
          in: query
          schema:
            type: string
        - name: prActionId
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/InfraPrFilesResponseDto'
        '400':
          description: 请求体无效。
        '403':
          description: 服务令牌缺失或与共享令牌不一致。
        '404':
          description: 未找到会话、项目或资源。
      security:
        - service-token: []
      summary: 返回基础设施 PR 的文件，供 gate 读取
      tags:
        - internal
  /internal/sessions/{sessionId}/infra-context:
    get:
      description: >-
        当前的 module_map 加上相关的基础设施 ADR，再加上 Architect 的模块路由（ADR 0131）——Infra Lead
        在提议 container_start 时从中选择的候选项。
      operationId: InternalSessionsController_infraContext
      parameters:
        - name: projectId
          required: true
          in: query
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/InfraContextResponseDto'
        '400':
          description: 请求体无效。
        '403':
          description: 服务令牌缺失或与共享令牌不一致。
        '404':
          description: 未找到会话、项目或资源。
      security:
        - service-token: []
      summary: 组装 InfraAgent 的初始上下文
      tags:
        - internal
  /internal/sessions/{sessionId}/infra-gates/verdict:
    post:
      description: 与 dev PR 相同的流水线和相同的状态机。
      operationId: InternalSessionsController_infraGateVerdict
      parameters:
        - name: sessionId
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/RecordInfraGateVerdictInternalDto'
      responses:
        '201':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/InfraGateVerdictResponseDto'
        '400':
          description: 请求体无效。
        '403':
          description: 服务令牌缺失或与共享令牌不一致。
        '404':
          description: 未找到会话、项目或资源。
      security:
        - service-token: []
      summary: 记录 QA 或 SecOps 对基础设施 PR 的裁决
      tags:
        - internal
  /internal/sessions/{sessionId}/instruction-patches:
    post:
      description: >-
        会变成 `proposed_action`，而不是直接写入：改变代理的行为是一种外部效果，要经过与其他一切相同的审批流水线。这闭合了假设 →
        补丁的循环。
      operationId: InternalSessionsController_instructionPatch
      parameters:
        - name: sessionId
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ProposeInstructionPatchInternalDto'
      responses:
        '201':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProposedActionResponseDto'
        '400':
          description: 请求体无效。
        '403':
          description: 服务令牌缺失或与共享令牌不一致。
        '404':
          description: 未找到会话、项目或资源。
      security:
        - service-token: []
      summary: 为代理的指令文件提议一个补丁
      tags:
        - internal
  /internal/sessions/{sessionId}/llm-turn:
    post:
      description: >-
        计量是强制的：每个回合都记录 `token_usage`，正是这一点让预算有意义。不记录任何事件——由 engine
        叙述事件日志。提供商失败时在 `error` 中以 200 返回，因为 `usage` 中的计费仍然有效：即使失败，该回合也产生了花费。
      operationId: InternalSessionsController_llmTurn
      parameters:
        - name: sessionId
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/RunLlmTurnDto'
      responses:
        '201':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/LlmTurnResponseDto'
        '400':
          description: 请求体无效。
        '403':
          description: 超出预算且 `policy=block`，或服务令牌无效。
        '404':
          description: 未找到会话、项目或资源。
      security:
        - service-token: []
      summary: 运行一个经过计量的 LLM 回合，支持工具
      tags:
        - internal
  /internal/sessions/{sessionId}/llm-turn-stream:
    post:
      description: 与 `llm-turn` 语义相同，逐帧交付。`done` 帧携带 `usage`——没有它，该回合将没有计费记录。
      operationId: InternalSessionsController_llmTurnStream
      parameters:
        - name: sessionId
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/StreamLlmTurnDto'
      responses:
        '200':
          description: 直到 `done` 或 `error` 的帧流。
          content:
            text/event-stream:
              schema:
                $ref: '#/components/schemas/LlmTurnStreamEventResponseDto'
        '400':
          description: 请求体无效。
        '403':
          description: 服务令牌缺失或与共享令牌不一致。
        '404':
          description: 未找到会话、项目或资源。
      security:
        - service-token: []
      summary: 运行一个以流式返回响应的 LLM 回合
      tags:
        - internal
  /internal/sessions/{sessionId}/max-parallel-proposals:
    post:
      description: 拒绝提议等于或低于当前值的上限：Anamnesis 周期性运行，否则每一轮都会重新提议同样的内容，用噪声填满用户需要阅读的队列。
      operationId: InternalSessionsController_maxParallelProposal
      parameters:
        - name: sessionId
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ProposeMaxParallelInternalDto'
      responses:
        '201':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProposedActionResponseDto'
        '400':
          description: 请求体无效。
        '403':
          description: 服务令牌缺失或与共享令牌不一致。
        '404':
          description: 未找到会话、项目或资源。
      security:
        - service-token: []
      summary: 提议提高某个区域的并行度上限
      tags:
        - internal
  /internal/sessions/{sessionId}/module-contracts:
    post:
      description: >-
        制品就是 `artifact.module_contracts` 事件：不可变、带版本、有作者，与 `artifact.module_map`
        并列。每次调用携带完整列表并替换上一版本。模块消费什么不在这里写：那是当前 module_map 的 `dependsOn`。
      operationId: InternalSessionsController_moduleContracts
      parameters:
        - name: sessionId
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DeclareModuleContractsInternalDto'
      responses:
        '201':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ContratosDeclaradosResponseDto'
        '400':
          description: >-
            列表为空、模块重复、模块不在当前 module_map 中（或没有 module_map）、模块的 `expoe` 为空或过大、条目的
            `tipo` 未知，或 `assinatura` 缺失/过大。
        '403':
          description: 服务令牌缺失或与共享令牌不一致。
        '404':
          description: 未找到会话、项目或资源。
      security:
        - service-token: []
      summary: 声明模块之间契约的新版本
      tags:
        - internal
  /internal/sessions/{sessionId}/module-map:
    post:
      description: >-
        历史不可变：每次发布都是新增一个版本，当前版本是 `version` 最高的那个。模块之间存在依赖环时映射会以 400
        被拒绝——图必须是无环的。
      operationId: InternalSessionsController_moduleMap
      parameters:
        - name: sessionId
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateModuleMapInternalDto'
      responses:
        '201':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ModuleMapResponseDto'
        '400':
          description: 模块之间存在依赖环。
        '403':
          description: 服务令牌缺失或与共享令牌不一致。
        '404':
          description: 未找到会话、项目或资源。
      security:
        - service-token: []
      summary: 发布 module_map 的新版本
      tags:
        - internal
  /internal/sessions/{sessionId}/module-routing:
    post:
      description: >-
        制品就是 `artifact.module_routing` 事件：不可变、带版本、有作者，与
        `artifact.module_map`/`artifact.project_image`/`artifact.c4_diagram`
        并列。每个条目都按与 `choose_project_image` 相同的规则校验（显式 tag/digest，拒绝
        `latest`，`rationale` 不能过于简单）；模块名不在当前 module_map 中、列表为空或模块重复，都会以 400 被拒绝。
      operationId: InternalSessionsController_moduleRouting
      parameters:
        - name: sessionId
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/RouteModulesToInfraInternalDto'
      responses:
        '201':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RoteamentoDeModulosGeradoResponseDto'
        '400':
          description: >-
            列表为空、模块重复、模块不在当前 module_map 中，或候选镜像不符合 `choose_project_image`
            的规则（没有显式 tag、`latest` 或 `rationale` 过短）。
        '403':
          description: 服务令牌缺失或与共享令牌不一致。
        '404':
          description: 未找到会话、项目或资源。
      security:
        - service-token: []
      summary: 将当前 module_map 的每个模块路由到一个候选镜像
      tags:
        - internal
  /internal/sessions/{sessionId}/pending-work:
    get:
      description: >-
        在心跳超时时由 engine 的 `SessionServer` 查询。`pending: true`
        会让超时被重新调度而不是关闭会话，`motivo` 会写入 engine 的日志——一个不说明原因就不关闭的会话是无法诊断的。
      operationId: InternalSessionsController_pendingWork
      parameters:
        - name: sessionId
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SessionPendingWorkResponseDto'
        '400':
          description: 请求体无效。
        '403':
          description: 服务令牌缺失或与共享令牌不一致。
        '404':
          description: 未找到会话、项目或资源。
      security:
        - service-token: []
      summary: 会话是否有阻止其关闭的待办工作？
      tags:
        - internal
  /internal/sessions/{sessionId}/proficiency:
    post:
      description: 目录之外的能力会被拒绝，引用其他项目事件的证据也会被拒绝——两种校验都存在，因为模型在这两点上都会出错。
      operationId: InternalSessionsController_proficiency
      parameters:
        - name: sessionId
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/RecordProficiencyInternalDto'
      responses:
        '201':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RecordProficiencyResponseDto'
        '400':
          description: 请求体无效。
        '403':
          description: 服务令牌缺失或与共享令牌不一致。
        '404':
          description: 未找到会话、项目或资源。
      security:
        - service-token: []
      summary: 记录本轮推导出的熟练度画像
      tags:
        - internal
  /internal/sessions/{sessionId}/project-image:
    post:
      description: >-
        制品就是 `artifact.project_image`
        事件：不可变、带版本、有作者。修订意味着发出新版本，绝不覆盖。在尚未有版本之前，项目的容器不会启动，Code 标签页返回 409。
      operationId: InternalSessionsController_projectImage
      parameters:
        - name: sessionId
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DecideProjectImageInternalDto'
      responses:
        '201':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ImagemDecididaResponseDto'
        '400':
          description: >-
            镜像没有显式 tag（或为 `latest`）、`rationale` 过短、`network` 不在 {none, egress}
            中，或某项资源超过上限。
        '403':
          description: 服务令牌缺失或与共享令牌不一致。
        '404':
          description: 未找到会话、项目或资源。
      security:
        - service-token: []
      summary: 设置项目的容器镜像
      tags:
        - internal
  /internal/sessions/{sessionId}/psychologist-context:
    get:
      description: >-
        `alreadyAnalyzed` 为自动路径提供幂等性：为 `true` 时 worker 直接短路，不花费任何
        token。之前的假设也会一并带上，以免本轮重复。
      operationId: InternalSessionsController_psychologistContext
      parameters:
        - name: sessionId
          required: true
          in: path
          schema:
            type: string
        - name: projectId
          required: true
          in: query
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PsychologistContextResponseDto'
        '400':
          description: 请求体无效。
        '403':
          description: 服务令牌缺失或与共享令牌不一致。
        '404':
          description: 未找到会话、项目或资源。
      security:
        - service-token: []
      summary: 为一轮 Psychologist 组装上下文
      tags:
        - internal
  /internal/sessions/{sessionId}/semantic-duplicate-check:
    post:
      description: >-
        用 RAG 嵌入模型对标题和项目规则的标题（最近 100 条）进行嵌入，余弦相似度 ≥
        阈值时发出警告。从不拒绝。没有嵌入提供商时检查会被跳过并说明原因，同样会在事件日志中叙述。嵌入花费作为单独的 `token_usage`
        记录计量（参与者 `system`/`duplicata-semantica`）。
      operationId: InternalSessionsController_semanticDuplicateCheck
      parameters:
        - name: sessionId
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SemanticDuplicateCheckInternalDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SemanticDuplicateCheckResponseDto'
        '400':
          description: 请求体无效。
        '403':
          description: 服务令牌缺失或与共享令牌不一致。
        '404':
          description: 未找到会话、项目或资源。
      security:
        - service-token: []
      summary: 当刚写下的业务规则与已有规则相似时发出警告
      tags:
        - internal
  /internal/sessions/{sessionId}/stories:
    post:
      description: >-
        `businessRuleIds` 为规则→story 覆盖提供数据。每个 id 必须引用一个确实存在的
        `artifact.business_rule` 事件——校验会拒绝编造的 id。响应还携带
        `semanticDuplicate`（RN-681）：一个基于嵌入、与项目 story 对比的警告，绝不是拒绝。
      operationId: InternalSessionsController_story
      parameters:
        - name: sessionId
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateStoryInternalDto'
      responses:
        '201':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CreatedStoryResponseDto'
        '400':
          description: 请求体无效。
        '403':
          description: 服务令牌缺失或与共享令牌不一致。
        '404':
          description: 未找到会话、项目或资源。
      security:
        - service-token: []
      summary: 创建一个带功能/非功能需求、DoD、DoR 和所覆盖规则的 story
      tags:
        - internal
  /internal/sessions/{sessionId}/stories/{storyId}/archive:
    post:
      description: >-
        该 story 及其任务离开 backlog、覆盖、计划和认领；记录保留，`backlog.story_archived` 记录此事，以 PO
        为参与者（RN-727）。
      operationId: InternalSessionsController_archiveStoryRoute
      parameters:
        - name: storyId
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ArchiveStoryInternalDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/StoryResponseDto'
        '400':
          description: 请求体无效。
        '403':
          description: 服务令牌缺失或与共享令牌不一致。
        '404':
          description: story 不在此项目中。
        '409':
          description: >-
            具名拒绝（`reason`）：`historia_arquivada`、`historia_nao_draft` 或
            `historia_com_tarefa_em_execucao`。
      security:
        - service-token: []
      summary: 归档一个草稿 story
      tags:
        - internal
  /internal/sessions/{sessionId}/stories/{storyId}/complete:
    post:
      description: >-
        `businessRuleIds` 会追加到已关联的规则中；非空列表替换当前列表，省略的列表保持不变。写入后，story 按与创建 story
        相同的标准提升（`ready` 用于 `auto` 模式，`proposedReady` 用于 `manual` 模式）（RN-720）。
      operationId: InternalSessionsController_completeStoryRoute
      parameters:
        - name: sessionId
          required: true
          in: path
          schema:
            type: string
        - name: storyId
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CompleteStoryInternalDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/StoryResponseDto'
        '400':
          description: 某个 business_rule_id 未引用已存在的规则。
        '403':
          description: 服务令牌缺失或与共享令牌不一致。
        '404':
          description: story 不在此项目中。
        '409':
          description: story 已不再是 `draft`。
      security:
        - service-token: []
      summary: 补全一个已有的草稿 story，而不是重新创建
      tags:
        - internal
  /internal/sessions/{sessionId}/stories/{storyId}/update:
    post:
      description: >-
        仅限没有执行中任务的 `draft` story；以 PO 为参与者写入 `backlog.story_updated`（RN-727）。状态为
        `ready` 的也可以，前提是 `questionId` 引用了一个用户已回答的未决问题（RN-859）。
      operationId: InternalSessionsController_updateStoryRoute
      parameters:
        - name: storyId
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateStoryInternalDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/StoryResponseDto'
        '400':
          description: 没有任何字段，或标题为空。
        '403':
          description: 服务令牌缺失或与共享令牌不一致。
        '404':
          description: story 不在此项目中。
        '409':
          description: >-
            具名拒绝（`reason`）：`historia_arquivada`、`historia_nao_draft` 或
            `historia_com_tarefa_em_execucao`。
      security:
        - service-token: []
      summary: 修正草稿 story 的标题和/或描述
      tags:
        - internal
  /internal/sessions/{sessionId}/story-modules:
    post:
      description: 这是交叉校验：当前映射中不存在的模块会变成一项架构待办，而不是悄无声息地溜过去。
      operationId: InternalSessionsController_storyModules
      parameters:
        - name: sessionId
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AssignStoryModulesInternalDto'
      responses:
        '201':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/StoryResponseDto'
        '400':
          description: 请求体无效。
        '403':
          description: 服务令牌缺失或与共享令牌不一致。
        '404':
          description: 未找到会话、项目或资源。
      security:
        - service-token: []
      summary: 将 story 与它所涉及的模块关联
      tags:
        - internal
  /internal/sessions/{sessionId}/tasks:
    post:
      operationId: InternalSessionsController_task
      parameters:
        - name: sessionId
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateTaskInternalDto'
      responses:
        '201':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TaskResponseDto'
        '400':
          description: 请求体无效。
        '403':
          description: 服务令牌缺失或与共享令牌不一致。
        '404':
          description: 未找到会话、项目或资源。
      security:
        - service-token: []
      summary: 在 story 内创建任务
      tags:
        - internal
  /internal/sessions/{sessionId}/tasks/{taskId}/block:
    post:
      description: 没有自动解除阻塞：解除阻塞的是人，通过人工路由。正是这一点阻止了代理无限空转。
      operationId: InternalSessionsController_blockTask
      parameters:
        - name: sessionId
          required: true
          in: path
          schema:
            type: string
        - name: taskId
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/BlockTaskInternalDto'
      responses:
        '201':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TaskResponseDto'
        '400':
          description: 请求体无效。
        '403':
          description: 服务令牌缺失或与共享令牌不一致。
        '404':
          description: 未找到会话、项目或资源。
      security:
        - service-token: []
      summary: 将任务标记为阻塞，并附上原因
      tags:
        - internal
  /internal/sessions/{sessionId}/tasks/{taskId}/gate/open:
    post:
      description: 在 dev agent 的 PR 准备好评审时调用。
      operationId: InternalSessionsController_openGateEndpoint
      parameters:
        - name: sessionId
          required: true
          in: path
          schema:
            type: string
        - name: taskId
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/OpenGateInternalDto'
      responses:
        '201':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GateAbertoResponseDto'
        '400':
          description: 请求体无效。
        '403':
          description: 服务令牌缺失或与共享令牌不一致。
        '404':
          description: 未找到会话、项目或资源。
      security:
        - service-token: []
      summary: 开启任务的 gate 流水线，从 QA 开始
      tags:
        - internal
  /internal/sessions/{sessionId}/tasks/{taskId}/status:
    post:
      operationId: InternalSessionsController_markTaskStatus
      parameters:
        - name: sessionId
          required: true
          in: path
          schema:
            type: string
        - name: taskId
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/MarkTaskInternalDto'
      responses:
        '201':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TaskResponseDto'
        '400':
          description: 请求体无效。
        '403':
          description: 服务令牌缺失或与共享令牌不一致。
        '404':
          description: 未找到会话、项目或资源。
        '409':
          description: 无效的状态转换，或是其他代理的任务。
      security:
        - service-token: []
      summary: 随着工作推进移动任务状态
      tags:
        - internal
  /internal/sessions/{sessionId}/tasks/{taskId}/unblock:
    post:
      description: >-
        由 engine 的重试任务调用（最多 3 次，分别在 1、3 和 10 分钟后）。与人工解除阻塞相同的用例，参与者为
        `system`；已不再阻塞的任务保持不变。
      operationId: InternalSessionsController_unblockTaskAutomatically
      parameters:
        - name: sessionId
          required: true
          in: path
          schema:
            type: string
        - name: taskId
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UnblockTaskInternalDto'
      responses:
        '201':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TaskResponseDto'
        '400':
          description: 请求体无效。
        '403':
          description: 服务令牌缺失或与共享令牌不一致。
        '404':
          description: 未找到会话、项目或资源。
      security:
        - service-token: []
      summary: 在短暂的网络/提供商故障后解除任务阻塞
      tags:
        - internal
  /internal/sessions/{sessionId}/tasks/claim:
    post:
      description: 按设计是原子的：同一模块上有多个 dev agent 时，两次并发认领不可能返回同一个任务。没有可用任务时返回空，而不是错误。
      operationId: InternalSessionsController_claimTask
      parameters:
        - name: sessionId
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ClaimTaskInternalDto'
      responses:
        '201':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TaskResponseDto'
        '400':
          description: 请求体无效。
        '403':
          description: 服务令牌缺失或与共享令牌不一致。
        '404':
          description: 未找到会话、项目或资源。
      security:
        - service-token: []
      summary: 原子地认领该模块下一个可认领的任务
      tags:
        - internal
  /internal/sessions/{sessionId}/tasks/in-progress-by-dev:
    get:
      description: >-
        未阻塞且不在 PR gate 中；除非会话是执行会话，否则为空。engine 在停止执行时使用它，以阻塞那些没有存活 dev agent
        持有的任务。
      operationId: InternalSessionsController_tarefasEmCursoDeDev
      parameters:
        - name: sessionId
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/TarefaEmCursoDeDevResponseDto'
        '400':
          description: 请求体无效。
        '403':
          description: 服务令牌缺失或与共享令牌不一致。
        '404':
          description: 未找到会话、项目或资源。
      security:
        - service-token: []
      summary: 列出项目中由 dev agent 持有的进行中任务
      tags:
        - internal
  /internal/sessions/{sessionId}/termination:
    post:
      description: >-
        只有不是由 api 引起的终止才会到达这里——崩溃、kill、`heartbeat_timeout`、防御性关闭。由 api
        自己计划的停止不经过这里：engine 已经通过 outbox 得知，再报告回来只是回声。
      operationId: InternalSessionsController_report
      parameters:
        - name: sessionId
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ReportSessionTerminationDto'
      responses:
        '201':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SessionResponseDto'
        '400':
          description: 请求体无效。
        '403':
          description: 服务令牌缺失或与共享令牌不一致。
        '404':
          description: 未找到会话、项目或资源。
      security:
        - service-token: []
      summary: 报告会话进程已在 engine 中终止
      tags:
        - internal
  /live:
    get:
      description: >-
        故意不访问数据库。如果访问，一个缓慢的 Postgres 会让所有副本同时重启，把性能降级变成全面停机。Pod 由 `/health`
        从负载均衡中摘除。
      operationId: HealthController_live
      parameters: []
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HealthStatusResponseDto'
      summary: 存活检查——进程仍在处理请求
      tags:
        - infrastructure
  /llm/provider-capabilities:
    get:
      description: >-
        九个提供商能力（ADR 0041）的提供商层，读取自处理调用的同一批实例。只有经真实 API 验证的能力才为 `true`。界面在为
        binding 提供路由偏好之前从这里读取 `routingPreference`（ADR 0166）。
      operationId: ProviderCapabilitiesController_list
      parameters: []
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/ProviderCapabilitiesResponseDto'
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 列出每个 LLM 提供商能做什么，与模型无关
      tags:
        - llm
  /metrics:
    get:
      description: >-
        不是 JSON：而是 exposition 格式的
        `text/plain`。出于必要而公开（抓取器不携带令牌），并通过网络加以限制——生产环境的 Ingress
        在边缘阻止此路径，NetworkPolicy 只允许监控命名空间访问。
      operationId: MetricsController_scrape
      parameters: []
      responses:
        '200':
          description: exposition 格式的指标。
          content:
            text/plain:
              schema:
                type: string
              example: |
                # HELP oban_queue_depth Oban queue depth
                # TYPE oban_queue_depth gauge
                oban_queue_depth{queue="default",state="available"} 0
      summary: 以 Prometheus exposition 格式暴露指标
      tags:
        - infrastructure
  /projects/{projectId}:
    get:
      operationId: ProjectsController_get
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProjectResponseDto'
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在项目上的角色不足。
        '404':
          description: 项目不存在或对调用方不可见。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 按 id 返回项目
      tags:
        - projects
    patch:
      operationId: ProjectsController_update
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateProjectDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProjectResponseDto'
        '400':
          description: >-
            请求体无效。`ValidationPipe` 以 `whitelist` 和 `forbidNonWhitelisted`
            运行，因此未知字段也会失败。
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在项目上的角色不足。
        '404':
          description: 项目不存在或对调用方不可见。
        '409':
          description: 该 workspace 中已存在使用此 slug 的项目。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 修改项目的名称或 slug
      tags:
        - projects
    delete:
      description: 会连同其会话、事件日志和提议的操作一起删除。
      operationId: ProjectsController_remove
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProjectResponseDto'
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在项目上的角色不足。
        '404':
          description: 项目不存在或对调用方不可见。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 删除项目
      tags:
        - projects
  /projects/{projectId}/actions:
    get:
      description: >-
        `status` 为 `pending`（默认）或 `failed`。`actionType` 按类型过滤——例如 `git_merge`，供
        PR 标签页在不知道是哪个会话创建的情况下找到某个 PR 的合并提议。`status=failed` 要求提供
        `actionType`，并列出执行失败的操作——PR 标签页从中读取每个 PR 最近一次被拒绝的合并（合并冲突会携带
        `executionResult.conflictingFiles`）。
      operationId: ProjectActionsController_list
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
        - name: status
          required: false
          in: query
          schema:
            example: pending
            type: string
        - name: actionType
          required: false
          in: query
          schema:
            example: git_merge
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/ProposedActionResponseDto'
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在项目上的角色不足。
        '404':
          description: 未找到项目。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 列出整个项目中跨任意会话的待处理操作
      tags:
        - actions
  /projects/{projectId}/agent-areas:
    get:
      description: >-
        项目诞生时就带有三个区域（RN-094），因此即使还没有 `module_map`，列表也是完整的。激活执行所增加的是 dev
        区域的成员，每个模块一个。
      operationId: ExecutionController_listAreas
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/AreaDeAgentesResponseDto'
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在项目上的角色不足。
        '404':
          description: 未找到项目、会话或任务。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 项目的代理区域，以及每个 lead 的上限
      tags:
        - execution
  /projects/{projectId}/agent-areas/{key}/budget:
    put:
      description: >-
        需要 `maintainer`，理由与并行度上限相同：这是与项目和会话预算并列的独立、叠加的检查（ADR
        0110）——不是级联，也不替代二者中的任何一个。限额以美元传入，并在服务端转换为微美元，与 `BudgetsController`
        的约定相同。`null` 清除上限。
      operationId: ExecutionController_setBudget
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
        - name: key
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SetAreaBudgetDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AreaDeAgentesResponseDto'
        '400':
          description: '`limitUsd` 既不是 `null` 也不是 >= 0 的数字。'
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在项目上的角色不足。
        '404':
          description: 未找到项目、会话或任务。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 设置（或清除）某个区域的花费上限
      tags:
        - execution
  /projects/{projectId}/agent-areas/{key}/max-parallel:
    patch:
      description: >-
        需要 `maintainer` 而非
        `developer`，理由与激活执行相同：提高上限就是决定产品在不询问的情况下可以花多少。适用于后续请求——已在等待你决定的内容会继续等待。
      operationId: ExecutionController_setMaxParallel
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
        - name: key
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SetMaxParallelDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AreaDeAgentesResponseDto'
        '400':
          description: '`maxParallel` 不是 >= 1 的整数。'
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在项目上的角色不足。
        '404':
          description: 未找到项目、会话或任务。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 修改某个区域的并行度上限
      tags:
        - execution
  /projects/{projectId}/agent-autonomy:
    get:
      description: 只有明确记录的规则。在此没有记录的代理会回落到项目 `permissions.json` 的默认值。
      operationId: AgentAutonomyController_list
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/AgentAutonomyRuleResponseDto'
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在项目上的角色不足。
        '404':
          description: 未找到项目。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 列出授予每个代理的自主权
      tags:
        - actions
    put:
      description: >-
        按（代理，类型）upsert。它不会覆盖 `permissions.json`：已在 `deny`
        中的模式无论在此授予多少自主权都保持阻止。`actionType: "*"`
        即“自动模式”（RN-153）——对该代理任意操作类型的自主权；之后记录的特定规则在该类型上仍优先于通配符。
      operationId: AgentAutonomyController_set
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SetAgentAutonomyDto'
      responses:
        '204':
          description: 规则已记录。无响应体。
        '400':
          description: >-
            请求体无效。`ValidationPipe` 以 `whitelist` 和 `forbidNonWhitelisted`
            运行，因此未知字段也会失败。
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在项目上的角色不足。
        '404':
          description: 未找到项目。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 为某个操作类型设置代理的自主权
      tags:
        - actions
  /projects/{projectId}/agent-bindings/{agentSlug}:
    get:
      description: >-
        代理 → 区域 → 项目 → workspace 级联，不含会话：这是代理在项目上的配置，而不是某次特定对话的配置。`origin:
        "agent"` 表示该代理偏离了其区域的默认值；任何其他来源都表示它在继承。
      operationId: ModelBindingsController_getAgentBinding
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
        - name: agentSlug
          required: true
          in: path
          schema:
            example: dev-api
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ResolvedBindingResponseDto'
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在该范围上的角色不足。
        '404':
          description: 未找到该范围。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 解析代理使用哪个模型，以及它的来源
      tags:
        - llm
    put:
      description: >-
        适用于本项目中所有没有自身 binding 的会话，且仅适用于这些会话——在 ADR 0064 之前 binding
        是全局的，也会影响其他项目。正是这样才能在同一项目中给 Psychologist 一个便宜的模型、给 Architect
        一个昂贵的模型，也正是这样代理才能偏离其区域的默认值。
      operationId: ModelBindingsController_setAgentBinding
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
        - name: agentSlug
          required: true
          in: path
          schema:
            example: dev-api
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SetModelBindingDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ModelBindingResponseDto'
        '400':
          description: >-
            请求体无效。`ValidationPipe` 以 `whitelist` 和 `forbidNonWhitelisted`
            运行，因此未知字段也会失败。
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在该范围上的角色不足。
        '404':
          description: 未找到该范围。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 在本项目中固定代理的模型
      tags:
        - llm
    delete:
      description: >-
        删除代理的
        binding——而不是把区域的模型复制进去。复制在界面上看起来一样，实则不然：它会变成一份副本，区域的下一次变更会悄无声息地把这个代理落下。已在继承时返回
        404。
      operationId: ModelBindingsController_clearAgentBinding
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
        - name: agentSlug
          required: true
          in: path
          schema:
            example: dev-api
            type: string
      responses:
        '204':
          description: 代理已恢复继承。无响应体。
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在该范围上的角色不足。
        '404':
          description: 未找到该范围。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 让代理恢复继承区域的模型
      tags:
        - llm
  /projects/{projectId}/agent-costs:
    get:
      description: >-
        一个滑动的 30 天窗口，而不是当前自然月：这是界面显示的标签，若用自然月，估算会在每月 1 日因翻页而骤降，而不是因为使用量变化。只计入
        `actor_kind = agent`（RN-038）。从未运行过的代理不会出现在列表中——界面为其显示一个短横线，这与零不同。
      operationId: BudgetsController_getProjectAgentCosts
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/AgentTokenUsageResponseDto'
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在项目上的角色不足。
        '404':
          description: 未找到项目或会话。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 按代理细分项目在过去 30 天的花费
      tags:
        - llm
  /projects/{projectId}/agents/{agent}/instruction-versions:
    get:
      description: 最新的在前，每个都已携带与上一版本的差异，由服务端计算。
      operationId: AnamneseController_versions
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
        - name: agent
          required: true
          in: path
          schema:
            example: dev-api
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/InstructionVersionResponseDto'
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在项目上的角色不足。
        '404':
          description: 项目、事件或版本不存在。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 列出代理指令的各个版本
      tags:
        - anamnesis
  /projects/{projectId}/agents/{agent}/instruction-versions/{version}/rollback:
    post:
      description: 历史不可变：恢复不会删除任何东西，而是以旧内容创建一个新版本。需要 `maintainer`，因为它会从此改变代理的行为——与补丁的校准相同。
      operationId: AnamneseController_rollbackVersion
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
        - name: agent
          required: true
          in: path
          schema:
            example: dev-api
            type: string
        - name: version
          required: true
          in: path
          description: 要恢复的版本。
          schema:
            example: 2
            type: string
      responses:
        '201':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RollbackResponseDto'
        '400':
          description: 版本不是正整数。
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在项目上的角色不足。
        '404':
          description: 项目、事件或版本不存在。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 恢复代理指令的某个先前版本
      tags:
        - anamnesis
  /projects/{projectId}/anamnese/run:
    post:
      description: 需要 `maintainer`，理由与 Psychologist 重新分析相同：它运行 ToolLoop 并花费真实预算。
      operationId: AnamneseController_run
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
      responses:
        '201':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OkResponseDto'
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在项目上的角色不足。
        '404':
          description: 项目、事件或版本不存在。
        '429':
          description: 按用户或按 IP 的速率限制。
        '503':
          description: 'Anamnese 已因用户的决定被全局禁用（不是缺陷）——响应体带有 `reason: "anamnese_disabled"`。'
      security:
        - bearer: []
      summary: 立即运行 Anamnese，无需等待 tick
      tags:
        - anamnesis
  /projects/{projectId}/architecture:
    get:
      description: >-
        当前映射是 `version` 最高的那个（历史不可变）。待办项是 story↔映射的交叉校验：没有模块的 story，或指向不存在模块的
        story。
      operationId: BacklogController_architecture
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ArchitectureResponseDto'
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在项目上的角色不足。
        '404':
          description: 项目不存在。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 返回当前的 module_map、ADR 以及待办项
      tags:
        - backlog
  /projects/{projectId}/area-bindings/{areaKey}:
    get:
      description: >-
        lead 与区域子代理共享的默认值。`origin: "area"` 表示有人为这个区域做了选择；任何其他来源都表示区域本身从项目或
        workspace 继承。
      operationId: ModelBindingsController_getAreaBinding
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
        - name: areaKey
          required: true
          in: path
          schema:
            example: qa
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ResolvedBindingResponseDto'
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在该范围上的角色不足。
        '404':
          description: 未找到该范围。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 解析区域的默认模型是什么，以及它的来源
      tags:
        - llm
    put:
      description: >-
        适用于 lead 以及该区域中所有没有自身 binding 的子代理（RN-102）。需要
        `maintainer`，因为它一次影响整个区域，而选择模型就是决定花费——与并行度上限的理由相同。
      operationId: ModelBindingsController_setAreaBinding
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
        - name: areaKey
          required: true
          in: path
          schema:
            example: qa
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SetModelBindingDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ModelBindingResponseDto'
        '400':
          description: >-
            请求体无效。`ValidationPipe` 以 `whitelist` 和 `forbidNonWhitelisted`
            运行，因此未知字段也会失败。
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在该范围上的角色不足。
        '404':
          description: 未找到该范围。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 设置区域的默认模型
      tags:
        - llm
    delete:
      description: >-
        区域不再有自己的默认值，开始从项目（或 workspace）继承。已偏离的代理继续偏离：它们的 binding
        是另一个，删除这一个无法替它们做决定。
      operationId: ModelBindingsController_clearAreaBinding
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
        - name: areaKey
          required: true
          in: path
          schema:
            example: qa
            type: string
      responses:
        '204':
          description: 区域已恢复继承。无响应体。
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在该范围上的角色不足。
        '404':
          description: 未找到该范围。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 让区域恢复继承项目的模型
      tags:
        - llm
  /projects/{projectId}/backlog:
    get:
      description: 嵌套且完整，一次调用即可。已归档的 story（RN-727）不会列出。backlog 由代理通过 `/internal/*` 路由写入。
      operationId: BacklogController_backlog
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/EpicComHistoriasResponseDto'
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在项目上的角色不足。
        '404':
          description: 项目不存在。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 返回 epic → story → task 树
      tags:
        - backlog
  /projects/{projectId}/budget:
    get:
      description: 数值以微美元计。需要 `maintainer`——这是成本信息。
      operationId: BudgetsController_getProjectBudget
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BudgetResponseDto'
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在项目上的角色不足。
        '404':
          description: 未找到项目或会话。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 读取项目预算以及已花费的金额
      tags:
        - llm
    put:
      description: 限额以美元传入并转换为微美元。在 `policy=block` 下，会超出上限的调用会被拒绝，而不仅仅是被记录。
      operationId: BudgetsController_setProjectBudget
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpsertBudgetDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BudgetResponseDto'
        '400':
          description: >-
            请求体无效。`ValidationPipe` 以 `whitelist` 和 `forbidNonWhitelisted`
            运行，因此未知字段也会失败。
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在项目上的角色不足。
        '404':
          description: 未找到项目或会话。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 设置项目花费上限
      tags:
        - llm
  /projects/{projectId}/code/blame:
    get:
      description: >-
        为已声明的 blame 待办项（PHASE 26b）打基础——逐行标注编辑器的界面尚不存在。当文件超过标注行数上限时，`truncated`
        会发出提示。
      operationId: CodeController_blame
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
        - name: path
          required: true
          in: query
          description: 要标注的文件路径。
          schema:
            type: string
        - name: ref
          required: false
          in: query
          description: 分支、tag 或 sha。缺省时使用仓库的默认分支。
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CodeBlameResponseDto'
        '400':
          description: 路径超出项目范围（RN-095）、ref 格式错误，或搜索词过短或过长。
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在项目上的角色不足。
        '404':
          description: 项目没有已置备的仓库，或 ref/路径/PR 不存在。
        '429':
          description: 按用户或按 IP 的速率限制。
        '501':
          description: >-
            项目的提供商没有声明该操作的能力——`listTree`、`pullRequestDiff`、`blame`、`pullRequestsList`
            或 `branchesDetailed`。能力只有在契约测试套件验证后才会声明，因此这是合法的响应，而非缺陷。
      security:
        - bearer: []
      summary: 为文件的每一行标注最后修改它的提交
      tags:
        - git
  /projects/{projectId}/code/branches:
    get:
      description: >-
        为丰富的下拉菜单（PHASE 26b）打基础——目前 ref 选择器是普通文本框。`ahead`/`behind`
        相对于仓库的默认分支；提供商无法计算时为 `null`（诚实的降级，而非错误）。它不是 `listBranches`（Gitflow
        引导使用的那个）——而是一个独立的操作，因为丰富信息需要为每个分支额外调用一次提供商。
      operationId: CodeController_branches
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CodeBranchDetailListResponseDto'
        '400':
          description: 路径超出项目范围（RN-095）、ref 格式错误，或搜索词过短或过长。
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在项目上的角色不足。
        '404':
          description: 项目没有已置备的仓库，或 ref/路径/PR 不存在。
        '429':
          description: 按用户或按 IP 的速率限制。
        '501':
          description: >-
            项目的提供商没有声明该操作的能力——`listTree`、`pullRequestDiff`、`blame`、`pullRequestsList`
            或 `branchesDetailed`。能力只有在契约测试套件验证后才会声明，因此这是合法的响应，而非缺陷。
      security:
        - bearer: []
      summary: 列出带 ahead/behind 和关联的打开 PR 的分支
      tags:
        - git
  /projects/{projectId}/code/file:
    get:
      description: >-
        UTF-8，超过字节上限时截断并带 `truncated: true`。逃出项目文件夹的路径会被 RN-095 的中央检查以 400
        拒绝——绝不用 404，那会招来探测。
      operationId: CodeController_file
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
        - name: path
          required: true
          in: query
          description: 文件路径。
          schema:
            type: string
        - name: ref
          required: false
          in: query
          description: 分支、tag 或 sha。
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CodeFileResponseDto'
        '400':
          description: 路径超出项目范围（RN-095）、ref 格式错误，或搜索词过短或过长。
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在项目上的角色不足。
        '404':
          description: 项目没有已置备的仓库，或 ref/路径/PR 不存在。
        '429':
          description: 按用户或按 IP 的速率限制。
        '501':
          description: >-
            项目的提供商没有声明该操作的能力——`listTree`、`pullRequestDiff`、`blame`、`pullRequestsList`
            或 `branchesDetailed`。能力只有在契约测试套件验证后才会声明，因此这是合法的响应，而非缺陷。
      security:
        - bearer: []
      summary: 返回文件内容
      tags:
        - git
  /projects/{projectId}/code/pull-requests:
    get:
      description: >-
        为可导航列表（PHASE 26b）打基础——目前 diff 只能通过已知 id 访问（例如从 Approvals 进入）；此路由解决了无需记住
        id 也能找到它的问题。当列表超过单次调用上限时，`truncated` 会发出提示。
      operationId: CodeController_pullRequests
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
        - name: state
          required: false
          in: query
          description: 缺省时列出所有状态。
          schema:
            enum:
              - open
              - merged
              - closed
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CodePullRequestListResponseDto'
        '400':
          description: 路径超出项目范围（RN-095）、ref 格式错误，或搜索词过短或过长。
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在项目上的角色不足。
        '404':
          description: 项目没有已置备的仓库，或 ref/路径/PR 不存在。
        '429':
          description: 按用户或按 IP 的速率限制。
        '501':
          description: >-
            项目的提供商没有声明该操作的能力——`listTree`、`pullRequestDiff`、`blame`、`pullRequestsList`
            或 `branchesDetailed`。能力只有在契约测试套件验证后才会声明，因此这是合法的响应，而非缺陷。
      security:
        - bearer: []
      summary: 列出仓库的 PR/MR
      tags:
        - git
  /projects/{projectId}/code/pull-requests/{pullRequestId}/diff:
    get:
      description: >-
        `patch` 为 null
        用于区分提供商没有交付文本（二进制文件或补丁过大）与文本返回为空这两种情况。把两者混为一谈，界面就会说一个已更改的二进制文件没有变化。当文件列表超过上限时，`truncated`
        字段会发出提示。
      operationId: CodeController_diff
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
        - name: pullRequestId
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CodeDiffResponseDto'
        '400':
          description: 路径超出项目范围（RN-095）、ref 格式错误，或搜索词过短或过长。
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在项目上的角色不足。
        '404':
          description: 项目没有已置备的仓库，或 ref/路径/PR 不存在。
        '429':
          description: 按用户或按 IP 的速率限制。
        '501':
          description: >-
            项目的提供商没有声明该操作的能力——`listTree`、`pullRequestDiff`、`blame`、`pullRequestsList`
            或 `branchesDetailed`。能力只有在契约测试套件验证后才会声明，因此这是合法的响应，而非缺陷。
      security:
        - bearer: []
      summary: 返回 PR 的 diff，已在各提供商之间统一格式
      tags:
        - git
  /projects/{projectId}/code/search:
    get:
      description: >-
        搜索不是 git
        契约中的操作：它是在树和内容之上组合而成的，因此是唯一一个成本随仓库大小增长的读取。三项预算为其设限——遍历的目录数、打开的文件数和返回的匹配数——`truncated`
        表示它已停止。`filesScanned` 显示它产生的成本。
      operationId: CodeController_search
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
        - name: q
          required: true
          in: query
          description: 搜索词，2 到 200 个字符。
          schema:
            type: string
        - name: ref
          required: false
          in: query
          description: 分支、tag 或 sha。
          schema:
            type: string
        - name: path
          required: false
          in: query
          description: 要搜索的子树。缩小范围才能避免截断。
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CodeSearchResponseDto'
        '400':
          description: 路径超出项目范围（RN-095）、ref 格式错误，或搜索词过短或过长。
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在项目上的角色不足。
        '404':
          description: 项目没有已置备的仓库，或 ref/路径/PR 不存在。
        '429':
          description: 按用户或按 IP 的速率限制。
        '501':
          description: >-
            项目的提供商没有声明该操作的能力——`listTree`、`pullRequestDiff`、`blame`、`pullRequestsList`
            或 `branchesDetailed`。能力只有在契约测试套件验证后才会声明，因此这是合法的响应，而非缺陷。
      security:
        - bearer: []
      summary: 在仓库中搜索文本，带预算限制
      tags:
        - git
  /projects/{projectId}/code/tree:
    get:
      description: 按契约设计不递归：标签页按需导航，而请求大型仓库的整棵树正是本阶段禁止的流量放大器。当该层级超过条目上限时，`truncated` 会发出提示。
      operationId: CodeController_tree
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
        - name: ref
          required: false
          in: query
          description: 分支、tag 或 sha。缺省时使用仓库的默认分支。
          schema:
            type: string
        - name: path
          required: false
          in: query
          description: 要列出的目录。缺省或为空即根目录。
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CodeTreeResponseDto'
        '400':
          description: 路径超出项目范围（RN-095）、ref 格式错误，或搜索词过短或过长。
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在项目上的角色不足。
        '404':
          description: 项目没有已置备的仓库，或 ref/路径/PR 不存在。
        '429':
          description: 按用户或按 IP 的速率限制。
        '501':
          description: >-
            项目的提供商没有声明该操作的能力——`listTree`、`pullRequestDiff`、`blame`、`pullRequestsList`
            或 `branchesDetailed`。能力只有在契约测试套件验证后才会声明，因此这是合法的响应，而非缺陷。
      security:
        - bearer: []
      summary: 列出仓库树的一个层级
      tags:
        - git
  /projects/{projectId}/container:
    get:
      description: >-
        当状态为 `sem_decisao` 时，项目容器不会启动，Code 标签页返回 409——这就是 RN-105 的
        gate。查看该状态所需的权限与查看项目相同，即 `viewer`。
      operationId: ContainersController_estado
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EstadoDoContainerResponseDto'
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在项目上的角色不足。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 项目容器的状态（当前的镜像决定）
      tags:
        - projects
  /projects/{projectId}/container/lifecycle:
    get:
      description: >-
        项目从未置备时为
        `null`——这是目前的常见情况，因为还没有真正的编排器转换这张表（RN-243/RN-267）。`status`/`imageVersion`/`resources`
        是登记的内容；`observado` 是 Docker 守护进程此刻报告的内容，经由 broker 查询（ADR
        0130）。两者绝不合并：从外部被杀掉的容器读起来是登记为 `running`、观测为 `exited`。当无法查看时，`observado`
        为 `null`，`naoObservado` 说明原因——它绝不继承登记状态（RN-468）。
      operationId: ContainersController_cicloDeVida
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: 登记的生命周期，或 `null`（从未置备）。
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CicloDeVidaDoContainerResponseDto'
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在项目上的角色不足。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 项目容器的生命周期（登记状态）
      tags:
        - projects
  /projects/{projectId}/container/network:
    get:
      description: >-
        The current declaration (ports on 127.0.0.1, DNS, extra hosts), or
        `null`. It is what was DECLARED; it only takes effect when the container
        is recreated (`container_recreate`).
      operationId: ContainersController_rede
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: Or `null`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RedeDoContainerResponseDto'
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在项目上的角色不足。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: Declared network of the container (artifact.container_network)
      tags:
        - projects
    put:
      description: >-
        Emits a new version of `artifact.container_network` (the current one
        replaces the previous). Not an external effect: nothing changes in
        Docker until a `container_recreate` is approved (AT-608, RN-932). 400
        `rede_invalida` names the field.
      operationId: ContainersController_declarar
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DeclararRedeDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RedeDoContainerResponseDto'
        '400':
          description: >-
            请求体无效。`ValidationPipe` 以 `whitelist` 和 `forbidNonWhitelisted`
            运行，因此未知字段也会失败。
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在项目上的角色不足。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: Declares the container network (human only)
      tags:
        - projects
  /projects/{projectId}/coverage:
    get:
      description: >-
        将项目的规则与 story 的 `businessRuleIds` 交叉比对。完全没有 story 的规则是一项发现——是 PO
        的待办项，而不是错误。
      operationId: BacklogController_coverage
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CoverageReportResponseDto'
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在项目上的角色不足。
        '404':
          description: 项目不存在。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 计算业务规则 → story 的覆盖情况
      tags:
        - backlog
  /projects/{projectId}/events/{eventId}:
    get:
      description: 与按会话查找事件的路由不同：这里不知道会话。正是这一点让画像的证据标签落在正确的事件上，因为 Anamnese 的窗口跨越多个会话。
      operationId: AnamneseController_event
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
        - name: eventId
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SessionEventResponseDto'
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在项目上的角色不足。
        '404':
          description: 项目、事件或版本不存在。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 按 id 返回项目事件，并解析其所属会话
      tags:
        - anamnesis
  /projects/{projectId}/execution-mode:
    put:
      description: >-
        为已存在的项目迁移 `execution_mode`/`workspacePath`（RN-447..450，ADR 0111）——专用路由，与
        PATCH 分开，因为它编排的不只是写一个列：它把 permissions.json 移动到新的范围根目录，在离开 `container`
        模式时撤下容器生命周期记录，并且只要项目中有任何 dev agent 不处于空闲状态就拒绝（409），因为运行中的代理不会自行重新解析其
        worktree 位置。
      operationId: ProjectsController_convertExecutionModeRoute
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ConvertExecutionModeDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProjectResponseDto'
        '400':
          description: >-
            请求体无效。`ValidationPipe` 以 `whitelist` 和 `forbidNonWhitelisted`
            运行，因此未知字段也会失败。
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在项目上的角色不足。
        '404':
          description: 项目不存在或对调用方不可见。
        '409':
          description: 此项目的某个 dev agent 正在工作或卡住。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 转换项目的执行模式
      tags:
        - projects
  /projects/{projectId}/execution/activate:
    post:
      description: >-
        需要 `maintainer` 而非 `developer`，因为 dev agent 的 git 操作在 IAM 评估中继承激活者的角色——以
        developer 身份激活会让代理无法打开 PR。`module_map` 中的每个模块都会在隔离的 worktree 中获得一个代理。
      operationId: ExecutionController_activate
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ActivateExecutionDto'
      responses:
        '201':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ExecucaoAtivadaResponseDto'
        '400':
          description: >-
            没有当前的 `module_map`（Architect 必须先定义模块），或项目存储的 workspace
            位置不一致——无法从中推导出在此初始化的 `permissions.json`（RN-478）。
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在项目上的角色不足。
        '404':
          description: 未找到项目、会话或任务。
        '409':
          description: >-
            项目还没有仓库（RN-582）：dev agent 在其 worktree
            中工作，因此什么都不会启动。消息会指明缺少什么——其接受会置备仓库的、到 Architect（或作为第二道门，到 Dev Lead）的
            handoff，或者当该 handoff 已被接受却没有留下仓库时，指向置备页面。另外：来源会话为 `consultiva`，会拒绝
            `execution.activated`（RN-097）。激活两次不是冲突：它通过
            `findActiveExecutionSession` 实现幂等，并在同一会话内重新激活。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 激活执行阶段并为每个模块启动一个 dev agent
      tags:
        - execution
  /projects/{projectId}/execution/session:
    get:
      description: >-
        最近的 `active` 会话，且已携带 `execution.activated`——与 `activate`
        决定是重新激活还是新建时使用的标准相同（RN-139）。没有执行在进行时为 `null`。它的存在让 Executors
        标签页不再从项目最近的会话推断这个会话，那会随会话变化而变化，界面中毫无线索。
      operationId: ExecutionController_getSession
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: 没有活跃执行时响应体为 `null`。
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SessionResponseDto'
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在项目上的角色不足。
        '404':
          description: 未找到项目、会话或任务。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 返回项目当前的执行会话，或什么也不返回
      tags:
        - execution
  /projects/{projectId}/git/{provider}/connect:
    get:
      description: 不重定向：返回 URL，由客户端决定何时让浏览器跳转。`state` 经 HMAC 签名，正是这一点防止回调被伪造。
      operationId: GitController_connect
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
        - name: provider
          required: true
          in: path
          schema:
            enum:
              - github
              - gitlab
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GitAuthorizeUrlResponseDto'
        '400':
          description: 提供商不在 `github`/`gitlab` 之列。
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在项目上的角色不足。
        '404':
          description: 项目不存在。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 与 git 提供商开始 OAuth
      tags:
        - git
  /projects/{projectId}/git/{provider}/repository:
    post:
      description: >-
        同步执行：只有在整个引导过程（永久分支、保护规则、模板）运行完毕后才会返回响应——背后没有 worker
        也没有队列。它是幂等且可恢复的，因此失败后再次调用会从失败的步骤继续。通过 `GET /projects/:id/git/bootstrap`
        跟踪进度并读取失败原因。
      operationId: GitController_provision
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
        - name: provider
          required: true
          in: path
          schema:
            enum:
              - local
              - github
              - gitlab
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ProvisionRepositoryDto'
      responses:
        '201':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProvisionRepositoryResponseDto'
        '400':
          description: 提供商无效，或缺少凭据。
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在项目上的角色不足。
        '404':
          description: 项目不存在。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 创建仓库并运行 Gitflow 引导
      tags:
        - git
  /projects/{projectId}/git/{provider}/repository/adopt:
    post:
      description: >-
        用 `getRepo`
        校验访问权限，并生成引导将要执行的计划（dry-run）——缺失的分支、缺失的保护规则、文件。不会在仓库上执行任何操作：之后通过
        `plan/approve` 或 `plan/skip` 做决定。重新接入同一个仓库会收敛（重新生成计划而不会重复记录）。
      operationId: GitController_adopt
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
        - name: provider
          required: true
          in: path
          schema:
            enum:
              - local
              - github
              - gitlab
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AdoptRepositoryDto'
      responses:
        '201':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AdoptRepositoryResponseDto'
        '400':
          description: >-
            请求体无效。`ValidationPipe` 以 `whitelist` 和 `forbidNonWhitelisted`
            运行，因此未知字段也会失败。
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 仓库存在，但已登记的凭据无法访问它。
        '404':
          description: 该仓库在提供商上不存在——请检查标识符。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 接入一个已有仓库，不创建任何东西
      tags:
        - git
  /projects/{projectId}/git/bootstrap:
    get:
      description: 当前处于哪一步、是否失败以及在哪里失败、尝试了多少次。逐步的细节来自专用会话中的 `bootstrap.step_*` 事件。
      operationId: GitController_getBootstrap
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RepoBootstrapStatusResponseDto'
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在项目上的角色不足。
        '404':
          description: 项目不存在。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 返回 Gitflow 引导状态
      tags:
        - git
  /projects/{projectId}/git/bootstrap/acknowledge-protection-failure:
    post:
      description: >-
        `protect_branches`
        在免费计划的私有仓库上会失败，向导会事先就此发出警告。没有这个出口，唯一的按钮是“重试”，而它总会因同样的原因失败——`provision_failed`
        又会让仪表盘把项目重定向回置备页面，使其永远无法进入。只有保护步骤的失败可以被确认：它是最后一步，也是唯一一个失败后仍留下可用仓库的步骤。产品自身的合并锁（RN-006）不依赖提供商的保护，会继续生效。
      operationId: GitController_reconhecerFalhaDeProtecao
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
      responses:
        '201':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ReconhecerFalhaDeProtecaoResponseDto'
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在项目上的角色不足。
        '404':
          description: 项目不存在。
        '409':
          description: 引导没有失败，或在保护之前的步骤失败——在那里继续会让项目没有可用的仓库。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 确认分支保护失败并继续
      tags:
        - git
  /projects/{projectId}/git/bootstrap/plan:
    get:
      description: '存在计划且 `decision: null` 是什么都不会运行的状态——等待批准或按原样接入（RN-045）。'
      operationId: GitController_plan
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BootstrapPlanEstadoResponseDto'
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在项目上的角色不足。
        '404':
          description: 项目不存在。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 返回引导计划以及对它做出的决定
      tags:
        - git
  /projects/{projectId}/git/bootstrap/plan/approve:
    post:
      description: >-
        批准是全有或全无的：批准零散的步骤会破坏 `dev←main, qa←dev, rc←qa`
        级联。实际运行的是在执行时重新推导的计划——等于或小于所展示的，绝不会更大。
      operationId: GitController_approvePlan
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DecideBootstrapPlanDto'
      responses:
        '201':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DecideBootstrapPlanResponseDto'
        '400':
          description: >-
            请求体无效。`ValidationPipe` 以 `whitelist` 和 `forbidNonWhitelisted`
            运行，因此未知字段也会失败。
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在项目上的角色不足。
        '404':
          description: 项目不存在。
        '409':
          description: 自你上次查看后计划已重新生成，或它已被决定。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 批准整个计划并运行引导
      tags:
        - git
  /projects/{projectId}/git/bootstrap/plan/skip:
    post:
      description: 记录决定，不触碰仓库。计划作为刻意未应用内容的证据保留，引导游标也不会被改动——没有任何步骤运行。
      operationId: GitController_skipPlan
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DecideBootstrapPlanDto'
      responses:
        '201':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DecideBootstrapPlanResponseDto'
        '400':
          description: >-
            请求体无效。`ValidationPipe` 以 `whitelist` 和 `forbidNonWhitelisted`
            运行，因此未知字段也会失败。
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在项目上的角色不足。
        '404':
          description: 项目不存在。
        '409':
          description: 自你上次查看后计划已重新生成，或它已被决定。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 按原样接入仓库，放弃引导
      tags:
        - git
  /projects/{projectId}/git/repository:
    get:
      operationId: GitController_get
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProvisionedRepositoryResponseDto'
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在项目上的角色不足。
        '404':
          description: 项目不存在。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 返回项目已置备的仓库
      tags:
        - git
  /projects/{projectId}/hypotheses:
    get:
      description: 每条假设都引用支持它的事件（`evidenceEventIds`）——正是这一点让它可审计，而不是凭信任接受。
      operationId: PsychologistController_hypotheses
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/HypothesisResponseDto'
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在项目上的角色不足。
        '404':
          description: 项目、会话或假设不存在。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 列出 Psychologist 关于代理的假设
      tags:
        - psychologist
  /projects/{projectId}/hypotheses/{hypothesisId}/accept:
    post:
      description: >-
        将 `proposed` → `accepted`。不是 `proposed_action`：Psychologist
        是只读的，这里没有任何外部效果。
      operationId: PsychologistController_accept
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
        - name: hypothesisId
          required: true
          in: path
          schema:
            type: string
      responses:
        '201':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HypothesisResponseDto'
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在项目上的角色不足。
        '404':
          description: 项目、会话或假设不存在。
        '409':
          description: 该假设已被决定。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 接受一条假设
      tags:
        - psychologist
  /projects/{projectId}/hypotheses/{hypothesisId}/dismiss:
    post:
      description: 将 `proposed` → `dismissed`。理由与接受相同。
      operationId: PsychologistController_dismiss
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
        - name: hypothesisId
          required: true
          in: path
          schema:
            type: string
      responses:
        '201':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HypothesisResponseDto'
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在项目上的角色不足。
        '404':
          description: 项目、会话或假设不存在。
        '409':
          description: 该假设已被决定。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 驳回一条假设
      tags:
        - psychologist
  /projects/{projectId}/infra-artifacts:
    get:
      description: >-
        InfraAgent 的制品（Dockerfile、compose、CI）。它们经过与 dev PR 相同的 QA 和 SecOps
        gate，背后没有任务或 worktree。
      operationId: BacklogController_infraArtifacts
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/InfraArtifactResponseDto'
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在项目上的角色不足。
        '404':
          description: 项目不存在。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 列出基础设施 PR 以及每个 PR 所处的 gate 阶段
      tags:
        - backlog
  /projects/{projectId}/instruction-versions:
    get:
      description: 从在项目中拥有版本的代理出发，而不是从静态名单出发——正是这一点让按模块划分的 dev agent（`dev-api`）显示出来。
      operationId: AnamneseController_allVersions
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/AgenteComVersoesResponseDto'
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在项目上的角色不足。
        '404':
          description: 项目、事件或版本不存在。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 列出项目中每个代理的指令历史
      tags:
        - anamnesis
  /projects/{projectId}/members:
    get:
      description: 只包含在此处关联的人，以及该关联的角色——仅通过 workspace 进入项目的人没有记录，不会显示。
      operationId: ProjectsController_listMembers
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/ProjectMemberComUsuarioResponseDto'
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在项目上的角色不足。
        '404':
          description: 项目不存在或对调用方不可见。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 列出项目成员
      tags:
        - projects
    post:
      description: >-
        该角色会覆盖此人在 workspace 中的角色，两个方向都是：在这里把某人关联为 `viewer` 确实会在此项目上限制一个
        workspace `developer`。有两种变动会以 403 拒绝，且在任何地方都无法启用：降级一个 workspace
        `owner`，以及修改你自己的角色——无论降级还是升级，因为自我提升正是提权的那一半。
      operationId: ProjectsController_addMember
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AddMemberDto'
      responses:
        '201':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProjectMemberResponseDto'
        '400':
          description: >-
            请求体无效。`ValidationPipe` 以 `whitelist` 和 `forbidNonWhitelisted`
            运行，因此未知字段也会失败。
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: >-
            在项目上的角色不足，或者触发两条上限之一（目标是 workspace
            `owner`；目标是调用方本人且所请求的角色与其当前有效角色不同）。
        '404':
          description: 项目不存在或对调用方不可见。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 将用户关联到项目
      tags:
        - projects
  /projects/{projectId}/members/{userId}:
    delete:
      description: >-
        只移除项目关联。在 workspace 中有角色的人仍会通过继承看到项目——而由于项目角色在两个方向上都覆盖 workspace
        角色，这种继承可能低于该记录所授予的角色。因此，只要净效果是降级（包括完全没有 workspace 角色的情况），移除你自己的记录就会以 403
        拒绝；当 workspace 持有相同角色时移除，以及移除其他任何人，仍然可行。
      operationId: ProjectsController_removeMember
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
        - name: userId
          required: true
          in: path
          schema:
            type: string
      responses:
        '204':
          description: 关联已移除。无响应体。
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在项目上的角色不足，或者调用方正在移除自己的记录，而移除后其角色会低于现在。
        '404':
          description: 项目不存在或对调用方不可见。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 解除用户与项目的关联
      tags:
        - projects
  /projects/{projectId}/mirror-path:
    put:
      description: >-
        用户机器上的绝对路径，本地代理会把工作复制到这里（RN-515，ADR
        0147）——一个位于挂载基目录之外的文件夹，这正是镜像存在的全部理由。按项目设置，绝不全局：一个全局目标会把项目 B 的制品放进项目 A
        的文件夹，而用户会从内容中发现，而不是从错误中发现。`mirrorPath: null` 会清除它并关闭镜像——该键是必需的，省略它返回
        400，因为省略字段的请求体与请求清除无法区分。`null` 是项目的正常状态。`maintainer`，与 `execution-mode`
        和 `projects-base` 的最低要求相同：此路由涉及运营者自己文件系统上的路径。它只校验词法形态，并以 400 拒绝位于
        `workspacePath` 内部或包含它的目标（同一循环的两个方向），以及 `container` 项目上的任何目标。它从不触碰磁盘：API
        看不到目标所在的机器。写入镜像不是 proposed_action——它是用户声明的配置，不是代理请求执行操作。
      operationId: ProjectsController_setMirrorPathRoute
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SetMirrorPathDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProjectResponseDto'
        '400':
          description: >-
            请求体无效。`ValidationPipe` 以 `whitelist` 和 `forbidNonWhitelisted`
            运行，因此未知字段也会失败。
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在项目上的角色不足。
        '404':
          description: 项目不存在或对调用方不可见。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 声明（或清除）项目的镜像目标
      tags:
        - projects
  /projects/{projectId}/mirror-state:
    get:
      description: >-
        本地代理在把工作复制到用户文件夹后通过通道推送的遥测（ADR 0147，第 7 点）——绝不进入事件日志，因为一轮镜像没有会话，而
        `session_events.session_id` 是 `NOT NULL`（与把 `rag_searches`
        做成表的理由相同）。三种答案绝不合并为一（RN-088）：`never`（从未报告过任何一轮）、`synced`（最近一轮已复制——`filesCopied`
        可能为 `0`，表示“看过了，没有需要复制的”）和
        `failed`。失败绝不会抹去最近一次成功的同步，成功也绝不会抹去最近一次错误：哪个是当前状态由比较两个时间戳得出，因此界面可以说“从今天开始失败，最近一次成功复制是昨天，共
        412 个文件”。`lastDestination` 是冻结的——在有人修改目标后它会与 `mirrorPath` 不同，因为授权随 join
        传递，只在 runner 重连时才会改变（RN-516）。`viewer`，与读取项目的最低要求相同，项目本身已携带 `mirrorPath`。
      operationId: ProjectsController_mirrorStateRoute
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProjectMirrorStateResponseDto'
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在项目上的角色不足。
        '404':
          description: 项目不存在或对调用方不可见。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 最近一轮镜像做了什么（RN-517）
      tags:
        - projects
  /projects/{projectId}/model-binding:
    get:
      description: 此范围的原始 binding——如果项目只从 workspace 继承则为 `null`。
      operationId: ModelBindingsController_getProjectBinding
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ModelBindingResponseDto'
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在该范围上的角色不足。
        '404':
          description: 未找到该范围。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 读取项目的模型 binding
      tags:
        - llm
    put:
      operationId: ModelBindingsController_setProjectBinding
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SetModelBindingDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ModelBindingResponseDto'
        '400':
          description: >-
            请求体无效。`ValidationPipe` 以 `whitelist` 和 `forbidNonWhitelisted`
            运行，因此未知字段也会失败。
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在该范围上的角色不足。
        '404':
          description: 未找到该范围。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 设置项目的模型
      tags:
        - llm
  /projects/{projectId}/model-bindings/resolved:
    get:
      description: >-
        `GET .../agent-bindings/:agentSlug` 和 `GET .../area-bindings/:areaKey`
        的批量形式：每个请求的键得到的值与其单键路由的回答完全相同（相同的级联、相同的来源，没有任何层级有模型时为 `null`）。它的存在让
        Settings 标签页用一次请求而不是 20 次读取 20 个已解析的
        binding——速率限制是按用户计的。键以逗号分隔并去重，格式错误的键或总数超过 64 个键返回 400，绝不悄悄丢弃。
      operationId: ModelBindingsController_getResolvedBindings
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
        - name: agents
          required: false
          in: query
          description: 以逗号分隔的代理 slug。
          schema:
            example: criativo,po,dev-lead
            type: string
        - name: areas
          required: false
          in: query
          description: 以逗号分隔的区域键。
          schema:
            example: dev,qa,infra
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BindingsResolvidosEmLoteResponseDto'
        '400':
          description: 格式错误的键，或键数超过批量上限。
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在该范围上的角色不足。
        '404':
          description: 未找到该范围。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 一次读取解析多个代理和区域的模型
      tags:
        - llm
  /projects/{projectId}/models:
    get:
      description: >-
        `local` 类别是 Ollama 的模型，不花钱；`cloud` 是付费 API 的模型。workspace
        未激活的模型不会出现在这里，但仍保留在历史成本中——要查看它请使用目录路由。该列表属于项目的所有者 workspace：同一模型可以在一个
        workspace 中开启而在相邻的 workspace 中关闭（ADR 0049）。
      operationId: ModelsController_list
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                type: object
                properties:
                  local:
                    type: object
                    additionalProperties:
                      type: array
                      items:
                        $ref: '#/components/schemas/ModelResponseDto'
                  cloud:
                    type: object
                    additionalProperties:
                      type: array
                      items:
                        $ref: '#/components/schemas/ModelResponseDto'
                example:
                  local:
                    ollama: []
                  cloud:
                    anthropic: []
                    openai: []
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 列出项目所属 workspace 中已激活的模型
      tags:
        - llm
  /projects/{projectId}/open-questions:
    get:
      description: >-
        状态来自事件日志：未决即存在 `artifact.open_question` 但没有
        `open_question.answered`。最新的在前，上限 50 条并附 `total`。
      operationId: OpenQuestionsController_list
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
        - name: status
          required: false
          in: query
          schema:
            default: open
            type: string
            enum:
              - open
              - answered
              - all
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OpenQuestionsListResponseDto'
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 列出项目中代理的未决问题（RN-858）
      tags:
        - backlog
  /projects/{projectId}/open-questions/{questionId}/answer:
    post:
      description: >-
        只有问题所在会话的作者才能回答。写入一条 `decision_record`（参与者：该用户，附 `questionId`）和
        `open_question.answered`，并尽力请求 engine 恢复每个受影响任务被暂停的 QA gate（RN-860）。
      operationId: OpenQuestionsController_answer
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
        - name: questionId
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AnswerOpenQuestionDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OpenQuestionResponseDto'
        '400':
          description: 没有回答，或在没有建议的地方接受了建议。
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: '不是会话作者——响应体带有 `reason: "so_o_autor_da_sessao"`。'
        '404':
          description: 问题不在此项目中。
        '409':
          description: '已经回答过——`reason: "pergunta_ja_respondida"`。'
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 回答一个未决问题（RN-858）
      tags:
        - backlog
  /projects/{projectId}/permissions:
    get:
      description: 这是项目 workspace 根目录下的物理文件，而不是数据库列。
      operationId: ProjectsController_getPermissions
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PermissionsFileResponseDto'
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在项目上的角色不足。
        '404':
          description: 项目不存在或对调用方不可见。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 读取项目的 permissions.json
      tags:
        - projects
    put:
      description: 一次替换全部三个列表。评估时 `deny` 仍优先于 `allow`——无法通过这里允许被拒绝的内容。
      operationId: ProjectsController_setPermissions
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SetProjectPermissionsDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PermissionsFileResponseDto'
        '400':
          description: >-
            请求体无效。`ValidationPipe` 以 `whitelist` 和 `forbidNonWhitelisted`
            运行，因此未知字段也会失败。
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在项目上的角色不足。
        '404':
          description: 项目不存在或对调用方不可见。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 重写项目的 permissions.json
      tags:
        - projects
  /projects/{projectId}/personal-access-tokens:
    get:
      description: 从不包含原始令牌——只包含已签发的信息。
      operationId: PersonalAccessTokensController_listPats
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/PersonalAccessTokenResponseDto'
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在项目上的角色不足。
        '404':
          description: 未找到项目。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 列出自己在此项目中的 Personal Access Token
      tags:
        - projects
    post:
      description: >-
        原始令牌（`brb_...`）只出现在这个响应中——之后无法找回，只保存哈希。用于
        `--token`/`BRABO_ACCOUNT_TOKEN`（`brabo-runner` 的参数）。
      operationId: PersonalAccessTokensController_issuePat
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/IssuePatRequestDto'
      responses:
        '201':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/IssuePatResponseDto'
        '400':
          description: >-
            请求体无效。`ValidationPipe` 以 `whitelist` 和 `forbidNonWhitelisted`
            运行，因此未知字段也会失败。
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在项目上的角色不足。
        '404':
          description: 未找到项目。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 为本地 runner 签发一个 Personal Access Token
      tags:
        - projects
  /projects/{projectId}/personal-access-tokens/{tokenId}:
    delete:
      description: >-
        幂等——再次撤销不是错误。自 RN-685（ADR 0201）起，它还会断开用此令牌打开的 runner
        连接，且只断开这些——同一用户的设备密钥不受影响——并作废其尚未使用的票据。engine 宕机不会导致撤销失败。
      operationId: PersonalAccessTokensController_revokePat
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
        - name: tokenId
          required: true
          in: path
          schema:
            type: string
      responses:
        '204':
          description: 令牌已撤销。无响应体。
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在项目上的角色不足。
        '404':
          description: 未找到项目。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 撤销自己的 Personal Access Token
      tags:
        - projects
  /projects/{projectId}/personal-access-tokens/{tokenId}/admin:
    delete:
      description: >-
        事件响应——离职的开发者令牌泄露（RN-427）。幂等——再次撤销不是错误。自 RN-685（ADR 0201）起，它还会断开已用此令牌连接的
        runner，且只断开它。
      operationId: PersonalAccessTokensController_revokePatAsMaintainer
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
        - name: tokenId
          required: true
          in: path
          schema:
            type: string
      responses:
        '204':
          description: 令牌已撤销。无响应体。
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在项目上的角色不足。
        '404':
          description: 未找到项目。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 撤销项目中任意用户的 Personal Access Token
      tags:
        - projects
  /projects/{projectId}/personal-access-tokens/all:
    get:
      description: 供 `maintainer` 用于事件响应的视图（RN-427）——包含每个令牌的所有者。从不包含原始令牌。
      operationId: PersonalAccessTokensController_listAllPats
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/PersonalAccessTokenAdminResponseDto'
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在项目上的角色不足。
        '404':
          description: 未找到项目。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 列出项目中所有用户的全部 Personal Access Token
      tags:
        - projects
  /projects/{projectId}/proficiency:
    get:
      description: 默认是用户自己的画像；团队汇总视图只对管理该项目的人开放。能力画像是关于这个人的数据，最不令人意外的默认是看到你自己的。
      operationId: AnamneseController_proficiency
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/ProficiencyProfileResponseDto'
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在项目上的角色不足。
        '404':
          description: 项目、事件或版本不存在。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 返回熟练度画像
      tags:
        - anamnesis
  /projects/{projectId}/proficiency/me:
    delete:
      description: >-
        只能是调用方自己的画像，绝不是别人的。选择退出会一并记录，因为没有它，下一轮会重新推导一切，删除就只是装样子。它故意设为
        `viewer`：画像覆盖所有成员，要求 `developer` 会让被画像的 viewer 无法删除自己的画像。
      operationId: AnamneseController_deleteMine
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PerfilApagadoResponseDto'
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在项目上的角色不足。
        '404':
          description: 项目、事件或版本不存在。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 删除自己的画像并记录选择退出
      tags:
        - anamnesis
  /projects/{projectId}/proficiency/me/opt-in:
    post:
      description: 撤销选择退出。从下一轮起重新开始推导画像。
      operationId: AnamneseController_optInMine
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
      responses:
        '201':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PerfilOptInResponseDto'
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在项目上的角色不足。
        '404':
          description: 项目、事件或版本不存在。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 重新启用对用户自己的画像
      tags:
        - anamnesis
  /projects/{projectId}/psychologist/analyses:
    get:
      description: >-
        `costMicros` 由该轮的 `token_usage`
        求和而得。正是这一点让轻量与重量分诊之间的成本差异可见，而不仅仅是断言——两个层级使用的确实是不同的模型。
      operationId: PsychologistController_analyses
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/PsychologistAnalysisResponseDto'
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在项目上的角色不足。
        '404':
          description: 项目、会话或假设不存在。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 列出分析轮次及其层级和真实成本
      tags:
        - psychologist
  /projects/{projectId}/psychologist/status:
    get:
      description: '只读——没有副作用。`enabled: false` 表示暂停是产品决定（不是缺陷）：已有的分析和假设不受影响。'
      operationId: PsychologistController_status
      parameters: []
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PsychologistStatusResponseDto'
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在项目上的角色不足。
        '404':
          description: 项目、会话或假设不存在。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 报告 Psychologist 今天是否可以运行新的分析
      tags:
        - psychologist
  /projects/{projectId}/rag/coverage:
    get:
      description: >-
        真实计数（从不估算）：`.md` 文件（位于项目仓库的 `docs`/`docs/adr`
        下）与其中有分块的数量对比，项目的会话数与其中有分块的数量对比。这三个范围不包含“N 分钟前”——没有按范围的索引时间戳列，猜测的数字会说谎（与
        ADR 0042 对模型评级的规则相同）。`local`（RN-455）是唯一的例外：`lastAttachedAt` 是该范围上真实的
        `MAX(created_at)`，因为浏览器上传没有可供比较的仓库状态。
      operationId: RagController_obterCobertura
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RagCoverageResponseDto'
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在项目上的角色不足。
        '404':
          description: 项目不存在。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 索引覆盖率：已索引的文件/会话与真实总数对比
      tags:
        - rag
  /projects/{projectId}/rag/feedback:
    post:
      description: >-
        RAG 测量唯一的真实性信号：延迟和降级率说明搜索是否运行了，却从不说明它是否正确。角色与 `search`
        相同——能读取结果的人就能评判它。对同一搜索的同一分块重新投票会覆盖你自己的投票，而不是新增一票（每个参与者唯一），因此该指标衡量的是准确性而不是热情。
      operationId: RagController_votar
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/RagFeedbackRequestDto'
      responses:
        '201':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RagFeedbackResponseDto'
        '400':
          description: 此项目未知的 `searchId`，或 `chunkId` 不在该次搜索的命中结果中——没有排名的投票将是一个没有意义的数字。
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在项目上的角色不足。
        '404':
          description: 项目不存在。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 将某次搜索的一个命中评判为有用或无关（RN-480）
      tags:
        - rag
  /projects/{projectId}/rag/local:
    post:
      description: >-
        浏览器已读取的文本内容（`<input webkitdirectory>`），绝不是宿主机路径——这一区别正是 ADR 0113
        的全部要点。`local`
        范围的幂等完全重建：每次调用都会删除并根据本次发送的文件重新创建，因此重新上传就是重新同步的机制。超过文件数/总字节数上限时拒绝（400），而不是悄悄截断——这是用户在文件夹选择器前的一次性操作，不是后台扫描。单个过大或扩展名无法识别的文件只会被跳过，绝不会让整批失败。
      operationId: RagController_anexarPastaLocal
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AttachLocalFolderRequestDto'
      responses:
        '201':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AttachLocalFolderResponseDto'
        '400':
          description: >-
            请求体无效。`ValidationPipe` 以 `whitelist` 和 `forbidNonWhitelisted`
            运行，因此未知字段也会失败。
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在项目上的角色不足。
        '404':
          description: 项目不存在。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 附加一个本地文件夹（浏览器上传）作为只读参考（RN-455，ADR 0113）
      tags:
        - rag
  /projects/{projectId}/rag/reindex:
    post:
      description: >-
        来自 Chat RAG
        面板的“立即重建索引”。根据当前状态删除并重建三个可信范围（RN-219）的分块——推送/会话关闭时没有自动监视器（决定记录在 ADR
        0079/0080 中）。在会话很多的项目上可能需要一段时间：它对每个会话运行一次索引，每次都可能批量调用嵌入提供商。
      operationId: RagController_reindexar
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
      responses:
        '201':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ReindexProjectResponseDto'
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在项目上的角色不足。
        '404':
          description: 项目不存在。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 重建项目的 docs/ADR/会话索引（幂等完全重建）
      tags:
        - rag
  /projects/{projectId}/rag/search:
    post:
      description: >-
        将余弦相似度（pgvector）与词法 `ts_rank`（tsvector）结合，二者来自独立的查询，通过加权求和合并并按阈值截断（ADR
        0080）。当嵌入提供商没有响应、搜索只用词法信号运行时，`vectorAvailable: false`
        会发出提示——它从不假装运行了完整的混合搜索。
      operationId: RagController_buscar
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/HybridSearchRequestDto'
      responses:
        '201':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HybridSearchResponseDto'
        '400':
          description: >-
            请求体无效。`ValidationPipe` 以 `whitelist` 和 `forbidNonWhitelisted`
            运行，因此未知字段也会失败。
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在项目上的角色不足。
        '404':
          description: 项目不存在。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 在项目已索引的分块上进行混合搜索（向量 + 词法）
      tags:
        - rag
  /projects/{projectId}/run-instructions:
    get:
      description: >-
        RN-852（ADR 0213）：当前的 `artifact.run_instructions`，在 `open_infra_pr` 携带
        `run` 执行时发出。由 Infra 代理声明，从未在读者的机器上验证。没有时为 `null`。
      operationId: BacklogController_runInstructions
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RunInstructionsResponseDto'
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在项目上的角色不足。
        '404':
          description: 项目不存在。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 返回如何在你的机器上运行交付物
      tags:
        - backlog
  /projects/{projectId}/runner-device-keys:
    get:
      description: >-
        没有人能撤销看不到的东西（RN-519）。包含已撤销的密钥——让记录消失会让界面声称该密钥从未存在。从不返回 JWK 公钥，私钥 api
        从未见过。`lastUsedAt` 为空是孤立密钥的信号：已登记但从未被任何 runner 使用。自 RN-543
        起，还包含调用方的机器密钥（`especie: "maquina"`，`projectId`
        为空），它们服务于此项目却不属于它——如果列表中没有它们，机器密钥在任何界面上都将不可见。
      operationId: RunnerDeviceKeysController_listDeviceKeys
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/RunnerDeviceKeyListResponseDto'
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在项目上的角色不足。
        '404':
          description: 未找到项目。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 列出自己在此项目中的设备密钥
      tags:
        - projects
  /projects/{projectId}/runner-device-keys/{deviceKeyId}:
    delete:
      description: >-
        幂等——再次撤销不是错误。自 RN-520 起还会断开活动连接：此前撤销只阻止新票据，已连接的 runner 会继续执行已批准的命令。自
        RN-685（ADR 0201）起，目标是密钥本身：只断开用它打开的连接——如果是机器密钥则遍及所有项目——同一用户用 PAT
        或其他密钥连接的另一个 runner 不受影响。它尚未使用的票据会被作废。engine 宕机或没有 runner 连接都不会导致撤销失败。
      operationId: RunnerDeviceKeysController_revokeDeviceKey
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
        - name: deviceKeyId
          required: true
          in: path
          schema:
            type: string
      responses:
        '204':
          description: 密钥已撤销。无响应体。
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在项目上的角色不足。
        '404':
          description: 未找到项目。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 撤销自己的一把设备密钥
      tags:
        - projects
  /projects/{projectId}/runner-ticket:
    post:
      description: >-
        用于 `connect/3` 认证，对象是 Phoenix 套接字 `/runner`（主题 `terminal:<projectId>`，角色
        “runner”）——不是复用的 JWT，此路由本身只接受 Personal Access Token 代替会话 JWT（ADR
        0105）。TTL 30 秒，一次性使用。如果项目不处于 “runner” 模式则以 400
        拒绝（RN-421）：在代码位于托管容器中的项目里，runner 无事可做。
      operationId: RunnerTicketsController_runnerTicket
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
      responses:
        '201':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RunnerTicketResponseDto'
        '400':
          description: 项目不处于 “runner” 模式。
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在项目上的角色不足。
        '404':
          description: 未找到项目。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 为本地 runner 连接签发一次性票据
      tags:
        - projects
  /projects/{projectId}/sessions:
    get:
      description: 每个条目都带有 `technical`：仅当会话由仓库置备打开时为 `true`（从 `repo_bootstraps` 关联）。
      operationId: SessionsController_list
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/SessionListItemResponseDto'
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在项目中的角色不足。
        '404':
          description: 项目或会话不存在。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 列出项目的会话
      tags:
        - sessions
    post:
      description: >-
        会话以 `created` 状态诞生，是代理所做一切的容器：事件日志、提议的操作和 token 预算都挂在它上面。`kind`
        是必需的并会被记录：`consultiva`（咨询式）只对话，`criativa`（创作式）产出内容，并且是唯一接受
        `execution.activated` 的。
      operationId: SessionsController_create
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateSessionDto'
      responses:
        '201':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SessionResponseDto'
        '400':
          description: 请求体缺少 `kind`，或其值不在允许列表中。
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在项目中的角色不足。
        '404':
          description: 项目或会话不存在。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 在项目中打开一个会话
      tags:
        - sessions
  /projects/{projectId}/sessions/{sessionId}:
    get:
      operationId: SessionsController_get
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
        - name: sessionId
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SessionResponseDto'
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在项目中的角色不足。
        '404':
          description: 项目或会话不存在。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 按 id 返回会话
      tags:
        - sessions
    patch:
      description: >-
        名称是导航标签，不会取代 id 的井号标签——界面会把两者组合起来，没有名称时退化为只显示井号标签。`null`
        或空白字符串会清除名称。无法修改 `kind`：它是创建时的意图，之后修改会把它变成状态。
      operationId: SessionsController_rename
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
        - name: sessionId
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/RenameSessionDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SessionResponseDto'
        '400':
          description: 请求体缺少 `name`，或名称超过字符上限。
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在项目中的角色不足。
        '404':
          description: 项目或会话不存在。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 设置或清除会话的友好名称
      tags:
        - sessions
  /projects/{projectId}/sessions/{sessionId}/actions:
    get:
      description: >-
        按 `seq` 排序；将 `nextCursor` 用作 `afterSeq`。`latest=true` 改为获取尾部（最新的 `limit`
        个操作，仍为升序）并忽略 `afterSeq`。`status=pending` 只保留等待决定的操作——配合 `latest`
        时是最新的待处理操作，这样拥有 200 个以上操作的会话不会把新的待处理操作挤出窗口（RN-637）。
      operationId: ActionsController_list
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
        - name: sessionId
          required: true
          in: path
          schema:
            type: string
        - name: afterSeq
          required: false
          in: query
          schema:
            example: 6
            type: string
        - name: limit
          required: false
          in: query
          schema:
            example: 50
            type: string
        - name: latest
          required: false
          in: query
          description: 获取会话尾部；忽略 `afterSeq`。
          schema:
            example: 'true'
            type: string
        - name: status
          required: false
          in: query
          description: 只支持 `pending`。
          schema:
            example: pending
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PaginaDeAcoesResponseDto'
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在项目上的角色不足。
        '404':
          description: 未找到项目、会话或操作。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 分页读取会话中提议的操作
      tags:
        - actions
    post:
      description: >-
        操作在创建时依据项目的 `permissions.json` 进行评估。响应中的 `resolvedPolicy`
        说明策略的决定：`auto_approve` 出来时即可执行，`require_approval` 等待人工决定，`deny`
        一出生即被拒绝。不存在不经过这里就执行的路径。
      operationId: ActionsController_propose
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
        - name: sessionId
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ProposeActionDto'
      responses:
        '201':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProposedActionResponseDto'
        '400':
          description: >-
            请求体无效。`ValidationPipe` 以 `whitelist` 和 `forbidNonWhitelisted`
            运行，因此未知字段也会失败。
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在项目上的角色不足。
        '404':
          description: 未找到项目、会话或操作。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 提议一个具有外部效果的操作
      tags:
        - actions
  /projects/{projectId}/sessions/{sessionId}/actions/{actionId}/approve:
    post:
      description: 只适用于此操作。决定连同决定者的 id 一起保存在事件日志中。
      operationId: ActionsController_approve
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
        - name: sessionId
          required: true
          in: path
          schema:
            type: string
        - name: actionId
          required: true
          in: path
          schema:
            type: string
      responses:
        '201':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProposedActionResponseDto'
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在项目上的角色不足。
        '404':
          description: 未找到项目、会话或操作。
        '409':
          description: 该操作已被决定或已执行。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 批准该操作，仅此一次
      tags:
        - actions
  /projects/{projectId}/sessions/{sessionId}/actions/{actionId}/approve_always:
    post:
      description: >-
        除了放行此操作外，还会把相应的模式加入项目的 `allow` 列表——今后匹配的操作会以 `auto_approved` 出来而无需询问。已在
        `deny` 中的模式保持阻止。批准和模式在同一事务中记录。点击已被批准的操作属于幂等成功（`desfecho:
        ja_aprovada`），仅在模式缺失时才记录它。
      operationId: ActionsController_approveAlways
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
        - name: sessionId
          required: true
          in: path
          schema:
            type: string
        - name: actionId
          required: true
          in: path
          schema:
            type: string
      responses:
        '201':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApproveAlwaysResponseDto'
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在项目上的角色不足。
        '404':
          description: 未找到项目、会话或操作。
        '409':
          description: '该操作已被拒绝（`reason: acao_ja_recusada`）；不记录任何模式。'
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 批准该操作并把模式记录到 permissions.json
      tags:
        - actions
  /projects/{projectId}/sessions/{sessionId}/actions/{actionId}/deny:
    post:
      description: 如果提供了理由，它会与决定一起保存在事件日志中。
      operationId: ActionsController_deny
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
        - name: sessionId
          required: true
          in: path
          schema:
            type: string
        - name: actionId
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DenyActionDto'
      responses:
        '201':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProposedActionResponseDto'
        '400':
          description: >-
            请求体无效。`ValidationPipe` 以 `whitelist` 和 `forbidNonWhitelisted`
            运行，因此未知字段也会失败。
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在项目上的角色不足。
        '404':
          description: 未找到项目、会话或操作。
        '409':
          description: 该操作已被决定或已执行。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 拒绝该操作
      tags:
        - actions
  /projects/{projectId}/sessions/{sessionId}/agents/{agent}/cancel:
    post:
      description: >-
        输入框的“停止”按钮（RN-122）：在 engine 中终止进行中的 LLM 调用，在流中途切断连接——这是真正节省
        token，而不仅仅是在客户端停止渲染。幂等：没有进行中的回合时，请求被接受但没有效果。
      operationId: AgentsController_cancel
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
        - name: sessionId
          required: true
          in: path
          schema:
            type: string
        - name: agent
          required: true
          in: path
          description: 活跃代理的 slug。
          schema:
            example: po
            type: string
      responses:
        '201':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OkResponseDto'
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在项目上的角色不足。
        '404':
          description: 未找到项目、会话或 handoff。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 取消活跃代理进行中的回合
      tags:
        - agents
  /projects/{projectId}/sessions/{sessionId}/agents/{agent}/message:
    post:
      description: >-
        响应只是一个确认，并在接受时返回——在代理的回合结束之前（ADR
        0163）。代理的回复通过会话的事件日志和通道到达——而不是通过这次调用。如果代理正处于回合中，消息不会被拒绝：它会进入代理的队列（`entrega:
        "enfileirada"`），在当前回合结束时与其他排队的消息在一个回合中一起被读取；等待期间可以取消（RN-673，ADR 0191）。
      operationId: AgentsController_message
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
        - name: sessionId
          required: true
          in: path
          schema:
            type: string
        - name: agent
          required: true
          in: path
          description: >-
            读取该消息的对话式代理的 slug：criativo、po、arquiteto、dev-lead、ux-designer 或
            staff。任何其他 slug——包括 infra——都会以 422 拒绝；绝不会投递给默认代理（RN-584）。
          schema:
            example: po
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SendAgentMessageDto'
      responses:
        '201':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MensagemAoAgenteResponseDto'
        '400':
          description: >-
            请求体无效。`ValidationPipe` 以 `whitelist` 和 `forbidNonWhitelisted`
            运行，因此未知字段也会失败。
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在项目上的角色不足。
        '404':
          description: 未找到项目、会话或 handoff。
        '409':
          description: >-
            代理在此会话中未处于活跃状态；或正在等待执行计划的决定；或其队列中已有 10
            条消息（`fila_de_mensagens_cheia`）——消息已记录，但代理没有读取（ADR 0163，RN-673）。
        '422':
          description: >-
            该代理不接收聊天消息（Infra Lead 通过提议工作，engine 中没有自身条款的任何 slug
            都会被具名拒绝）——消息已记录，但没有任何代理读取（RN-584）。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 向活跃代理发送消息
      tags:
        - agents
  /projects/{projectId}/sessions/{sessionId}/agents/{agent}/messages/{messageId}/cancel:
    post:
      description: >-
        在代理处于回合中时发送的消息会在队列中等待，并在回合结束时被读取（RN-673）。等待期间，发送者可以取消它：engine
        将其从队列中移除并记录 `chat.message_cancelled`；它绝不会展示给模型。取消别人的消息返回 403。
      operationId: AgentsController_cancelQueued
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
        - name: sessionId
          required: true
          in: path
          schema:
            type: string
        - name: agent
          required: true
          in: path
          description: 队列中持有该消息的代理的 slug。
          schema:
            example: po
            type: string
        - name: messageId
          required: true
          in: path
          description: '`chat.message` 事件的 id（消息路由返回的 `mensagemId`）。'
          schema:
            example: 01JC4Z0000EVENTO000000000001
            type: string
      responses:
        '201':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OkResponseDto'
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在项目上的角色不足。
        '404':
          description: 未找到项目、会话或 handoff。
        '409':
          description: 该消息已不在队列中——代理已经读取了它，或它已被取消；或会话已关闭（RN-581）。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 取消代理队列中一条正在等待的消息
      tags:
        - agents
  /projects/{projectId}/sessions/{sessionId}/agents/{agent}/resume:
    post:
      description: >-
        Human gesture, with no cap: the automatic resume happens once at boot
        (ADR 0226) and this is the button for the rest. The reply is the ACCEPT
        (the turn goes up in the engine); progress arrives on the session
        channel. Requires `developer`.
      operationId: AgentResumeController_resume
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
        - name: sessionId
          required: true
          in: path
          schema:
            type: string
        - name: agent
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OkResponseDto'
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在项目上的角色低于 `developer`。
        '404':
          description: Session does not exist.
        '409':
          description: >-
            Nothing to resume — `reason: turno_nao_interrompido` (the agent has
            no interrupted turn, or one is already running) or
            `agente_sem_retomada` (the agent cannot resume).
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: Resumes the turn an engine restart interrupted
      tags:
        - sessions
  /projects/{projectId}/sessions/{sessionId}/agents/{agent}/start:
    post:
      description: >-
        只有 Criativo 可以通过直接命令启动。其他每个代理都需要一个指向它的已接受的
        handoff——该规则位于领域中，而不在这里，试图绕过它会返回 409。
      operationId: AgentsController_start
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
        - name: sessionId
          required: true
          in: path
          schema:
            type: string
        - name: agent
          required: true
          in: path
          description: 代理的 slug。
          schema:
            example: criativo
            type: string
      responses:
        '201':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AgenteAtivadoResponseDto'
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在项目上的角色不足。
        '404':
          description: 未找到项目、会话或 handoff。
        '409':
          description: 代理没有已接受的 handoff，或已在会话中处于活跃状态。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 在会话中启动一个代理
      tags:
        - agents
  /projects/{projectId}/sessions/{sessionId}/agents/{agent}/structured-question/{questionSetId}/answer:
    post:
      description: >-
        记录
        `chat.structured_question_answered`，并把答案作为普通消息重新发送给代理。一组问题只能回答一次。在接受时返回，在代理的回合结束之前（ADR
        0163）：回合继续在 engine
        中运行，其叙述、结束和失败通过会话通道和事件日志到达（`agent.status`、`agent.response`、`agent.error`）——绝不通过这个响应。
      operationId: AgentsController_submitStructuredQuestionAnswer
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
        - name: sessionId
          required: true
          in: path
          schema:
            type: string
        - name: agent
          required: true
          in: path
          description: 提出问题（并将读取答案）的代理的 slug。
          schema:
            example: criativo
            type: string
        - name: questionSetId
          required: true
          in: path
          description: 已回答的 `chat.structured_question` 事件的 id。
          schema:
            example: 01JC4Z0000EVENTO000000000001
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AnswerStructuredQuestionDto'
      responses:
        '201':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OkResponseDto'
        '400':
          description: >-
            请求体无效。`ValidationPipe` 以 `whitelist` 和 `forbidNonWhitelisted`
            运行，因此未知字段也会失败。
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在项目上的角色不足。
        '404':
          description: 未找到项目、会话或 handoff。
        '409':
          description: 这组问题已经回答过；或代理仍处于回合中，没有读取答案（ADR 0163）。
        '422':
          description: 提问的代理不接收聊天消息——答案已记录，但没有任何代理读取（RN-584）。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 回答代理的一组结构化问题
      tags:
        - agents
  /projects/{projectId}/sessions/{sessionId}/agents/{agentId}/rearm:
    post:
      description: 重置连续阻塞任务计数器（RN-047），让代理重新尝试认领工作。这是离开 `idle_tripped` 的唯一方式——没有自动解除阻塞。
      operationId: ExecutionController_rearm
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
        - name: sessionId
          required: true
          in: path
          schema:
            type: string
        - name: agentId
          required: true
          in: path
          schema:
            type: string
      responses:
        '201':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OkResponseDto'
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在项目上的角色不足。
        '404':
          description: 未找到项目、会话或任务。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 重新启用被熔断器触发的 dev agent
      tags:
        - execution
  /projects/{projectId}/sessions/{sessionId}/agents/arquiteto/handoff-infra:
    post:
      description: >-
        专用端点，而不是复用属于 Criativo 的
        `readiness`：它们是会话中两个不同的里程碑，混为一谈会让事件日志含糊不清。在接受时返回，在代理的回合结束之前（ADR
        0163）：回合继续在 engine
        中运行，其叙述、结束和失败通过会话通道和事件日志到达（`agent.status`、`agent.response`、`agent.error`）——绝不通过这个响应。到
        Dev Lead 的 handoff 仍在到 Infra 的之后：engine 会一直持有它，直到收尾回合结束。幂等（ADR
        0182，RN-635）：已有待处理提议或已在项目中活跃的目标不会被再次触发，两者都如此时不记录任何内容。
      operationId: AgentsController_handoffInfra
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
        - name: sessionId
          required: true
          in: path
          schema:
            type: string
      responses:
        '201':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ConfirmacaoDeArquiteturaResponseDto'
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在项目上的角色不足。
        '404':
          description: 未找到项目、会话或 handoff。
        '409':
          description: Arquiteto 仍处于回合中——确认已记录，但收尾回合没有开始（ADR 0163）。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 确认架构已就绪，并向 Infra 提议 handoff
      tags:
        - agents
  /projects/{projectId}/sessions/{sessionId}/agents/criativo/validate-necessity:
    post:
      description: >-
        记录 `necessity.validated`。要求 Criativo 已在此会话中整合出一份
        `product_brief`（RN-406）——没有它会被拒绝：没有可验证的内容。自 ADR
        0185（RN-657）起，“我准备好了——需求已验证”的点击（`POST .../readiness`）会自行记录此事件，Web
        端不再调用此路由；保留它是为了那些就绪点击早于该 ADR 的会话。
      operationId: AgentsController_validateNecessityHandoff
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
        - name: sessionId
          required: true
          in: path
          schema:
            type: string
      responses:
        '201':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OkResponseDto'
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在项目上的角色不足。
        '404':
          description: 未找到项目、会话或 handoff。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 确认 Criativo 的业务需求已得到验证
      tags:
        - agents
  /projects/{projectId}/sessions/{sessionId}/budget:
    get:
      description: 这是会话界面实时 token 计量表的数据来源。
      operationId: BudgetsController_getSessionBudget
      parameters:
        - name: sessionId
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BudgetResponseDto'
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在项目上的角色不足。
        '404':
          description: 未找到项目或会话。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 读取会话预算和累计花费
      tags:
        - llm
    put:
      description: 与项目预算相同的美元到微美元转换。
      operationId: BudgetsController_setSessionBudget
      parameters:
        - name: sessionId
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpsertBudgetDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BudgetResponseDto'
        '400':
          description: >-
            请求体无效。`ValidationPipe` 以 `whitelist` 和 `forbidNonWhitelisted`
            运行，因此未知字段也会失败。
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在项目上的角色不足。
        '404':
          description: 未找到项目或会话。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 设置会话花费上限
      tags:
        - llm
  /projects/{projectId}/sessions/{sessionId}/chat:
    post:
      description: >-
        Server-Sent Events。`delta` 帧携带增量文本，`done` 以 token
        和成本计费收尾。`metering_failed` 帧表示响应已发出但成本没有计入——失败会显现出来而不是消失。如果在
        `policy=block` 下超出预算，流会携带 `error` 且没有
        `delta`。此路由不携带代理：它把文本单独发送给绑定的模型，没有历史也没有系统提示词。在从未激活过任何代理的 `consultiva`
        会话中，它会在产生任何效果之前被拒绝（RN-682）——请改用 `.../agents/:agent/message` 与代理对话。
      operationId: ChatController_chat
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
        - name: sessionId
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SendChatMessageDto'
      responses:
        '200':
          description: 直到 `done` 或 `error` 的帧流。
          content:
            text/event-stream:
              schema:
                $ref: '#/components/schemas/ChatSseEventResponseDto'
        '400':
          description: >-
            请求体无效。`ValidationPipe` 以 `whitelist` 和 `forbidNonWhitelisted`
            运行，因此未知字段也会失败。
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在项目上的角色不足。
        '404':
          description: 未找到项目或会话。
        '422':
          description: >-
            `destinatario_ausente`：没有代理的 `consultiva`
            会话——消息没有接收者。没有记录任何内容，也没有调用任何模型（RN-682）。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 与会话的模型对话，以流式返回响应
      tags:
        - llm
  /projects/{projectId}/sessions/{sessionId}/context-summary:
    get:
      description: >-
        在整个会话日志上统计的业务规则、决策记录和每个代理的制品（界面读取的是 200 个事件的尾部），以及此会话和其已批准的 Dev Lead
        计划所激活的执行会话中被 `git_commit`/`git_push` 触及的文件（RN-828）。自 RN-844
        起，它还返回规则和决策的条目（各最新 50 条；总数仍在 `regras`/`decisoes` 中）以及这些会话中打开的 PR 的文件，读取自
        PR 标签页所读的同一 diff，每次调用最多 20 个 PR；`prsNaoLidas` 说明有多少被略过或失败。只读；没有新表。
      operationId: SessionsController_contextSummary
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
        - name: sessionId
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SessionContextSummaryResponseDto'
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在项目中的角色不足。
        '404':
          description: 项目或会话不存在。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 为上下文面板统计整个会话
      tags:
        - sessions
  /projects/{projectId}/sessions/{sessionId}/events:
    get:
      description: >-
        日志不可变，按 `seq` 排序。常规分页是增量式的：把上一个响应的 `nextCursor` 作为 `afterSeq`
        传入。`latest=true` 则相反——它获取尾部并忽略 `afterSeq`，供那些在末尾打开而不是从头重建整个会话的界面使用（ADR
        0021）。
      operationId: SessionsController_listEvents
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
        - name: sessionId
          required: true
          in: path
          schema:
            type: string
        - name: afterSeq
          required: false
          in: query
          description: 返回 `seq` 大于此值的事件。
          schema:
            example: 40
            type: string
        - name: limit
          required: false
          in: query
          schema:
            example: 50
            type: string
        - name: latest
          required: false
          in: query
          description: 获取日志尾部；忽略 `afterSeq`。
          schema:
            example: 'true'
            type: string
        - name: actionId
          required: false
          in: query
          description: >-
            只返回 `payload.actionId` 为此操作的事件——例如它的
            `proposed_action.created`，其中携带操作记录不保存的策略 `reason` 和
            `scopeRoot`（RN-614）。可与其他参数组合；页内的 `seq`
            不再连续，因此按操作过滤的调用方不得据此推算省略的数量。空值返回 `400`。
          schema:
            example: 01JC4Z0000ACAO00000000000001
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PaginaDeEventosResponseDto'
        '400':
          description: '`actionId` 存在但为空。'
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在项目中的角色不足。
        '404':
          description: 项目或会话不存在。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 分页读取会话的事件日志
      tags:
        - sessions
    post:
      description: 仅追加：`seq` 由服务器分配，日志中的任何内容之后都不会被更新或删除。按设计没有编辑端点。
      operationId: SessionsController_appendEvent
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
        - name: sessionId
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AppendSessionEventDto'
      responses:
        '201':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SessionEventResponseDto'
        '400':
          description: >-
            请求体无效。`ValidationPipe` 以 `whitelist` 和 `forbidNonWhitelisted`
            运行，因此未知字段也会失败。
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在项目中的角色不足。
        '404':
          description: 项目或会话不存在。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 向会话日志追加一个事件
      tags:
        - sessions
  /projects/{projectId}/sessions/{sessionId}/events/{eventId}:
    get:
      description: 它的存在是为了让 Psychologist 的假设证据无论落在哪一页、无论信息流的过滤条件如何，都能找到所引用的事件。
      operationId: SessionsController_getEvent
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
        - name: sessionId
          required: true
          in: path
          schema:
            type: string
        - name: eventId
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SessionEventResponseDto'
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在项目中的角色不足。
        '404':
          description: 项目或会话不存在。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 按 id 返回日志事件
      tags:
        - sessions
  /projects/{projectId}/sessions/{sessionId}/execution/parallelize:
    post:
      description: >-
        受 dev 区域上限约束（RN-083）：在上限之内，代理会立即在自己的 worktree 中启动；超过上限时，响应是一个
        `proposed_action`，类型为 `parallelize`，等待你的决定，在你决定之前什么都不会启动。上限设为多少可在
        Settings 中按 lead 配置。
      operationId: ExecutionController_parallelize
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
        - name: sessionId
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AcceptParallelizationDto'
      responses:
        '201':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PedidoDeParalelismoResponseDto'
        '400':
          description: >-
            请求体无效。`ValidationPipe` 以 `whitelist` 和 `forbidNonWhitelisted`
            运行，因此未知字段也会失败。
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在项目上的角色不足。
        '404':
          description: 未找到项目、会话或任务。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 为某个模块再请求一个 dev agent
      tags:
        - execution
  /projects/{projectId}/sessions/{sessionId}/handoffs:
    get:
      operationId: AgentsController_handoffs
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
        - name: sessionId
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/HandoffResponseDto'
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在项目上的角色不足。
        '404':
          description: 未找到项目、会话或 handoff。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 列出会话中代理之间的 handoff
      tags:
        - agents
    post:
      description: >-
        以 `offered` 状态诞生，与代理自己的 `offer_handoff` 完全一样——唯一的区别是由谁决定。`toAgent`
        必须在可寻址目录中（区域 lead 或没有区域的代理）；子代理或未知 slug 会以 400 拒绝。每个（项目，目标）最多一个待处理提议：见
        `desfecho`（ADR 0182，RN-635）。
      operationId: AgentsController_requestManual
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
        - name: sessionId
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/RequestManualHandoffDto'
      responses:
        '201':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OfertaDeHandoffResponseDto'
        '400':
          description: >-
            请求体无效。`ValidationPipe` 以 `whitelist` 和 `forbidNonWhitelisted`
            运行，因此未知字段也会失败。
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在项目上的角色不足。
        '404':
          description: 未找到项目、会话或 handoff。
        '409':
          description: '`agente_ja_ativo`（ADR 0182，RN-635）：目标已在该项目一个未关闭的会话中处于活跃状态——不会创建提议。'
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 手动向所选代理提议 handoff
      tags:
        - agents
  /projects/{projectId}/sessions/{sessionId}/handoffs/{handoffId}/accept:
    post:
      description: 只做一件事：转换为 `accepted` 并启动目标代理。这是除 Criativo 之外每个代理的常规激活路径。
      operationId: AgentsController_accept
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
        - name: sessionId
          required: true
          in: path
          schema:
            type: string
        - name: handoffId
          required: true
          in: path
          schema:
            type: string
      responses:
        '201':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HandoffResponseDto'
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在项目上的角色不足。
        '404':
          description: 未找到项目、会话或 handoff。
        '409':
          description: 该 handoff 不处于 `offered` 状态。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 接受 handoff 并激活目标代理
      tags:
        - agents
  /projects/{projectId}/sessions/{sessionId}/model-binding:
    get:
      description: >-
        返回由级联解析得出的 binding，而不是会话的原始 binding：没有自身 binding 的会话使用项目的 binding，在这里回答
        `null` 就是对正确问题的错误回答。`origin` 字段说明该值来自哪个范围。


        `agentId` 是可选的，表示此刻在会话中真正活跃的代理（与 `RunLlmTurnUseCase`
        运行回合时使用的相同）——没有它，级联只能看到 会话→项目→workspace（加上固定回落到 Creative），在 handoff
        之后永远无法反映 PO/Architect/Dev Lead/区域。
      operationId: ModelBindingsController_getSessionBinding
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
        - name: sessionId
          required: true
          in: path
          schema:
            type: string
        - name: agentId
          required: false
          in: query
          description: 此会话中当前活跃的代理（例如 “po”、“arquiteto”、“dev-lead”）。
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ResolvedBindingResponseDto'
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在该范围上的角色不足。
        '404':
          description: 未找到该范围。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 解析会话使用哪个模型，以及它的来源
      tags:
        - llm
    put:
      description: 在会话存续期间优先于级联中的其他一切。
      operationId: ModelBindingsController_setSessionBinding
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
        - name: sessionId
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SetModelBindingDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ModelBindingResponseDto'
        '400':
          description: >-
            请求体无效。`ValidationPipe` 以 `whitelist` 和 `forbidNonWhitelisted`
            运行，因此未知字段也会失败。
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在该范围上的角色不足。
        '404':
          description: 未找到该范围。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 为此会话固定模型
      tags:
        - llm
  /projects/{projectId}/sessions/{sessionId}/psychologist/reanalyze:
    post:
      description: >-
        需要 `maintainer` 而非 `developer`，因为它会再次运行 ToolLoop
        并花费真实预算——不同于接受或驳回，后者只移动本地状态。之前的分析会被标记为已取代。
      operationId: PsychologistController_reanalyze
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
        - name: sessionId
          required: true
          in: path
          schema:
            type: string
      responses:
        '201':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OkResponseDto'
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在项目上的角色不足。
        '404':
          description: 项目、会话或假设不存在。
        '429':
          description: 按用户或按 IP 的速率限制。
        '503':
          description: >-
            Psychologist 已因用户的决定被全局禁用（不是缺陷）——响应体带有 `reason:
            "psychologist_disabled"`。
      security:
        - bearer: []
      summary: 触发对会话的重新分析
      tags:
        - psychologist
  /projects/{projectId}/sessions/{sessionId}/readiness:
    post:
      description: >-
        这是触发 `product_brief` 和到 PO 的 handoff 的按钮。在事件日志中记录
        `readiness.confirmed`。在接受时返回，在代理的回合结束之前（ADR 0163）：回合继续在 engine
        中运行，其叙述、结束和失败通过会话通道和事件日志到达（`agent.status`、`agent.response`、`agent.error`）——绝不通过这个响应。
      operationId: AgentsController_readiness
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
        - name: sessionId
          required: true
          in: path
          schema:
            type: string
      responses:
        '201':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OkResponseDto'
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在项目上的角色不足。
        '404':
          description: 未找到项目、会话或 handoff。
        '409':
          description: Criativo 仍处于回合中——确认已记录，但 brief 没有开始（ADR 0163）。
        '422':
          description: 这次对话中没有捕获任何业务规则——还没有可以整合成 brief 的内容（ADR 0163）。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 确认与 Criativo 的探索会话已完成
      tags:
        - agents
  /projects/{projectId}/sessions/{sessionId}/reopen:
    post:
      description: >-
        只有 `closed`/`closed_abnormally` 可以重新打开，并且只能回到 `active`——`closing`
        绝不回退（ADR 0183）。会话保留其事件日志、制品、已回答的问题和 handoff；`kind` 不变。一个新事件
        `session.reopened` 记录之前的 `closedAt` 和 `terminationReason`，记录中的这两列会被清空。携带
        `execution.activated`
        的会话会被拒绝（`sessao_com_execucao`）：请打开新会话并在那里激活执行。没有时间限制。需要 `developer`（ADR
        0184）。
      operationId: SessionsController_reopen
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
        - name: sessionId
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SessionResponseDto'
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在项目中的角色不足。
        '404':
          description: 项目或会话不存在。
        '409':
          description: >-
            会话未关闭（`reason: sessao_nao_encerrada`），或它激活过执行（`reason:
            sessao_com_execucao`）。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 重新打开已关闭的会话，保留其所有内容
      tags:
        - sessions
  /projects/{projectId}/sessions/{sessionId}/response-language:
    get:
      description: >-
        为调用方解析，绝不为会话解析：会话覆盖 > 账户选择 > 检测并确认的 >
        界面语言（RN-618）。返回胜出者、其来源以及链条的每一环——并且当你最近的消息指向另一种语言、界面应当就此询问时，返回
        `detectionQuestion`（RN-624）。
      operationId: SessionResponseLanguageController_get
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
        - name: sessionId
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SessionResponseLanguageResponseDto'
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在项目中的角色不足。
        '404':
          description: 项目或会话不存在。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 代理在此会话中回答你所使用的语言
      tags:
        - sessions
    put:
      description: 只针对调用方，只在此会话中（RN-618）——其他参与者和你的账户选择不受影响。`null` 恢复为从账户继承。
      operationId: SessionResponseLanguageController_set
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
        - name: sessionId
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SetSessionResponseLanguageDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SessionResponseLanguageResponseDto'
        '400':
          description: 请求体缺少 `language`，或代码不是可识别的 BCP-47 语言。
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在项目中的角色不足。
        '404':
          description: 项目或会话不存在。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 固定（或释放）你在此会话中的回复语言
      tags:
        - sessions
  /projects/{projectId}/sessions/{sessionId}/socket-ticket:
    post:
      description: >-
        该票据用于 engine 上会话 Phoenix 套接字 `session:<id>` 的 `connect/3` 认证——不是复用的
        JWT。TTL 30 秒，一次性使用：每次重连（包括自动重连）都会请求新票据。`scope: "heartbeat"` 需要 `viewer`
        角色；`scope: "terminal"` 需要 `developer`，与终端操作的最低角色相同。
      operationId: SessionsController_issueSocketTicket
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
        - name: sessionId
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateSocketTicketDto'
      responses:
        '201':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SocketTicketResponseDto'
        '400':
          description: >-
            请求体无效。`ValidationPipe` 以 `whitelist` 和 `forbidNonWhitelisted`
            运行，因此未知字段也会失败。
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 角色不足以使用所请求的范围。
        '404':
          description: 项目或会话不存在。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 为会话套接字签发一个不透明的一次性票据
      tags:
        - sessions
  /projects/{projectId}/sessions/{sessionId}/tasks/{taskId}/unblock:
    post:
      description: 重置 gate 修正计数器，并把任务交还给代理。这是修正上限的人工逃生口——没有自动解除阻塞。
      operationId: ExecutionController_unblock
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
        - name: sessionId
          required: true
          in: path
          schema:
            type: string
        - name: taskId
          required: true
          in: path
          schema:
            type: string
      responses:
        '201':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OkResponseDto'
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在项目上的角色不足。
        '404':
          description: 未找到项目、会话或任务。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 解除被阻塞任务的阻塞
      tags:
        - execution
  /projects/{projectId}/sessions/{sessionId}/token-usage:
    get:
      description: 数据一直在 `token_usage` 中，但没有聚合也没有路由，团队面板就无法显示每张卡片的成本。所需角色与会话预算相同。
      operationId: BudgetsController_getSessionTokenUsage
      parameters:
        - name: sessionId
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/SessionAgentTokenUsageResponseDto'
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在项目上的角色不足。
        '404':
          description: 未找到项目或会话。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 按代理细分会话花费
      tags:
        - llm
  /projects/{projectId}/sessions/{sessionId}/transition:
    post:
      description: >-
        状态转换由领域的状态机校验，而不是由客户端：created → active → closing → closed |
        closed_abnormally。无效的跳转返回 409。
      operationId: SessionsController_transition
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
        - name: sessionId
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TransitionSessionDto'
      responses:
        '201':
          description: >-
            返回 201 而不是 200，因为处理器是 `@Post` 且没有 `@HttpCode`——这是 Nest
            的默认行为。按现状记录而不是修改：语义值得商榷，行为本身并无缺陷，而表格测试会在两者之间出现任何差异时失败。
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SessionResponseDto'
        '400':
          description: >-
            请求体无效。`ValidationPipe` 以 `whitelist` 和 `forbidNonWhitelisted`
            运行，因此未知字段也会失败。
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在项目中的角色不足。
        '404':
          description: 项目或会话不存在。
        '409':
          description: 不允许从当前状态进行此转换。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 将会话移动到另一个状态
      tags:
        - sessions
  /projects/{projectId}/spend/me:
    get:
      description: >-
        面向成员的视图。只有调用方自己的记录——参与者来自已认证的令牌，没有可替换它的参数。以 token
        和估算成本呈现，不按提供商或凭据细分：运行的密钥属于所有者（RN-058），其账单也属于所有者（RN-060）。提供商维度在所有者的报告中才存在（ADR
        0076），从这里仍不可达——此路由没有维度参数，仓储类型也拒绝这种组合（RN-187）。代理不计入：`token_usage`
        记录的是谁花费了，而不是谁下令花费。
      operationId: SpendController_getMySpend
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
        - name: dias
          required: false
          in: query
          description: 滑动窗口。默认 30，最大 180。
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MySpendResponseDto'
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 角色不足。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 我自己在此项目中的消耗，按会话和按天
      tags:
        - llm
  /projects/{projectId}/spend/team:
    get:
      description: >-
        The MEMBER's view of the PROJECT (RN-953, ADR 0233, revising 0063):
        total and cost per agent/area, NEVER provider or credential — the route
        has no dimension parameter and never asks for `provider`.
      operationId: SpendController_getProjectSpendTeam
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
        - name: dias
          required: false
          in: query
          description: 滑动窗口。默认 30，最大 180。
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProjectSpendTeamResponseDto'
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 角色不足。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: The project's total spend and cost by agent and area
      tags:
        - llm
  /projects/{projectId}/stories/{storyId}/archive:
    post:
      description: >-
        该 story 离开 backlog、覆盖、提升队列、Dev Lead 的计划和认领——其任务也一样。不会删除任何内容：记录保留并带有
        `archivedAt`，`backlog.story_archived` 记录由谁、为何以及哪些任务。仅限没有执行中任务的 `draft`
        story。
      operationId: BacklogController_archive
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
        - name: storyId
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ArchiveStoryDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/StoryResponseDto'
        '400':
          description: >-
            请求体无效。`ValidationPipe` 以 `whitelist` 和 `forbidNonWhitelisted`
            运行，因此未知字段也会失败。
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在项目上的角色不足。
        '404':
          description: 项目不存在。
        '409':
          description: >-
            具名拒绝（`reason`）：`historia_arquivada`、`historia_nao_draft` 或
            `historia_com_tarefa_em_execucao`。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 归档一个草稿 story（RN-727）
      tags:
        - backlog
  /projects/{projectId}/stories/{storyId}/return:
    post:
      description: >-
        该 story 离开提议队列，理由会变成固定在 PO 会话中的一条消息——与把 gate 退回给 dev 的模式相同。如果该会话的 PO
        已不在运行，退回仍会被记录：这是用户的决定，不依赖于有代理在听。
      operationId: BacklogController_return
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
        - name: storyId
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ReturnStoryDto'
      responses:
        '201':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OkResponseDto'
        '400':
          description: >-
            请求体无效。`ValidationPipe` 以 `whitelist` 和 `forbidNonWhitelisted`
            运行，因此未知字段也会失败。
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在项目上的角色不足。
        '404':
          description: 项目不存在。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 将被拒绝的 story 退回给 PO，并附上理由
      tags:
        - backlog
  /projects/{projectId}/stories/{storyId}/title:
    patch:
      description: >-
        仅限 `draft` story，且没有执行中的任务（`in_progress`/`in_review`）。写入
        `backlog.story_updated`，附带旧标题和新标题。所需角色与其他 backlog 写入操作（提升/退回）相同。
      operationId: BacklogController_updateTitle
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
        - name: storyId
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateStoryTitleDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/StoryResponseDto'
        '400':
          description: 标题为空或超过 200 个字符。
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在项目上的角色不足。
        '404':
          description: 项目不存在。
        '409':
          description: >-
            具名拒绝（`reason`）：`historia_arquivada`、`historia_nao_draft` 或
            `historia_com_tarefa_em_execucao`。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 修正草稿 story 的标题（RN-727）
      tags:
        - backlog
  /projects/{projectId}/stories/promote:
    post:
      description: >-
        `manual` 模式（自 Phase 12c 起为默认）交还给用户的人工步骤：在提升之前，该 story 的任何任务都不能被任何 dev
        agent 认领。提升会一次性放出这批任务，并唤醒该模块中空闲的代理（Phase 12b）。这一批不是全有或全无——未通过的会以
        `failed` 返回并附上原因，通过的则被提升。
      operationId: BacklogController_promote
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PromoteStoriesDto'
      responses:
        '201':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PromoteStoriesResponseDto'
        '400':
          description: >-
            请求体无效。`ValidationPipe` 以 `whitelist` 和 `forbidNonWhitelisted`
            运行，因此未知字段也会失败。
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在项目上的角色不足。
        '404':
          description: 项目不存在。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 将 PO 提议的 story 提升为 `ready`
      tags:
        - backlog
  /projects/{projectId}/tasks/{taskId}/gates/{gate}/resume:
    post:
      description: 停滞超过 2 小时的 gate 周期会被暂停，而不是自行重启（ADR 0207）。这是恢复它的人工操作。
      operationId: GateResumeController_resume
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
        - name: taskId
          required: true
          in: path
          schema:
            type: string
        - name: gate
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OkResponseDto'
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在项目上的角色低于 `developer`。
        '409':
          description: '该周期未被暂停——响应体带有 `reason: "gate_nao_estacionado"`。'
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 恢复一个被暂停的 gate 周期
      tags:
        - gates
  /projects/{projectId}/terminal-ticket:
    post:
      description: >-
        同一个 `/runner` 套接字、同一个主题，角色为 “terminal”——即查看终端的一方（命令本身只有在有 runner
        连接时才会真正运行）。适用于任何项目模式。
      operationId: RunnerTicketsController_terminalTicket
      parameters:
        - name: projectId
          required: true
          in: path
          schema:
            type: string
      responses:
        '201':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RunnerTicketResponseDto'
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在项目上的角色不足。
        '404':
          description: 未找到项目。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 为 Web 的 Terminal 标签页签发一次性票据
      tags:
        - projects
  /runner-releases/binary:
    get:
      description: >-
        GitHub Releases 代理——`platform` 只接受
        linux-x64、linux-arm64、darwin-arm64。无需认证：二进制文件不是机密。在返回任何内容之前，字节会与同一
        Release 的 `checksums.txt` 核对（ADR
        0149，RN-525）；该路由不验证清单的签名——这是完整性而非来源，验证签名的是 `install.sh`。
      operationId: RunnerReleasesController_binary
      parameters: []
      responses:
        '200':
          description: >-
            以流方式返回 runner 二进制文件，其 sha256 已与清单核对。不是 JSON：`Content-Type:
            application/octet-stream`。`Content-Digest` 头重复已核对的哈希（RFC 9530）。
          content:
            application/octet-stream:
              schema:
                type: string
                format: binary
        '502':
          description: >-
            具名拒绝——绝不返回带警告的字节。响应体带有
            `motivo`：`plataforma_nao_publicada`、`release_sem_manifesto`、`manifesto_nao_cobre_a_plataforma`、`manifesto_ilegivel`、`download_falhou`
            或 `hash_divergente`。
      summary: 为所请求的平台下载本地 runner 的独立二进制文件
      tags:
        - infrastructure
  /runner/llm-credentials:
    post:
      description: >-
        Authenticated by a MACHINE device key (never a session JWT, never a
        project-bound credential). Same envelope encryption as `POST
        /users/me/credentials`. 409 `credencial_existente` when the user already
        has this provider and `substituir` is not `true`. The key is never
        logged nor returned.
      operationId: RunnerLlmCredentialsController_execute
      parameters: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ImportLlmCredentialDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ImportLlmCredentialResponseDto'
        '400':
          description: >-
            请求体无效。`ValidationPipe` 以 `whitelist` 和 `forbidNonWhitelisted`
            运行，因此未知字段也会失败。
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: Credential bound to a project.
        '409':
          description: Code `credencial_existente`.
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: Stores an LLM API key imported by the local runner
      tags:
        - credentials
  /runner/projects:
    get:
      description: >-
        通过机器设备密钥认证（ADR 0154）——绝不通过会话 JWT，也绝不通过绑定到项目的凭据，后者返回 403。返回处于
        `execution_mode: "runner"` 的项目，其中密钥所有者至少拥有 `developer`（与 `POST
        .../runner-ticket`
        的最低要求相同），并附带每个项目的文件夹名称和验证状态。代理会询问而不是扫描磁盘：机器的基目录属于用户，其中可能有并非任何项目的文件夹。
      operationId: RunnerProjectsController_listRunnerProjects
      parameters: []
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/RunnerProjectResponseDto'
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 所出示的凭据绑定到某个项目——此路由需要机器设备密钥。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 列出此本地代理所服务的 “runner” 模式项目
      tags:
        - projects
  /users/me/credentials:
    get:
      description: 无需角色：凭据关乎调用者本人，而不是某个 workspace 或项目。在同一列表中涵盖 LLM 密钥和 git 令牌。
      operationId: CredentialsController_list
      parameters: []
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/UserCredentialResponseDto'
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 列出当前用户自己的凭据
      tags:
        - credentials
    post:
      description: 按（用户，提供商）upsert。密钥在接触数据库之前通过信封加密进行加密，响应只携带投影——绝不包含机密，即使是加密后的也不包含。
      operationId: CredentialsController_create
      parameters: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpsertCredentialDto'
      responses:
        '201':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UserCredentialResponseDto'
        '400':
          description: >-
            请求体无效。`ValidationPipe` 以 `whitelist` 和 `forbidNonWhitelisted`
            运行，因此未知字段也会失败。
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 登记或替换某个提供商的 API 密钥
      tags:
        - credentials
  /users/me/credentials/{provider}:
    delete:
      description: '幂等：删除不存在的内容也返回 `{ ok: true }`。'
      operationId: CredentialsController_remove
      parameters:
        - name: provider
          required: true
          in: path
          schema:
            example: anthropic
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OkResponseDto'
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 移除某个提供商的凭据
      tags:
        - credentials
  /users/me/credentials/{provider}/test:
    post:
      description: >-
        检查是一个显式操作，与登记分开（ADR 0050）：api 解密机密、调用提供商，只返回裁决——密钥绝不会返回，哪怕是一部分。三种结果都返回
        200，因为请求已被处理；`refused` 是一种结果，不是协议错误。当（用户，提供商）没有凭据时返回 404——那时没有可测试的东西。
      operationId: CredentialsController_test
      parameters:
        - name: provider
          required: true
          in: path
          schema:
            example: openrouter
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CredentialTestResultResponseDto'
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '404':
          description: 此提供商没有凭据。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 用提供商检查已存储的凭据
      tags:
        - credentials
  /users/me/git-credentials:
    post:
      description: >-
        使用自己的路径，而不是 `users/me/credentials`，因为请求体不同（`token`，且提供商枚举为
        `github`/`gitlab`）。令牌经过加密并在未事先验证的情况下存储（ADR 0050）；如需验证，请调用 `POST
        /users/me/credentials/{provider}/test`。读取、移除和测试复用 `users/me/credentials`
        路由——它们是同一张表。
      operationId: GitCredentialsController_create
      parameters: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/RegisterGitCredentialDto'
      responses:
        '201':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UserCredentialResponseDto'
        '400':
          description: >-
            请求体无效。`ValidationPipe` 以 `whitelist` 和 `forbidNonWhitelisted`
            运行，因此未知字段也会失败。
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 登记一个 git 令牌（加密，不测试）
      tags:
        - credentials
  /users/me/machine-device-keys:
    get:
      description: >-
        账户级别，路径中没有项目（RN-611）：一行命令安装会在任何项目存在之前创建机器密钥，而其他所有列表都是按项目的。只返回机器密钥（`especie:
        "maquina"`，`projectId:
        null`），且只返回调用方的——项目密钥保留在其项目的列表中，任何人都看不到其他用户的密钥（RN-519）。已撤销的密钥也包含在内：登记新的机器密钥会撤销上一把（RN-552），而这个列表就是体现这一点的地方。`lastUsedAt`
        是一次记录的使用，绝不是活动连接；为空表示该密钥从未被使用。
      operationId: MachineDeviceKeysController_listMachineDeviceKeys
      parameters: []
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/RunnerDeviceKeyListResponseDto'
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 列出已认证用户自己的机器设备密钥
      tags:
        - users
  /users/me/machine-device-keys/{deviceKeyId}:
    delete:
      description: >-
        幂等——再次撤销不是错误。与 `DELETE
        /projects/{projectId}/runner-device-keys/{deviceKeyId}`
        是同一种撤销：它还会断开用此密钥打开的活动连接，遍及所有项目（RN-520/RN-543）。自 RN-685（ADR
        0201）起，目标是密钥本身：同一用户用 PAT 或其他密钥连接的另一个 runner
        不受影响。该密钥尚未使用的票据会被作废。还没有项目时，就没有可断开的连接。项目密钥、不存在的密钥和其他用户的密钥都返回同样的 404。
      operationId: MachineDeviceKeysController_revokeMachineDeviceKey
      parameters:
        - name: deviceKeyId
          required: true
          in: path
          schema:
            type: string
      responses:
        '204':
          description: 密钥已撤销。无响应体。
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '404':
          description: 没有属于调用方且具有此 id 的机器密钥（它不存在、是项目密钥，或属于别人——一种回答涵盖所有情况）。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 撤销已认证用户自己的一把机器设备密钥
      tags:
        - users
  /users/me/preferences:
    get:
      operationId: UserPreferencesController_get
      parameters: []
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UserPreferencesResponseDto'
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 读取已认证用户的界面语言和代理回复语言
      tags:
        - users
    patch:
      description: >-
        两个字段都是可选且相互独立的——发送其中一个绝不会影响另一个，两者都没有的请求体返回 400。`locale`（界面）仍限定在
        `pt-BR`/`en` 列表中。`responseLanguage`（RN-618）为 `automatico` 或任何可识别的 BCP-47
        代码，以规范形式存储；无法识别的代码返回 400，请求体中的任何内容都不会被写入。
      operationId: UserPreferencesController_update
      parameters: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateUserPreferencesDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UserPreferencesResponseDto'
        '400':
          description: >-
            请求体无效。`ValidationPipe` 以 `whitelist` 和 `forbidNonWhitelisted`
            运行，因此未知字段也会失败。
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 写入已认证用户的界面语言和/或代理回复语言
      tags:
        - users
  /users/me/preferences/detected-language:
    post:
      description: >-
        检测到的语言成为偏好的唯一途径（RN-624）：仅凭检测绝不会改变任何东西。`confirm` 写入 `detectedLanguage` +
        `detectedLanguageConfirmedAt`；`decline` 记录拒绝，以免再次询问同一种语言。返回回答之后的偏好设置。
      operationId: UserPreferencesController_answerDetected
      parameters: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AnswerDetectedLanguageDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UserPreferencesResponseDto'
        '400':
          description: 不是可识别的 BCP-47 语言代码。
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '409':
          description: '`deteccao_mudou`：在 `confirm` 时，你最近的消息已不再指向该语言——未写入任何内容。'
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 回答关于检测到的语言的问题
      tags:
        - users
  /workspaces:
    get:
      description: 已按关联关系过滤——没有全局列表。每个条目都携带调用方自己的角色。
      operationId: WorkspacesController_list
      parameters: []
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/WorkspaceComPapelResponseDto'
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在 workspace 中的角色不足。
        '404':
          description: workspace 不存在或对调用方不可见。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 列出调用方的 workspace
      tags:
        - workspaces
    post:
      description: 不需要任何角色：创建者成为 `owner`。这是尚不属于任何 workspace 的人的唯一入口。
      operationId: WorkspacesController_create
      parameters: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateWorkspaceDto'
      responses:
        '201':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WorkspaceResponseDto'
        '400':
          description: >-
            请求体无效。`ValidationPipe` 以 `whitelist` 和 `forbidNonWhitelisted`
            运行，因此未知字段也会失败。
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在 workspace 中的角色不足。
        '404':
          description: workspace 不存在或对调用方不可见。
        '409':
          description: 已存在使用此 slug 的 workspace。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 创建一个 workspace
      tags:
        - workspaces
  /workspaces/{workspaceId}:
    get:
      operationId: WorkspacesController_get
      parameters:
        - name: workspaceId
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WorkspaceResponseDto'
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在 workspace 中的角色不足。
        '404':
          description: workspace 不存在或对调用方不可见。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 按 id 返回 workspace
      tags:
        - workspaces
    patch:
      operationId: WorkspacesController_update
      parameters:
        - name: workspaceId
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateWorkspaceDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WorkspaceResponseDto'
        '400':
          description: >-
            请求体无效。`ValidationPipe` 以 `whitelist` 和 `forbidNonWhitelisted`
            运行，因此未知字段也会失败。
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在 workspace 中的角色不足。
        '404':
          description: workspace 不存在或对调用方不可见。
        '409':
          description: 已存在使用此 slug 的 workspace。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 修改 workspace 的名称或 slug
      tags:
        - workspaces
    delete:
      description: 需要 `owner`。会连同其项目、会话及其历史一起删除。
      operationId: WorkspacesController_remove
      parameters:
        - name: workspaceId
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WorkspaceResponseDto'
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在 workspace 中的角色不足。
        '404':
          description: workspace 不存在或对调用方不可见。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 删除 workspace
      tags:
        - workspaces
  /workspaces/{workspaceId}/containers:
    get:
      description: >-
        workspace 的每个项目一行——自 RN-521 起，从未置备过容器的项目会以 `registrado: null`
        出现，而不是缺席，因为这个页面是人工启动第一个容器的途径。只对 `provisioning`/`running` 的行向 broker
        查询观测状态，且仅在每次加载的预算之内——被跳过的行见 `naoVerificado`，原因见 ADR 0136。
      operationId: ContainersOverviewController_list
      parameters:
        - name: workspaceId
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/ContainerOverviewItemResponseDto'
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在 workspace 中的角色不足。
        '404':
          description: workspace 不存在或对调用方不可见。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 列出 workspace 中的每个项目及其容器（如有）
      tags:
        - projects
  /workspaces/{workspaceId}/credential-spend:
    get:
      description: >-
        之所以存在，是因为代理花费的密钥属于 workspace
        所有者（RN-058）——付账的人需要看到账单。按提供商分组，因为那是凭据的单位，并把代理花出去的与人在聊天中花出去的分开：两者出自同一把密钥，回答的是不同的问题。不返回任何机密。
      operationId: BudgetsController_getCredentialSpend
      parameters:
        - name: workspaceId
          required: true
          in: path
          schema:
            type: string
        - name: meses
          required: true
          in: query
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CredentialSpendResponseDto'
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 需要在 workspace 上拥有 `owner`。
        '404':
          description: 未找到项目或会话。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 所有者的密钥花费了多少，按提供商和按月
      tags:
        - llm
  /workspaces/{workspaceId}/huggingface/models:
    get:
      description: >-
        默认只筛选官方发布者（允许列表位于 `domain/llm/huggingface-official-publishers.ts`）——与
        LLM 目录（ADR 0042）“始终人工策展”的精神相同，只是应用于“官方”徽章而不是激活。`includeCommunity=true`
        会纳入所有发布者，每个都标记为 `official: false`，供界面显示安全警告——绝不隐藏。
      operationId: HuggingFaceModelsController_search
      parameters:
        - name: q
          required: true
          in: query
          description: 搜索文本。
          schema:
            type: string
        - name: includeCommunity
          required: false
          in: query
          description: 传入 `true` 以同时包含非官方发布者。
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/HuggingFaceModelResponseDto'
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 需要在 workspace 上拥有 `maintainer`。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 在 Hugging Face Hub 上搜索 GGUF 模型
      tags:
        - llm
  /workspaces/{workspaceId}/huggingface/pull-requests:
    post:
      description: >-
        两个显式步骤中的第一步：此调用只记录意图，状态为 `pending_confirmation`。在另行调用 `POST
        .../pull-requests/:id/confirm` 之前不会拉取任何东西——产品绝不会运行自动的、静默的拉取。
      operationId: HuggingFaceModelsController_createPullRequest
      parameters:
        - name: workspaceId
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/RequestModelPullDto'
      responses:
        '201':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ModelPullRequestResponseDto'
        '400':
          description: >-
            请求体无效。`ValidationPipe` 以 `whitelist` 和 `forbidNonWhitelisted`
            运行，因此未知字段也会失败。
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 需要在 workspace 上拥有 `maintainer`。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 创建一个待处理的拉取请求——尚不下载任何东西
      tags:
        - llm
  /workspaces/{workspaceId}/huggingface/pull-requests/{id}:
    get:
      operationId: HuggingFaceModelsController_getStatus
      parameters:
        - name: workspaceId
          required: true
          in: path
          schema:
            type: string
        - name: id
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ModelPullRequestResponseDto'
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 需要在 workspace 上拥有 `maintainer`。
        '404':
          description: 未找到拉取请求。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 拉取请求的状态，供前端轮询
      tags:
        - llm
  /workspaces/{workspaceId}/huggingface/pull-requests/{id}/confirm:
    post:
      description: >-
        将 `pending_confirmation` → `confirmed` → `pulling`，调用 Ollama 守护进程，成功后将模型
        upsert 到目录（`models`）并为此 workspace 激活它。失败时请求以 `failed`
        结束，附带声明其来源的原因（infra | model | code |
        policy）——绝不是静默失败。此调用在整个拉取过程中同步运行（原因见用例）；请使用下面的 GET 路由轮询，而不要一直保持此连接。
      operationId: HuggingFaceModelsController_confirm
      parameters:
        - name: workspaceId
          required: true
          in: path
          schema:
            type: string
        - name: id
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ModelPullRequestResponseDto'
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 需要在 workspace 上拥有 `maintainer`。
        '404':
          description: 未找到拉取请求。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 第二次确认——真正执行拉取
      tags:
        - llm
  /workspaces/{workspaceId}/llm-providers/credentialed:
    get:
      description: >-
        The owner of record (`workspaces.created_by`) is whose key the agents
        spend (RN-058). Only provider NAMES — never a secret, id or date.
        `ollama` (local, no key) never appears.
      operationId: ModelsController_credentialed
      parameters:
        - name: workspaceId
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CredentialedProvidersResponseDto'
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: Lists the LLM providers the workspace owner has a key for
      tags:
        - llm
  /workspaces/{workspaceId}/members:
    get:
      description: >-
        谁与该 workspace 关联，以及其 workspace 角色——除非项目记录覆盖它，每个项目都会继承这个角色。补充 `GET
        projects/:projectId/members`，后者只列出项目记录：仅通过 workspace
        进入项目的人出现在这里而不是那里。只包含 id、姓名、邮箱和角色。
      operationId: WorkspacesController_listMembers
      parameters:
        - name: workspaceId
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/WorkspaceMemberComUsuarioResponseDto'
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在 workspace 中的角色不足。
        '404':
          description: workspace 不存在或对调用方不可见。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 列出 workspace 的成员
      tags:
        - workspaces
    post:
      description: >-
        只有 `owner` 可以改动成员名单。这里的角色会被该 workspace
        的所有项目继承。这个角色是必要的但不是充分的：在这里修改你自己的角色会以 403
        拒绝，两个方向都是，且在任何地方都无法启用。上面没有更高层级来兜底，因此自我降级将无法通过界面恢复。降级另一个 `owner`
        仍然允许——这是撤销所有权的唯一方式。
      operationId: WorkspacesController_addMember
      parameters:
        - name: workspaceId
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AddMemberDto'
      responses:
        '201':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WorkspaceMemberResponseDto'
        '400':
          description: >-
            请求体无效。`ValidationPipe` 以 `whitelist` 和 `forbidNonWhitelisted`
            运行，因此未知字段也会失败。
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 不是该 workspace 的 `owner`，或者目标是调用方本人且所请求的角色与其当前角色不同（自我变动上限）。
        '404':
          description: workspace 不存在或对调用方不可见。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 将用户关联到 workspace
      tags:
        - workspaces
  /workspaces/{workspaceId}/members/{userId}:
    delete:
      description: >-
        只有 `owner` 可以调用，与 upsert 相同。移除 workspace 记录，并在同一事务中移除该用户在此 workspace
        每个项目中的记录（项目角色覆盖 workspace
        角色，保留它们会让该用户留在那些项目中），以及该用户在这些项目中的项目设备密钥和个人访问令牌。之后会尽力断开该用户在每个项目中的活动
        runner 连接。机器设备密钥和已打开的会话不受影响——由于角色解析为无，它们不再能触及此 workspace。


        移除你自己会以 403 拒绝，且在任何地方都无法启用：上面没有更高层级来兜底。正是这一条款保护了最后一个 `owner`——只有 `owner`
        可以调用此路由且没有人能移除自己，因此每次成功调用后至少还有调用方是 `owner`。移除另一个 `owner`
        是允许的——这是撤销所有权的方式——但记录所有者（`createdBy`）除外，它会以 409 `criador_do_workspace`
        拒绝，直到 `PUT :workspaceId/owner-of-record` 把它转给另一个所有者：代理花费的 LLM
        密钥和花费报告都是他们的。幂等：没有 workspace 记录的目标仍会清除其在此 workspace 中的项目记录，并返回 `204`。
      operationId: WorkspacesController_removeMember
      parameters:
        - name: workspaceId
          required: true
          in: path
          schema:
            type: string
        - name: userId
          required: true
          in: path
          schema:
            type: string
      responses:
        '204':
          description: 关联已移除。无响应体。
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 不是该 workspace 的 `owner`，或者目标是调用方本人（自我移除上限）。
        '404':
          description: workspace 不存在或对调用方不可见。
        '409':
          description: '`criador_do_workspace`：目标是记录所有者；请先转移。'
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 解除用户与 workspace 的关联
      tags:
        - workspaces
  /workspaces/{workspaceId}/model-binding:
    get:
      description: 此范围的原始 binding。它是级联的根。
      operationId: ModelBindingsController_getWorkspaceBinding
      parameters:
        - name: workspaceId
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ModelBindingResponseDto'
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在该范围上的角色不足。
        '404':
          description: 未找到该范围。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 读取 workspace 的模型 binding
      tags:
        - llm
    put:
      description: 适用于所有没有自身 binding 的项目。
      operationId: ModelBindingsController_setWorkspaceBinding
      parameters:
        - name: workspaceId
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SetModelBindingDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ModelBindingResponseDto'
        '400':
          description: >-
            请求体无效。`ValidationPipe` 以 `whitelist` 和 `forbidNonWhitelisted`
            运行，因此未知字段也会失败。
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在该范围上的角色不足。
        '404':
          description: 未找到该范围。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 设置 workspace 的默认模型
      tags:
        - llm
  /workspaces/{workspaceId}/models/{modelId}/billing:
    patch:
      description: >-
        Same curation level as the pricing route. `subscription` makes the spend
        report show the cost apart from the US$ paid (RN-954). Never re-prices
        the past.
      operationId: ModelsController_updateBilling
      parameters:
        - name: modelId
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateModelBillingDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ModelResponseDto'
        '400':
          description: >-
            请求体无效。`ValidationPipe` 以 `whitelist` 和 `forbidNonWhitelisted`
            运行，因此未知字段也会失败。
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 需要在 workspace 上拥有 `owner`。
        '404':
          description: 未找到模型。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: Sets a model's curated billing mark (api | subscription | free)
      tags:
        - llm
  /workspaces/{workspaceId}/models/{modelId}/price-changes:
    get:
      description: 最新的在前。这是不可变的日志：绝不执行 UPDATE，每条记录都携带前/后值对，因此审计不依赖于从上一条记录重建“之前”的值。
      operationId: ModelsController_priceChanges
      parameters:
        - name: modelId
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/ModelPriceChangeResponseDto'
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在 workspace 上的角色不足。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 模型价格变更的历史
      tags:
        - llm
  /workspaces/{workspaceId}/models/{modelId}/pricing:
    patch:
      description: >-
        从现在起生效，绝不重新为过去定价：每条 `token_usage`
        记录都保留产生其成本的价格。变更会连同前/后值对一起记录——可通过历史路由查询。
      operationId: ModelsController_updatePricing
      parameters:
        - name: modelId
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateModelPricingDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ModelResponseDto'
        '400':
          description: >-
            请求体无效。`ValidationPipe` 以 `whitelist` 和 `forbidNonWhitelisted`
            运行，因此未知字段也会失败。
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 需要在 workspace 上拥有 `owner`。
        '404':
          description: 未找到模型。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 修正模型的价格
      tags:
        - llm
  /workspaces/{workspaceId}/models/activate:
    post:
      description: >-
        所有者的策展，仅适用于此 workspace（ADR 0049）。只改动 `isActive`——`availability`
        是同步在提供商处观测到的，是全局的，在这里不会改变。整批全有或全无：不存在的 id 会让调用失败，不应用任何一行。
      operationId: ModelsController_activate
      parameters:
        - name: workspaceId
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SetModelsActiveDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/ModelComCuradoriaResponseDto'
        '400':
          description: >-
            请求体无效。`ValidationPipe` 以 `whitelist` 和 `forbidNonWhitelisted`
            运行，因此未知字段也会失败。
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 需要在 workspace 上拥有 `owner`。
        '404':
          description: 批次中有某个 id 不存在。
        '422':
          description: >-
            激活 OpenRouter 免费路由别名（`~…`）——`code:
            "alias_de_roteamento_livre"`，并附被拒绝的
            `modelIds`。整批都会被拒绝。停用一个别名始终允许（AT-271，RN-679）。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 在选择器中开启或关闭模型（批量）
      tags:
        - llm
  /workspaces/{workspaceId}/models/catalog:
    get:
      description: >-
        策展界面。同步发现的模型以 `isActive: false` 进入，在有人激活它之前只出现在这里；从提供商消失的模型会带有
        `availability: "unavailable"`，且绝不删除，因为 binding 和成本历史都指向它。`isActive` 是针对此
        workspace 的（ADR 0049）；`availability` 是全局的，因为它是同步在提供商处观测到的。
      operationId: ModelsController_catalog
      parameters:
        - name: workspaceId
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                type: object
                properties:
                  local:
                    type: object
                    additionalProperties:
                      type: array
                      items:
                        $ref: '#/components/schemas/ModeloDoCatalogoResponseDto'
                  cloud:
                    type: object
                    additionalProperties:
                      type: array
                      items:
                        $ref: '#/components/schemas/ModeloDoCatalogoResponseDto'
                example:
                  local:
                    ollama: []
                  cloud:
                    anthropic: []
                    openai: []
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在 workspace 上的角色不足。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 列出整个目录，包括未激活和不可用的模型
      tags:
        - llm
  /workspaces/{workspaceId}/models/sync:
    post:
      description: 与 engine 调度的内部路由相同的用例——策展界面的刷新按钮没有自己的路径，因此绝不会出现两个可能产生分歧的对账。
      operationId: ModelsController_sync
      parameters: []
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SyncModelCatalogResponseDto'
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 需要在 workspace 上拥有 `owner`。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 立即强制执行定期任务所做的目录同步
      tags:
        - llm
  /workspaces/{workspaceId}/models/uses:
    post:
      description: >-
        没有任何目录会公布的维度：“适合写代码”不是任何提供商声明的能力，而是运营者的意见——且仅适用于此 workspace（ADR
        0049）。与激活相互独立的维度：标记某种用途不会在选择器中开启模型，更改用途也不会关闭已开启的模型。用途列表会替换之前的列表。
      operationId: ModelsController_uses
      parameters:
        - name: workspaceId
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SetModelUsesDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/ModelComCuradoriaResponseDto'
        '400':
          description: >-
            请求体无效。`ValidationPipe` 以 `whitelist` 和 `forbidNonWhitelisted`
            运行，因此未知字段也会失败。
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 需要在 workspace 上拥有 `owner`。
        '404':
          description: 批次中有某个 id 不存在。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 标记 workspace 将每个模型用于什么（批量）
      tags:
        - llm
  /workspaces/{workspaceId}/owner-of-record:
    put:
      description: >-
        记录所有者是 `createdBy`：代理花费的是谁的 LLM 凭据、代理操作使用的是谁的 git
        凭据（RN-058），以及花费报告归谁所有（RN-060）。它不授予授权——授权由角色决定。目标必须已经是该 workspace 的
        `owner`（否则返回 409
        `titular_precisa_ser_owner`）。从提交那一刻起，代理回合会查找新记录所有者的凭据；如果他们没有该模型提供商的凭据，回合会以该提供商现有的“没有登记凭据”结果结束——此路由不会事先检查。任何
        `owner` 都可以调用，包括为另一个所有者调用。转给当前的记录所有者是空操作。
      operationId: WorkspacesController_transferOwnership
      parameters:
        - name: workspaceId
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TransferOwnershipDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WorkspaceResponseDto'
        '400':
          description: >-
            请求体无效。`ValidationPipe` 以 `whitelist` 和 `forbidNonWhitelisted`
            运行，因此未知字段也会失败。
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在 workspace 中的角色不足。
        '404':
          description: workspace 不存在或对调用方不可见。
        '409':
          description: '`titular_precisa_ser_owner`：目标不是该 workspace 的 `owner`。'
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 转移 workspace 的记录所有者
      tags:
        - workspaces
  /workspaces/{workspaceId}/project-folders:
    get:
      description: >-
        Mounted 模式的文件夹选择器，由 api 而不是 runner 提供（RN-504）。严格限定在
        `BRABO_PROJECTS_BASE` 内（ADR 0141）：`path` 是可选的，默认为基目录本身，任何在其之外的 `path`
        都返回 `400`——这是格式错误的请求，而不是权限问题，因为没有任何角色能浏览基目录之外的内容。


        刻意设限：只列目录，最多 500 个（在截断之前排序，并用 `truncado`
        说明），绝不递归，排除以点开头的条目，符号链接只报告而绝不跟随。被略去的内容会被计数（`arquivos`、`simbolicos`），这样满是代码的文件夹绝不会看起来是空的。


        没有配套的 POST：创建文件夹属于物化挂载 workspace 的过程，绝不属于选择器。
      operationId: WorkspacesController_listProjectFolders
      parameters:
        - name: path
          required: false
          in: query
          description: >-
            要列出的绝对路径。省略即基目录本身。必须位于 `BRABO_PROJECTS_BASE` 内；`..`、`.`
            和相对路径会被直接拒绝，而不是被解析。
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProjectFoldersResponseDto'
        '400':
          description: 路径在项目基目录之外，或格式错误。
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在 workspace 中的角色不足。
        '404':
          description: workspace 不存在或对调用方不可见。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 列出项目基目录内某个文件夹的子文件夹
      tags:
        - workspaces
  /workspaces/{workspaceId}/projects:
    get:
      operationId: WorkspacesController_listProjects
      parameters:
        - name: workspaceId
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/ProjectResponseDto'
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在 workspace 中的角色不足。
        '404':
          description: workspace 不存在或对调用方不可见。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 列出 workspace 的项目
      tags:
        - workspaces
    post:
      operationId: WorkspacesController_createProjectInWorkspace
      parameters:
        - name: workspaceId
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateProjectDto'
      responses:
        '201':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProjectResponseDto'
        '400':
          description: >-
            请求体无效。`ValidationPipe` 以 `whitelist` 和 `forbidNonWhitelisted`
            运行，因此未知字段也会失败。
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在 workspace 中的角色不足。
        '404':
          description: workspace 不存在或对调用方不可见。
        '409':
          description: 该 workspace 中已存在使用此 slug 的项目。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 在 workspace 内创建项目
      tags:
        - workspaces
  /workspaces/{workspaceId}/projects-base:
    get:
      description: >-
        运营者机器上 api 和 engine 容器都能看到的那个唯一文件夹，按身份挂载（ADR
        0141）。`null`——一种正常状态，绝不是错误——表示此安装没有 `BRABO_PROJECTS_BASE`，因此项目向导完全不能提供
        Mounted 模式。每个 workspace 的值都相同：它是安装配置，`workspaceId` 只用于限定授权范围。


        `brokerConfigurado` 回答同一问题的另一半：`container` 和 `mounted` 只通过 broker
        启动容器（ADR 0144），因此没有它时向导不会预选 Mounted，而是提供 Runner（ADR 0161，RN-573）。
      operationId: WorkspacesController_getProjectsBase
      parameters: []
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProjectsBaseResponseDto'
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在 workspace 中的角色不足。
        '404':
          description: workspace 不存在或对调用方不可见。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: Mounted 模式项目的基目录
      tags:
        - workspaces
  /workspaces/{workspaceId}/projects-status:
    get:
      description: workspace 中每个有阻塞任务的项目一行——用一次查询为仪表盘侧边栏的状态点提供数据，而不是对 backlog 调用 N 次。
      operationId: WorkspacesController_getProjectsStatus
      parameters:
        - name: workspaceId
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/ProjectBlockedStatusResponseDto'
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在 workspace 中的角色不足。
        '404':
          description: workspace 不存在或对调用方不可见。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 每个项目的阻塞任务数
      tags:
        - workspaces
  /workspaces/{workspaceId}/projects-summary:
    get:
      description: >-
        workspace 中每个项目一行，包含 git 提供商、置备状态、预算、最近活动、等待提升的
        story，以及决定代理名单的事实（RN-090）。


        与 `projects-status` 同样的思路，只是高了一层：卡片网格是一次 workspace 读取，而不是 N
        次项目读取。之前的做法每张卡片要发起七个轮询查询，有 23 个项目时仅仪表盘就突破了 300 次/分钟的速率限制。
      operationId: WorkspacesController_getProjectsSummary
      parameters:
        - name: workspaceId
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/ProjectCardSummaryResponseDto'
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在 workspace 中的角色不足。
        '404':
          description: workspace 不存在或对调用方不可见。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 一次调用拿到仪表盘卡片渲染所需的一切
      tags:
        - workspaces
  /workspaces/{workspaceId}/spend-report:
    get:
      description: >-
        面向所有者的视图。提供商维度回来了（ADR
        0076，RN-186），并且在这里而不是在成员的路由上，因为按提供商细分就是按凭据细分——而凭据是付钱的人的事（RN-060）。补充
        `credential-spend`，后者继续回答账单问题（按月，并与当前存在的密钥对应）。人和代理也按 `actor_kind`
        分成单独的块。以天为单位的滑动窗口，每日序列是稠密的——没有花费的日子以零出现，否则迷你图会在节奏上说谎。
      operationId: SpendController_getWorkspaceSpendReport
      parameters:
        - name: workspaceId
          required: true
          in: path
          schema:
            type: string
        - name: dias
          required: false
          in: query
          description: 滑动窗口。默认 30，最大 180。
          schema:
            type: string
        - name: projectId
          required: false
          in: query
          description: >-
            Slice to ONE project of this workspace (RN-952). A project from
            another workspace answers 404.
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WorkspaceSpendReportResponseDto'
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 需要在 workspace 上拥有 `owner`。
        '404':
          description: Project not in this workspace.
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 按模型、提供商、项目、参与者和天细分 workspace 的花费
      tags:
        - llm
  /workspaces/{workspaceId}/spend/me:
    get:
      description: >-
        The non-owner's global page (RN-953). Only the caller's rows, by PROJECT
        and by day, without provider or credential.
      operationId: SpendController_getMyWorkspaceSpend
      parameters:
        - name: workspaceId
          required: true
          in: path
          schema:
            type: string
        - name: dias
          required: false
          in: query
          description: 滑动窗口。默认 30，最大 180。
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MyWorkspaceSpendResponseDto'
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 角色不足。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: My own consumption in this workspace, summed by project
      tags:
        - llm
  /workspaces/{workspaceId}/summary:
    get:
      description: 项目数、本月花费过 token 的代理以及本月花费——为项目仪表盘的摘要行提供数据。
      operationId: WorkspacesController_getSummary
      parameters:
        - name: workspaceId
          required: true
          in: path
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WorkspaceSummaryResponseDto'
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在 workspace 中的角色不足。
        '404':
          description: workspace 不存在或对调用方不可见。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: workspace 汇总摘要
      tags:
        - workspaces
  /workspaces/{workspaceId}/tool-router:
    put:
      description: >-
        默认开启。开启且回合模型来自 OpenRouter 时，拥有两个或更多工具的代理步骤会先询问 Jev
        哪个工具合适，聊天模型只会被提供该工具和上一个工具。Jev 从不批准或拒绝任何东西：需要批准的操作仍然需要批准。Jev
        的任何失败都会回落到整个目录。关闭它会停止对 Jev 的所有调用，其成本也随之停止。
      operationId: WorkspacesController_setToolRouter
      parameters:
        - name: workspaceId
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SetToolRouterDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WorkspaceResponseDto'
        '400':
          description: >-
            请求体无效。`ValidationPipe` 以 `whitelist` 和 `forbidNonWhitelisted`
            运行，因此未知字段也会失败。
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在 workspace 中的角色不足。
        '404':
          description: workspace 不存在或对调用方不可见。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 为 workspace 开启或关闭 Jev 工具路由
      tags:
        - workspaces
  /workspaces/{workspaceId}/unread-events:
    post:
      description: >-
        铃铛的抽屉，面向整个 workspace（RN-091）。每个游标说明该项目已读到哪个 `seq`，响应携带每个项目最近会话中在此之后的内容。


        **之所以用 POST，是因为它是唯一带请求体的动词，而不是因为它改变状态。**
        服务器不知道每个读者停在哪里：“读到这里”是存储在浏览器中的每个项目一个
        `seq`，并且（刻意地）没有标记为已读的端点。因此截止点需要随请求传递，而这样的对有几十个——放在查询字符串中会变成很长的
        URL，代理会截断它，还会把用户的项目 id 写进访问日志。该路由是幂等的且没有副作用：它返回 `200`，绝不返回 `201`。


        取代了抽屉打开时每个项目一次的请求——23 个项目要花 286 次/分钟，而限制是 300。
      operationId: WorkspacesController_getUnreadEvents
      parameters:
        - name: workspaceId
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UnreadEventsDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/ProjectUnreadEventsResponseDto'
        '400':
          description: >-
            请求体无效。`ValidationPipe` 以 `whitelist` 和 `forbidNonWhitelisted`
            运行，因此未知字段也会失败。
        '401':
          description: 没有令牌、令牌已过期或签名无效。
        '403':
          description: 在 workspace 中的角色不足。
        '404':
          description: workspace 不存在或对调用方不可见。
        '429':
          description: 按用户或按 IP 的速率限制。
      security:
        - bearer: []
      summary: 一次调用获取多个项目中的未读事件
      tags:
        - workspaces
info:
  title: Brabo API
  description: >-
    Brabo API。除了四个明确记录了不同 `content-type` 的端点（两个 SSE 端点、指标端点，以及会重定向的 OAuth
    回调）之外，每个端点都返回 JSON。


    ## 认证


    访问令牌是一个 **15 分钟有效的 EdDSA JWT**，通过

    `Authorization: Bearer <token>` 出示。公钥位于

    `GET /.well-known/jwks.json`。


    refresh **不是** JWT：它是一个不透明令牌，存放在

    `httpOnly` cookie（`brabo_refresh`、`Path=/auth`、`SameSite=Strict`）中，

    从不出现在任何响应体中。`POST /auth/refresh` 和

    `POST /auth/logout` 读取该 cookie，并要求 `X-CSRF-Token` 头

    与 `brabo_csrf` cookie 一致。


    **轮换是强制的。** 每次 refresh 都会消耗所出示的令牌并

    签发另一个。重复出示已被消耗的令牌是被盗的特征，

    会撤销整个令牌族——包括合法的会话。一个

    同时发起两次并发 refresh 的客户端会把自己踢下线；请只保留一个进行中的

    调用。


    `/internal/*` 路由是 api ↔ engine 之间的接口，位于 **JWT 之外**：

    它们要求带有共享机密的 `X-Brabo-Service-Token` 头。

    用户令牌打不开其中任何一个。


    ## 错误


    NestJS 默认格式：`{ "statusCode": number, "message": string | string[], "error":
    string }`。


    | 代码 | 何时 |

    |---|---|

    | `400` | 请求体无效——`ValidationPipe` 以 `whitelist` 和 `forbidNonWhitelisted`
    运行，因此未知字段也会失败 |

    | `401` | 没有令牌、令牌已过期或凭据无效 |

    | `403` | 已认证但没有 RBAC 要求的角色，或服务令牌错误 |

    | `404` | 资源不存在**或**对调用方不可见 |

    | `409` | 状态冲突——无效的会话状态转换、资源已存在 |

    | `429` | 速率限制或渐进式锁定 |


    认证路由对不存在的邮箱、错误的密码和被锁定的账户

    **刻意给出无法区分的**响应：相同的响应体、相同的状态码、相同的

    耗时。这正是防止账户枚举的手段，而不是诊断上的

    缺陷。


    ## 速率限制


    基于 Postgres 的滑动窗口：每 60 秒每个用户 300 次请求、每个 IP 600 次请求，

    可配置。`@Public()` 路由和服务路由**豁免**——保护

    `/auth/*` 的是渐进式锁定，而不是速率限制。
  version: 0.1.0
  contact: {}
tags:
  - name: auth
    x-displayName: 认证
  - name: gates
    x-displayName: gate
  - name: git
    x-displayName: git
  - name: infrastructure
    x-displayName: 基础设施
  - name: internal
    x-displayName: 内部
  - name: llm
    x-displayName: LLM
  - name: projects
    x-displayName: 项目
  - name: actions
    x-displayName: 操作
  - name: execution
    x-displayName: 执行
  - name: anamnesis
    x-displayName: Anamnesis
  - name: backlog
    x-displayName: Backlog
  - name: psychologist
    x-displayName: Psychologist
  - name: rag
    x-displayName: RAG
  - name: sessions
    x-displayName: 会话
  - name: agents
    x-displayName: 代理
  - name: credentials
    x-displayName: 凭据
  - name: users
    x-displayName: 用户
  - name: workspaces
    x-displayName: workspace
servers: []
components:
  securitySchemes:
    bearer:
      scheme: bearer
      bearerFormat: JWT
      type: http
    service-token:
      type: apiKey
      in: header
      name: X-Brabo-Service-Token
  schemas:
    AcceptParallelizationDto:
      type: object
      properties:
        module:
          type: string
          example: api
          description: 模块名称，与 `module_map` 中的一致。会为其启动一个专用的 dev agent，位于隔离的 worktree 中。
      required:
        - module
    AceiteAutomaticoResponseDto:
      type: object
      properties:
        aceito:
          type: boolean
          example: true
        criterio:
          $ref: '#/components/schemas/CriterioDoAceiteResponseDto'
        motivo:
          type: string
          enum:
            - nao_e_po_para_arquiteto
            - oferta_nao_pendente
            - sem_regras_de_negocio
            - regras_sem_historia
            - repositorio_nao_local
            - credencial_de_git_no_projeto
            - autor_sem_papel
            - falhou
          example: regras_sem_historia
          description: >-
            为什么仍需要有人点击。`falhou`：系统尝试过但接受失败了——事件日志中有
            `handoff.auto_accept_failed`。
      required:
        - aceito
    AceiteResponseDto:
      type: object
      properties:
        message:
          type: string
          example: If the address is available, we've sent an email.
          description: 通用消息，对已知和未知地址完全相同——参见参考文档中关于枚举的说明。
      required:
        - message
    ActivateExecutionDto:
      type: object
      properties:
        taskBudgetMicros:
          type: number
          example: 500000
          description: 每个任务的预算，以微美元计。省略时使用领域默认值（DEFAULT_TASK_BUDGET_MICROS）。
        maxGateCorrections:
          type: number
          example: 3
          description: 在任务变为 `blocked` 之前，dev↔gate 往返的次数。正是它阻止代理在修正循环中耗尽预算。
        devAgentImpl:
          type: string
          enum:
            - real
            - noop
          example: real
          description: >-
            `noop` 在不使用 LLM 的情况下启动 dev agent，用于在不花费 token 的情况下端到端地演练执行基础设施。省略时使用
            `real`。
        terminalAllowPatterns:
          example:
            - Terminal(pnpm test:*)
            - Terminal(pnpm lint)
          description: 在 `permissions.json` 中放行的模式，供 dev agent 运行测试套件。`deny` 仍优先于此处列出的任何内容。
          type: array
          items:
            type: string
        originSessionId:
          type: string
          example: 3fa85f64-5717-4562-b3fc-2c963f66afa6
          description: >-
            “激活执行”点击所来自的聊天会话的 id——Dev Lead/PO
            的创作式/咨询式会话，绝不是执行会话。省略时（旧版调用方，例如在没有会话上下文的 Overview
            中激活）不关闭任何会话，与以前相同。提供时，会在最后关闭它——但仅当它没有待处理的
            handoff/操作/回合时（RN-135），并且如果它就是执行会话本身则绝不关闭。
    ActorDto:
      type: object
      properties:
        kind:
          type: string
          enum:
            - user
            - agent
            - system
          example: agent
          description: 执行操作者的性质。
        id:
          type: string
          example: dev-api
          description: '`kind=user` 时为用户 id；`kind=agent` 时为代理的 slug。'
      required:
        - kind
        - id
    ActorResponseDto:
      type: object
      properties:
        kind:
          type: string
          enum:
            - user
            - agent
            - system
          example: agent
          description: 执行操作者的性质。
        id:
          type: string
          example: dev-api
          description: '`kind=user` 时为用户 id；`kind=agent` 时为代理的 slug。'
      required:
        - kind
        - id
    AddMemberDto:
      type: object
      properties:
        userId:
          type: string
          format: uuid
          example: 3f1b2c8e-5a4d-4b7e-9c10-2d6f8a1b4c33
          description: 要关联的用户 id。
        role:
          type: string
          enum:
            - viewer
            - developer
            - maintainer
            - owner
          example: developer
          description: >-
            此关联中的角色。在项目上，它在两个方向上都覆盖 workspace
            角色——只要关联存在，有效角色就是这个，无论更高还是更低——并且有两种变动会以 403 拒绝：降级 workspace
            `owner`，以及修改你自己的角色（任一方向）。在 workspace 上它就是单纯的角色，只有上述第二条上限：你不能修改自己的角色。
      required:
        - userId
        - role
    AdoptRepositoryDto:
      type: object
      properties:
        externalId:
          type: string
          example: acme/checkout
          description: >-
            仓库在提供商上的标识符：github 上为 `owner/repo`，gitlab 上为
            `namespace/path`，本地为裸仓库的绝对路径。不会创建任何东西——仓库必须已经存在，且已登记的凭据必须能够访问它。
      required:
        - externalId
    AdoptRepositoryResponseDto:
      type: object
      properties:
        repository:
          $ref: '#/components/schemas/ProvisionedRepositoryResponseDto'
        plan:
          description: >-
            DRY-RUN：没有在仓库上执行任何操作。通过 `POST .../bootstrap/plan/approve` 或
            `.../plan/skip` 做决定。
          allOf:
            - $ref: '#/components/schemas/BootstrapPlanResponseDto'
        alreadyAdopted:
          type: boolean
          example: false
          description: 当项目已经接入过这个仓库时为 `true`——没有创建任何东西，只是根据当前状态重新生成了计划。
      required:
        - repository
        - plan
        - alreadyAdopted
    AdrRefResponseDto:
      type: object
      properties:
        actionId:
          type: string
          example: 01JC4Z8QK3M7YV2N5T9B0PXHRC
          description: 打开该 ADR 的 `open_adr_pr` `proposed_action` 的 id。
        title:
          type: string
          example: 0007 — Postgres queue instead of Redis
        status:
          type: string
          example: executed
        pullRequestUrl:
          type: object
          example: https://github.com/daneiel/brabo/pull/42
          nullable: true
          description: 在 PR 尚未真正打开期间为 `null`。
      required:
        - actionId
        - title
        - status
        - pullRequestUrl
    AgentAutonomyRuleResponseDto:
      type: object
      properties:
        agentId:
          type: string
          example: dev-api
          description: 代理的 slug。
        actionType:
          type: string
          example: terminal
          description: 操作类型，或 `"*"`——“自动模式”（RN-153）：对该代理任意操作类型的自主权。
        mode:
          type: string
          enum:
            - auto_approve
            - require_approval
            - deny
          example: auto_approve
          description: 授予的自主权。不会覆盖 `permissions.json`：那里的 `deny` 仍然优先。
      required:
        - agentId
        - actionType
        - mode
    AgenteAtivadoResponseDto:
      type: object
      properties:
        agent:
          type: string
          example: po
          description: 被激活代理的 slug。
        status:
          type: string
          example: active
          enum:
            - active
      required:
        - agent
        - status
    AgenteComVersoesResponseDto:
      type: object
      properties:
        agent:
          type: string
          example: dev-api
          description: 真实的 slug，包括按模块划分的 dev agent。该列表不从静态名单出发——如果那样，实际存在的代理就不会显示出来。
        versions:
          type: array
          items:
            $ref: '#/components/schemas/InstructionVersionResponseDto'
      required:
        - agent
        - versions
    AgentTokenUsageResponseDto:
      type: object
      properties:
        actorId:
          type: string
          example: dev-api
          description: 代理的 slug，或用户的 id。
        costMicros:
          type: number
          example: 3400000
          description: 成本，以微美元计。
        inputTokens:
          type: number
          example: 128000
        outputTokens:
          type: number
          example: 14200
      required:
        - actorId
        - costMicros
        - inputTokens
        - outputTokens
    AlvoJaAtendidoResponseDto:
      type: object
      properties:
        toAgent:
          type: string
          enum:
            - infra
          example: infra
        motivo:
          type: string
          enum:
            - oferta_pendente
            - agente_ativo
      required:
        - toAgent
        - motivo
    AlvoQaInternalDto:
      type: object
      properties:
        qa:
          type: boolean
          example: true
        taskId:
          type: string
          format: uuid
      required:
        - qa
        - taskId
    AnaliseDeTerminoResponseDto:
      type: object
      properties:
        causa:
          type: string
          example: heartbeat_timeout
          description: 失败的来源，绝不通过排除法推断（ADR 0020 的教训）。
        estadoDaSessao:
          type: string
          example: closed_abnormally
        analise:
          type: string
          example: The agent stopped responding after a 90s tool call.
      required:
        - causa
        - estadoDaSessao
        - analise
    AnamneseContextResponseDto:
      type: object
      properties:
        competencyCatalog:
          example:
            - TypeScript
            - Elixir
            - Kubernetes
          description: 封闭的目录。模型不能产出此范围之外的能力——校验会拒绝——因此它以列表形式进入提示词。
          type: array
          items:
            type: string
        members:
          description: 已排除选择不被画像的人。
          type: array
          items:
            $ref: '#/components/schemas/AnamneseMemberResponseDto'
        queuedHypotheses:
          description: Psychologist 已接受、等待成为优先输入的假设。
          type: array
          items:
            $ref: '#/components/schemas/AnamneseQueuedResponseDto'
        currentProfiles:
          type: array
          items:
            $ref: '#/components/schemas/AnamneseProfileResponseDto'
        instructions:
          type: array
          items:
            $ref: '#/components/schemas/AnamneseInstructionResponseDto'
        decisions:
          description: 窗口内用户的批准和拒绝。它们通过这里传递，因为它们不在事件日志中。
          type: array
          items:
            $ref: '#/components/schemas/AnamneseDecisionResponseDto'
        windowFrom:
          type: object
          example: '2026-07-27T12:00:00.000Z'
          nullable: true
          description: >-
            要分析的窗口起点（上一轮的终点）。第一轮时为 `null`。engine 直接从 Postgres 读取事件本身——通过 HTTP
            传输会更昂贵，却不会更正确。
      required:
        - competencyCatalog
        - members
        - queuedHypotheses
        - currentProfiles
        - instructions
        - decisions
        - windowFrom
    AnamneseDecisionResponseDto:
      type: object
      properties:
        actionType:
          type: string
          example: terminal
        status:
          type: string
          example: denied
        rejectionReason:
          type: object
          example: Command would delete the directory.
          nullable: true
        decidedBy:
          type: object
          example: 01JC4Z0000USUARIO0000000001
          nullable: true
        decidedAt:
          type: object
          example: '2026-07-27T13:00:00.000Z'
          nullable: true
      required:
        - actionType
        - status
        - rejectionReason
        - decidedBy
        - decidedAt
    AnamneseInstructionResponseDto:
      type: object
      properties:
        agent:
          type: string
          example: dev-api
        version:
          type: number
          example: 4
        content:
          type: string
          example: |-
            # dev-api

            …
      required:
        - agent
        - version
        - content
    AnamneseMemberResponseDto:
      type: object
      properties:
        userId:
          type: string
          example: 01JC4Z0000USUARIO0000000002
        name:
          type: object
          example: Senior Dev
          nullable: true
        email:
          type: string
          example: dev@brabo.dev
        role:
          type: string
          example: developer
      required:
        - userId
        - name
        - email
        - role
    AnamneseProfileResponseDto:
      type: object
      properties:
        userId:
          type: string
          example: 01JC4Z0000USUARIO0000000002
        competency:
          type: string
          example: TypeScript
        level:
          type: string
          example: advanced
        rationale:
          type: string
          example: Fixed three generic typing errors without help.
      required:
        - userId
        - competency
        - level
        - rationale
    AnamneseQueuedResponseDto:
      type: object
      properties:
        queueId:
          type: string
          example: 01JC4Z0000FILA00000000000001
        hypothesisId:
          type: string
          example: 01JC4Z0000HIPOTESE000000001
        agenteAlvo:
          type: string
          example: dev-api
        hipotese:
          type: string
          example: The instructions don't say when the task is done.
        sugestao:
          type: string
          example: Add an explicit definition-of-done criterion.
        confiancaPercent:
          type: number
          example: 72
      required:
        - queueId
        - hypothesisId
        - agenteAlvo
        - hipotese
        - sugestao
        - confiancaPercent
    AnswerDetectedLanguageDto:
      type: object
      properties:
        language:
          type: string
          example: es
          maxLength: 35
          description: >-
            问题所涉及的语言——即 `detectionQuestion`，来自 `GET
            .../sessions/:sessionId/response-language`。
        answer:
          type: string
          enum:
            - confirm
            - decline
          example: confirm
          description: >-
            `confirm` 将其存储为你检测并确认的语言（之后只要你的账户设置为自动，就会用它回答你）；当你的消息已不再指向它时返回 409
            `deteccao_mudou`。`decline` 存储拒绝，此后绝不再询问这种语言（之后确认它会清除拒绝）。
      required:
        - language
        - answer
    AnswerOpenQuestionDto:
      type: object
      properties:
        resposta:
          type: string
          example: 404 for a code that does not exist.
          description: '自由文本回答。发送这个或者 `aceitarSugestao: true`。'
        aceitarSugestao:
          type: boolean
          example: true
          description: 接受代理的建议作为回答。
    AnswerStructuredQuestionDto:
      type: object
      properties:
        answers:
          type: object
          additionalProperties:
            type: string
          example:
            name: Easy Checkout
            users: Small business owners
            platform: Web
          description: 以问题 `id` 为键的回答——这个 `id` 就是 `chat.structured_question` 中传来的那个。
      required:
        - answers
    AppDeExecucaoResponseDto:
      type: object
      properties:
        command:
          type: string
          example: npm start
        port:
          type: number
          example: 3000
      required:
        - command
        - port
    AppendSessionEventDto:
      type: object
      properties:
        type:
          type: string
          example: chat.message
          description: 事件类型。完整清单见 `docs/reference/events.md`。
        actor:
          $ref: '#/components/schemas/ActorDto'
        payload:
          type: object
          additionalProperties: true
          example:
            role: user
            content: I want a new checkout.
          description: 自由形态，因 `type` 而异。
      required:
        - type
        - actor
        - payload
    AppendSessionEventInternalDto:
      type: object
      properties:
        projectId:
          type: string
          format: uuid
          example: 01JC4Z0000PROJETO0000000001
        type:
          type: string
          example: agent.tool_call
          description: 见 `docs/reference/events.md`。
        actorKind:
          type: string
          enum:
            - user
            - agent
            - system
          example: agent
        actorId:
          type: string
          example: dev-api
          description: 发出它的代理的 slug。
        payload:
          type: object
          additionalProperties: true
          example:
            tool: ler_arquivo
      required:
        - projectId
        - type
        - actorKind
        - actorId
        - payload
    ApproveAlwaysResponseDto:
      type: object
      properties:
        id:
          type: string
          example: 01JC4Z8QK3M7YV2N5T9B0PXHRC
          description: 操作的 ULID。
        projectId:
          type: string
          example: 01JC4Z0000PROJETO0000000001
        sessionId:
          type: string
          example: 01JC4Z8QK3M7YV2N5T9B0PXHRA
        seq:
          type: number
          example: 7
          description: 操作在会话中的顺序；在列表中用作游标。
        actionType:
          type: string
          enum:
            - terminal
            - git_commit
            - git_push
            - pr_open
            - spend
            - git_repo_create
            - git_branch_create
            - git_branch_protect
            - write_file
            - open_adr_pr
            - git_merge
            - open_infra_pr
            - instruction_patch
            - parallelize
            - raise_max_parallel
            - propose_execution_plan
            - assess_implementability
            - container_start
            - container_stop
            - container_remove
            - container_recreate
            - container_start_via_runner
            - qa_environment_start
          example: terminal
          description: 该操作将要做什么。`git_push` 和 `git_merge` 需要 `maintainer`。
        payload:
          type: object
          additionalProperties: true
          example:
            command: pnpm test
          description: 操作的参数，因 `actionType` 而异。
        status:
          type: string
          enum:
            - pending
            - approved
            - denied
            - auto_approved
            - executed
            - failed
          example: pending
          description: 在流水线中的状态。`auto_approved` 刻意与 `approved` 区分开：日志需要区分人做出的决定和策略放行的内容。
        resolvedPolicy:
          type: string
          enum:
            - auto_approve
            - require_approval
            - deny
          example: require_approval
          description: '`permissions.json` 对此操作的决定。`deny` 始终优先于 `allow`——即使代理的自主权也无法覆盖它。'
        actor:
          description: 提议者。
          allOf:
            - $ref: '#/components/schemas/ActorResponseDto'
        decidedBy:
          type: object
          example: null
          nullable: true
          description: 批准或拒绝它的用户 id。绝不是代理。
        decidedAt:
          type: object
          example: null
          format: date-time
          nullable: true
        rejectionReason:
          type: object
          example: null
          nullable: true
        executionResult:
          type: object
          additionalProperties: true
          nullable: true
          example: null
          description: >-
            执行结果，形态因 `actionType` 而异——终端为输出和退出码，git 类型为 PR 编号和 URL。在操作尚未执行时为
            `null`。
        createdAt:
          type: string
          example: '2026-07-27T14:33:10.900Z'
          format: date-time
        updatedAt:
          type: string
          example: '2026-07-27T14:33:10.900Z'
          format: date-time
        desfecho:
          type: string
          enum:
            - aprovada
            - ja_aprovada
          example: aprovada
          description: >-
            `aprovada`：这次点击批准了该操作。`ja_aprovada`：该操作已因一次批准（之前的点击、另一个人或策略）离开了
            `pending`——幂等成功，不是错误。已拒绝的操作仍返回 409 `acao_ja_recusada`。
        padraoGravado:
          type: boolean
          example: true
          description: 这次点击是否记录了模式（及其 `permission.granted` 事件）。`false` 表示它已经存在；没有写入任何内容。
      required:
        - id
        - projectId
        - sessionId
        - seq
        - actionType
        - payload
        - status
        - resolvedPolicy
        - actor
        - decidedBy
        - decidedAt
        - rejectionReason
        - executionResult
        - createdAt
        - updatedAt
        - desfecho
        - padraoGravado
    ArchitecturePendencyResponseDto:
      type: object
      properties:
        storyId:
          type: string
          example: 01JC4Z0000HISTORIA000000001
        title:
          type: string
          example: Add item to cart
        status:
          type: string
          example: ready
        reason:
          type: string
          enum:
            - no_module
            - missing_module
          example: missing_module
          description: '`no_module`：story 没有指向任何模块。`missing_module`：它指向的模块在当前映射中不存在。'
        missing:
          example:
            - pagamentos
          description: 被引用但不存在的模块。`no_module` 时为空。
          type: array
          items:
            type: string
      required:
        - storyId
        - title
        - status
        - reason
        - missing
    ArchitectureResponseDto:
      type: object
      properties:
        moduleMap:
          nullable: true
          description: 在 Architect 写出第一份映射之前为 `null`。
          type: object
          allOf:
            - $ref: '#/components/schemas/ModuleMapResponseDto'
        adrs:
          type: array
          items:
            $ref: '#/components/schemas/AdrRefResponseDto'
        pendencies:
          description: story↔映射交叉校验。空列表是健康状态。
          type: array
          items:
            $ref: '#/components/schemas/ArchitecturePendencyResponseDto'
        c4Diagram:
          $ref: '#/components/schemas/EstadoDoC4DiagramaResponseDto'
      required:
        - moduleMap
        - adrs
        - pendencies
        - c4Diagram
    ArchiveStoryDto:
      type: object
      properties:
        reason:
          type: string
          example: Duplicate of "Register a user".
          description: 可选；记录在 `backlog.story_archived` 中。
    ArchiveStoryInternalDto:
      type: object
      properties:
        projectId:
          type: string
          format: uuid
          example: 01JC4Z0000PROJETO0000000001
        reason:
          type: string
          example: Duplicate of "Register a user".
      required:
        - projectId
    AreaDeAgentesResponseDto:
      type: object
      properties:
        id:
          type: string
          example: 01JC4Z8QK3M7YV2N5T9B0PXHRA
        projectId:
          type: string
          example: 01JC4Z8QK3M7YV2N5T9B0PXHRA
        key:
          type: string
          example: dev
          description: 区域标识符。在项目内唯一。
        leadAgentId:
          type: string
          example: dev-lead
          description: 区域的 lead——其唯一的对外联系人。
        maxParallel:
          type: number
          example: 2
          description: >-
            lead 在会话中无需请求授权即可拥有的代理数量。超过后会生成
            `proposed_action`。上限按会话计，而不是按模块计：按模块计数会允许 N 个模块 × N 个代理而完全不需要授权。
        budgetMicros:
          type: object
          example: 20000000
          nullable: true
          description: >-
            区域的花费上限，以微美元计（ADR 0110，RN-443）。`null`
            表示没有上限——这是与项目和会话预算并列的独立、叠加的检查，而不是级联：三者中谁先达到上限，谁就阻止调用。
        spentMicros:
          type: number
          example: 4300000
          description: 区域的累计花费，以微美元计。每当属于此区域的代理花费时都会递增，无论是否设置了上限。
        members:
          example:
            - dev-api
            - dev-web
          description: 成员。在 dev 区域中，它们来自 `module_map`，每个模块一个——正是这一点让区域不再是代码中的固定列表。
          type: array
          items:
            type: string
      required:
        - id
        - projectId
        - key
        - leadAgentId
        - maxParallel
        - budgetMicros
        - spentMicros
        - members
    ArquivosTocadosResponseDto:
      type: object
      properties:
        itens:
          description: 最新的在前，上限 100 条。
          type: array
          items:
            $ref: '#/components/schemas/ArquivoTocadoResponseDto'
        total:
          type: number
          example: 140
          description: 真实总数，即使 `itens` 被截断。
        prsNaoLidas:
          type: number
          example: 0
          description: 已读取会话中文件未被读取的 PR：超过了 20 个 PR 的上限，或提供商拒绝了 diff（RN-844）。
      required:
        - itens
        - total
        - prsNaoLidas
    ArquivoTocadoResponseDto:
      type: object
      properties:
        actionId:
          type: string
          example: 01JC4Z0000ACAO00000000000001
        path:
          type: string
          example: src/app.ts
        additions:
          type: number
          example: 12
        deletions:
          type: number
          example: 3
      required:
        - actionId
        - path
        - additions
        - deletions
    ArtefatosDoAgenteResponseDto:
      type: object
      properties:
        actorId:
          type: string
          example: po
        quantos:
          type: number
          example: 7
          description: 该代理在会话中创建的 PR（`pr_open`/`open_adr_pr`）以及 epic、story 和任务。
      required:
        - actorId
        - quantos
    AssignStoryModulesInternalDto:
      type: object
      properties:
        projectId:
          type: string
          format: uuid
          example: 01JC4Z0000PROJETO0000000001
        storyId:
          type: string
          format: uuid
          example: 01JC4Z0000HISTORIA000000001
        moduleIds:
          example:
            - api
            - web
          description: 来自当前 `module_map` 的名称。不存在的模块会变成一项架构待办。
          type: array
          items:
            type: string
      required:
        - projectId
        - storyId
        - moduleIds
    AttachLocalFolderRequestDto:
      type: object
      properties:
        folderName:
          type: string
          example: brabo
          description: 在浏览器中选择的文件夹名称——显示在覆盖率面板中。
        files:
          description: >-
            最多 500 个文件。精确的字节总和上限（RAG_LOCAL_TOTAL_BYTES_LIMIT）和逐文件的扩展名允许列表由用例而不是此
            DTO 强制执行——由各自有效的字符串组成的数组加起来仍可能是过大的载荷。
          type: array
          items:
            $ref: '#/components/schemas/LocalFolderFileDto'
      required:
        - folderName
        - files
    AttachLocalFolderResponseDto:
      type: object
      properties:
        folderName:
          type: string
        filesIndexed:
          type: number
        filesSkipped:
          type: number
          description: 因超过单文件字节上限或扩展名无法识别而被跳过——绝不会让整批失败。
        chunksCreated:
          type: number
        embedding:
          $ref: '#/components/schemas/IndexEmbeddingResponseDto'
      required:
        - folderName
        - filesIndexed
        - filesSkipped
        - chunksCreated
        - embedding
    BindingResolvidoDaChaveResponseDto:
      type: object
      properties:
        key:
          type: string
          example: dev-lead
          description: 代理 slug 或区域键，与请求中的完全一致。
        binding:
          nullable: true
          description: 与单键路由对此键的回答相同的值——当级联的任何层级都没有模型时为 `null`。
          type: object
          allOf:
            - $ref: '#/components/schemas/ResolvedBindingResponseDto'
      required:
        - key
        - binding
    BindingsResolvidosEmLoteResponseDto:
      type: object
      properties:
        agents:
          description: 每个请求的代理一条记录，按请求的顺序。
          type: array
          items:
            $ref: '#/components/schemas/BindingResolvidoDaChaveResponseDto'
        areas:
          description: 每个请求的区域一条记录，按请求的顺序。
          type: array
          items:
            $ref: '#/components/schemas/BindingResolvidoDaChaveResponseDto'
      required:
        - agents
        - areas
    BlockTaskInternalDto:
      type: object
      properties:
        projectId:
          type: string
          format: uuid
          example: 01JC4Z0000PROJETO0000000001
        agentId:
          type: string
          example: dev-api
        reason:
          type: string
          example: Gate correction cap reached.
        diagnosis:
          type: string
          example: Correction cycle exhausted without exit 0.
          description: 自由文本诊断——对所发生情况的人类可读细节，而不是失败的结构化来源。
        origin:
          type: string
          enum:
            - infra
            - modelo
            - codigo
            - politica
          example: infra
          description: >-
            失败的来源：`infra`、`modelo`、`codigo` 或 `politica`。绝不通过排除法——这是 ADR 0020
            的教训，由 QA Lead 在 ADR 0038 中提升了一个层次。可选：并非每个调用点都知道来源。
      required:
        - projectId
        - agentId
        - reason
        - diagnosis
    BootstrapDiagnosticResponseDto:
      type: object
      properties:
        kind:
          type: string
          enum:
            - missing_branch
            - unprotected_branch
            - missing_file
            - extra_branch
            - capability_unsupported
          example: extra_branch
          description: '`extra_branch` 仅供参考，绝不阻塞：接入的仓库有它自己的策略，引导不会删除它。'
        detail:
          type: object
          example:
            branchName: develop
            protected: false
          additionalProperties: true
      required:
        - kind
        - detail
    BootstrapPlanEstadoResponseDto:
      type: object
      properties:
        plan:
          nullable: true
          description: 项目没有接入的仓库时为 `null`。
          type: object
          allOf:
            - $ref: '#/components/schemas/BootstrapPlanResponseDto'
        decision:
          type: string
          enum:
            - approved
            - as_is
          example: null
          nullable: true
          description: '`null` = 计划已生成但尚未决定。这是什么都不会运行的状态（RN-045）。'
        decidedAt:
          type: object
          example: null
          nullable: true
          format: date-time
        decidedBy:
          type: object
          example: null
          nullable: true
      required:
        - plan
        - decision
        - decidedAt
        - decidedBy
    BootstrapPlanResponseDto:
      type: object
      properties:
        generatedAt:
          type: string
          example: '2026-08-01T23:45:00.000Z'
          format: date-time
        steps:
          description: 引导将会做的事。空列表 = 仓库已经符合模板的要求，没有需要批准的内容。
          type: array
          items:
            $ref: '#/components/schemas/BootstrapPlanStepResponseDto'
        diagnostics:
          description: 仓库与模板之间的差异。
          type: array
          items:
            $ref: '#/components/schemas/BootstrapDiagnosticResponseDto'
      required:
        - generatedAt
        - steps
        - diagnostics
    BootstrapPlanStepResponseDto:
      type: object
      properties:
        step:
          type: string
          enum:
            - commit_pr_template
            - commit_branching_policy
            - commit_gitignore
            - create_dev_branch
            - create_qa_branch
            - create_rc_branch
            - protect_branches
          example: create_qa_branch
        actionType:
          type: string
          example: git_branch_create
          description: 与 `proposed_actions` 相同的分类——引导运行时，每个已批准的步骤都会成为一条记录的操作。
        payload:
          type: object
          example:
            branchName: qa
            fromRef: dev
          description: 变更数据：哪个分支、哪个文件、来自哪里。
          additionalProperties: true
      required:
        - step
        - actionType
        - payload
    BudgetResponseDto:
      type: object
      properties:
        id:
          type: string
          example: 01JC4Z0000ORCAMENTO000000001
        projectId:
          type: object
          example: 01JC4Z0000PROJETO0000000001
          nullable: true
          description: 项目预算时填写；会话预算时为 `null`。
        sessionId:
          type: object
          example: null
          nullable: true
          description: 与上面相反。
        limitMicros:
          type: number
          example: 50000000
          description: 上限，以微美元计（50 美元）。
        spentMicros:
          type: number
          example: 12400000
          description: 累计花费，以微美元计。
        policy:
          type: string
          enum:
            - block
            - allow
          example: block
          description: '`block` 会在调用将超出上限时拒绝它；`allow` 会放行并只发出警告。'
        lastThresholdNotified:
          type: number
          example: 70
          description: 已经通知过的最高阈值（70/90/100）。用作下限的是它，而不是之前的花费——使用花费会让同一警告在越过阈值后的每次调用中再次触发。
        createdAt:
          type: string
          example: '2026-07-12T08:00:00.000Z'
          format: date-time
        updatedAt:
          type: string
          example: '2026-07-27T15:00:00.000Z'
          format: date-time
      required:
        - id
        - projectId
        - sessionId
        - limitMicros
        - spentMicros
        - policy
        - lastThresholdNotified
        - createdAt
        - updatedAt
    C4AtorInternalDto:
      type: object
      properties:
        name:
          type: string
          example: User
        type:
          type: string
          enum:
            - person
            - external_system
          default: person
          example: person
          description: '`person`（默认）表示操作系统的人；`external_system` 表示与之交互的另一个系统（例如 Git 提供商）。'
        description:
          type: string
          example: Whoever operates the product via the web.
      required:
        - name
    C4AtorResponseDto:
      type: object
      properties:
        name:
          type: string
          example: User
        type:
          type: string
          enum:
            - person
            - external_system
          example: person
        description:
          type: string
          example: Whoever operates the product through the web.
      required:
        - name
        - type
        - description
    C4DiagramaGeradoResponseDto:
      type: object
      properties:
        diagrama:
          $ref: '#/components/schemas/C4DiagramaResponseDto'
        version:
          type: number
          example: 1
      required:
        - diagrama
        - version
    C4DiagramaResponseDto:
      type: object
      properties:
        systemName:
          type: string
          example: Brabo
        systemDescription:
          type: string
          example: Agent-orchestrated engineering platform.
        actors:
          type: array
          items:
            $ref: '#/components/schemas/C4AtorResponseDto'
        contextDiagram:
          type: string
          example: |-
            C4Context
              title Context Diagram -- Brabo
              ...
          description: Mermaid `C4Context` 语法——系统和外部参与者。
        containerDiagram:
          type: string
          example: |-
            C4Container
              title Container Diagram -- Brabo
              ...
          description: Mermaid `C4Container` 语法——当前 module_map 的模块及其之间的依赖。
      required:
        - systemName
        - systemDescription
        - actors
        - contextDiagram
        - containerDiagram
    ChatSseEventResponseDto:
      type: object
      properties:
        type:
          type: string
          enum:
            - delta
            - done
            - error
            - metering_failed
          example: delta
          description: >-
            `delta` 携带增量 `text`；`done` 以 token/成本计费收尾；`error`
            中止；`metering_failed` 表示响应已发出但成本没有计入——该帧的存在是为了不让这种失败被忽视。
        text:
          type: string
          example: I will open the epic
          description: 仅在 `delta` 中。
        inputTokens:
          type: number
          example: 1820
          description: 仅在 `done` 中。
        outputTokens:
          type: number
          example: 340
          description: 仅在 `done` 中。
        costMicros:
          type: number
          example: 52700
          description: 仅在 `done` 中，以微美元计。
        estimated:
          type: boolean
          example: false
          description: 仅在 `done` 中。当提供商没有返回计数、成本是估算出来的时候为 `true`——这个数字的价值较低，界面需要能够说明这一点。
        message:
          type: string
          example: session budget exhausted
          description: 仅在 `error` 和 `metering_failed` 中。
      required:
        - type
    CicloDeVidaDoContainerResponseDto:
      type: object
      properties:
        status:
          type: string
          enum:
            - provisioning
            - running
            - stopped
            - failed
            - removed
          example: provisioning
          description: 登记的内容，而不是 Docker 守护进程确认的内容——目前没有真正的编排器转换这张表（RN-243）。
        imageVersion:
          type: number
          example: 1
          description: 在第一次状态转换时冻结的 `artifact.project_image` 版本（RN-245）——不会在每次读取时重新应用。
        resources:
          description: 置备时声明的上限——不强制执行（RN-248）。
          allOf:
            - $ref: '#/components/schemas/RecursosDoContainerResponseDto'
        failureReason:
          type: object
          nullable: true
          example: null
          description: 仅在转换为 `failed` 时填写。
        createdAt:
          type: string
          format: date-time
        statusChangedAt:
          type: string
          format: date-time
        observado:
          nullable: true
          description: >-
            Docker 守护进程此刻报告的内容，经由 broker 查询（ADR
            0130）——绝不与上面那些登记的字段合并。从外部被杀掉的容器会显示为登记 `running`、观测
            `exited`，这正是要点所在。`null` 表示“没有容器”或“我无法查看”——读取 `naoObservado` 来区分两者。
          type: object
          allOf:
            - $ref: '#/components/schemas/ObservacaoDeContainerResponseDto'
        naoObservado:
          type: string
          enum:
            - broker-nao-configurado
            - broker-sem-resposta
            - broker-recusou
            - null
          nullable: true
          example: broker-nao-configurado
          description: >-
            当观测确实发生时为
            `null`——包括返回为空的情况，那是“我看过了，没有容器”的肯定陈述。无法查看时填写，并说明原因。在这里继承登记状态正是
            RN-468 所禁止的。
        detalheDaObservacao:
          type: object
          nullable: true
          example: null
          description: broker 自己的消息（如有）。
      required:
        - status
        - imageVersion
        - resources
        - failureReason
        - createdAt
        - statusChangedAt
        - observado
        - naoObservado
        - detalheDaObservacao
    ClaimTaskInternalDto:
      type: object
      properties:
        projectId:
          type: string
          format: uuid
          example: 01JC4Z0000PROJETO0000000001
        module:
          type: string
          example: api
          description: 只有此模块的任务参与抽取。
        agentId:
          type: string
          example: dev-api
      required:
        - projectId
        - module
        - agentId
    CloseAnamneseWindowInternalDto:
      type: object
      properties:
        projectId:
          type: string
          format: uuid
          example: 01JC4Z0000PROJETO0000000001
        windowFrom:
          type: string
          example: '2026-07-27T12:00:00.000Z'
          format: date-time
        windowTo:
          type: string
          example: '2026-07-27T12:15:00.000Z'
          format: date-time
        eventCount:
          type: number
          example: 12
      required:
        - projectId
        - windowFrom
        - windowTo
        - eventCount
    CodeBlameLineResponseDto:
      type: object
      properties:
        line:
          type: number
          example: 12
          description: 从 1 开始，与所有编辑器的显示一致。
        commitSha:
          type: string
          example: 0f3e8181fbd010b10c78db17b90ecb35fb8cc89c
        author:
          type: string
          example: Daniel Souza
        authorDate:
          type: string
          example: '2026-08-04T12:00:00.000Z'
        summary:
          type: string
          example: 'fix(api): fix the credential resolver'
      required:
        - line
        - commitSha
        - author
        - authorDate
        - summary
    CodeBlameResponseDto:
      type: object
      properties:
        ref:
          type: string
          example: dev
        path:
          type: string
          example: apps/api/src/main.ts
        lines:
          type: array
          items:
            $ref: '#/components/schemas/CodeBlameLineResponseDto'
        truncated:
          type: boolean
          example: false
          description: 文件超过了单次调用的标注行数上限。
      required:
        - ref
        - path
        - lines
        - truncated
    CodeBranchDetailListResponseDto:
      type: object
      properties:
        items:
          type: array
          items:
            $ref: '#/components/schemas/CodeBranchDetailResponseDto'
        truncated:
          type: boolean
          example: false
          description: 列表在丰富分支数上限处被截断。
      required:
        - items
        - truncated
    CodeBranchDetailResponseDto:
      type: object
      properties:
        name:
          type: string
          example: feature/x
        commitSha:
          type: string
          example: 0f3e8181fbd010b10c78db17b90ecb35fb8cc89c
        protected:
          type: boolean
          example: false
        ahead:
          type: number
          nullable: true
          example: 3
          description: 领先默认分支的提交数。无法计算时为 `null`。
        behind:
          type: number
          nullable: true
          example: 0
          description: 落后默认分支的提交数。
        pullRequest:
          nullable: true
          type: object
          allOf:
            - $ref: '#/components/schemas/CodeBranchPullRequestRefResponseDto'
        producedBy:
          nullable: true
          description: >-
            拥有该分支的 dev agent/模块（RN-152），当名称匹配 `feature/task-XXXXXXXX`
            且任务/模块仍可解析时。为 `null` 的情况：用户的手动分支，或 `main`/`dev`/`qa`。
          type: object
          allOf:
            - $ref: '#/components/schemas/CodeBranchProducedByResponseDto'
      required:
        - name
        - commitSha
        - protected
        - ahead
        - behind
        - pullRequest
        - producedBy
    CodeBranchProducedByResponseDto:
      type: object
      properties:
        agentId:
          type: string
          example: dev-pieces
          description: 创建该分支的 dev 的 agent_id（`dev-<module>`/`dev-<module>-2`，RN-087）。
        moduleId:
          type: string
          example: pieces
          description: 模块名称，来自项目当前的 `module_map`。
      required:
        - agentId
        - moduleId
    CodeBranchPullRequestRefResponseDto:
      type: object
      properties:
        number:
          type: number
          example: 42
        state:
          type: string
          enum:
            - open
            - merged
            - closed
          example: open
      required:
        - number
        - state
    CodeDiffFileResponseDto:
      type: object
      properties:
        path:
          type: string
          example: apps/api/src/main.ts
          description: 变更之后的路径（对 `removed` 而言，是消失的那个）。
        previousPath:
          type: string
          nullable: true
          example: null
          description: 之前的路径；仅当 `status` 为 `renamed` 时填写。
        status:
          type: string
          enum:
            - added
            - modified
            - removed
            - renamed
          example: modified
        additions:
          type: number
          example: 12
        deletions:
          type: number
          example: 3
        patch:
          type: string
          nullable: true
          example: |
            @@ -1,3 +1,4 @@
            +import x
          description: >-
            文件的统一 diff。提供商没有交付时（二进制或补丁过大）为 `null`——与 `""`
            不同，后者是“返回为空”。没有这一区分，界面会对一个已更改的二进制文件说“没有变化”。
      required:
        - path
        - previousPath
        - status
        - additions
        - deletions
        - patch
    CodeDiffResponseDto:
      type: object
      properties:
        pullRequestId:
          type: string
          example: pr-1
        files:
          type: array
          items:
            $ref: '#/components/schemas/CodeDiffFileResponseDto'
        truncated:
          type: boolean
          example: false
          description: 列表在单个 diff 的文件数上限处被截断。
      required:
        - pullRequestId
        - files
        - truncated
    CodeFileResponseDto:
      type: object
      properties:
        ref:
          type: string
          example: dev
        path:
          type: string
          example: apps/api/src/main.ts
        content:
          type: string
          example: |
            import { NestFactory } from '@nestjs/core';
          description: UTF-8 内容。此路由不提供二进制文件。
        truncated:
          type: boolean
          example: false
          description: 文件超过了字节上限，`content` 是其开头部分。该标签页是只读的：截断并提示比拒绝该文件更好。
        bytes:
          type: number
          example: 1284
          description: 返回的字节数——截断之后的，而不是之前的。
      required:
        - ref
        - path
        - content
        - truncated
        - bytes
    CodePullRequestListResponseDto:
      type: object
      properties:
        items:
          type: array
          items:
            $ref: '#/components/schemas/CodePullRequestSummaryResponseDto'
        truncated:
          type: boolean
          example: false
          description: 列表在单次调用的 PR 数上限处被截断。
      required:
        - items
        - truncated
    CodePullRequestSummaryResponseDto:
      type: object
      properties:
        id:
          type: string
          example: '2401938475'
        number:
          type: number
          example: 42
        title:
          type: string
          example: 'fix(api): the agents area is born with the project'
        url:
          type: string
          example: https://github.com/acme/repo/pull/42
        author:
          type: string
          nullable: true
          example: daneiel
          description: 打开者的登录名/用户名。提供商不报告时为 `null`。
        state:
          type: string
          enum:
            - open
            - merged
            - closed
          example: open
        sourceBranch:
          type: string
          example: feature/x
        targetBranch:
          type: string
          example: dev
        updatedAt:
          type: string
          nullable: true
          example: '2026-08-04T12:00:00.000Z'
          description: 提供商不报告时为 `null`。
      required:
        - id
        - number
        - title
        - url
        - author
        - state
        - sourceBranch
        - targetBranch
        - updatedAt
    CodeSearchMatchResponseDto:
      type: object
      properties:
        path:
          type: string
          example: apps/api/src/main.ts
        line:
          type: number
          example: 12
          description: 从 1 开始，与所有编辑器的显示一致。
        text:
          type: string
          example: '  const app = await NestFactory.create(AppModule);'
          description: 整行内容，经过裁剪，确保压缩后的文件绝不会被完整返回。
      required:
        - path
        - line
        - text
    CodeSearchResponseDto:
      type: object
      properties:
        ref:
          type: string
          example: dev
        path:
          type: string
          example: ''
          description: 搜索所遍历的子树。
        query:
          type: string
          example: NestFactory
        matches:
          type: array
          items:
            $ref: '#/components/schemas/CodeSearchMatchResponseDto'
        filesScanned:
          type: number
          example: 87
          description: 实际打开的文件数。这是搜索的真实成本，放在响应中是为了不让它隐而不见。
        truncated:
          type: boolean
          example: true
          description: 扫描因预算（目录、文件或匹配）而在遍历完整棵树之前停止。请缩小 `path` 或搜索词的范围。
      required:
        - ref
        - path
        - query
        - matches
        - filesScanned
        - truncated
    CodeTreeEntryResponseDto:
      type: object
      properties:
        path:
          type: string
          example: apps/api/src/main.ts
          description: 从仓库根目录开始的完整路径。
        name:
          type: string
          example: main.ts
          description: '`path` 的最后一段。'
        type:
          type: string
          enum:
            - file
            - dir
          example: file
        size:
          type: number
          nullable: true
          example: 1284
          description: 字节数。目录以及提供商不报告时为 `null`——与 `0` 区分很重要，这样界面绝不会声称“空文件”。
      required:
        - path
        - name
        - type
        - size
    CodeTreeResponseDto:
      type: object
      properties:
        ref:
          type: string
          example: dev
          description: 读取的分支、tag 或 sha。
        path:
          type: string
          example: apps/api
          description: 列出的目录；`""` 即根目录。
        entries:
          type: array
          items:
            $ref: '#/components/schemas/CodeTreeEntryResponseDto'
        truncated:
          type: boolean
          example: false
          description: 列表在每层条目数上限处被截断。目录中的内容比这里返回的更多。
      required:
        - ref
        - path
        - entries
        - truncated
    CompleteStoryInternalDto:
      type: object
      properties:
        projectId:
          type: string
          format: uuid
          example: 01JC4Z0000PROJETO0000000001
        description:
          type: string
          example: As a buyer, I want to pay by card.
        rf:
          example:
            - The cart accepts up to 50 items
          type: array
          items:
            type: string
        rnf:
          example:
            - Response under 200ms at p95
          type: array
          items:
            type: string
        dod:
          example:
            - Unit tests green
          type: array
          items:
            type: string
        dor:
          example:
            - Module defined
          type: array
          items:
            type: string
        businessRuleIds:
          example:
            - RN-014
          description: >-
            追加到 story 已引用的规则中（补全绝不会解除规则的关联）；每个新 id 都必须引用一个已存在的
            `artifact.business_rule`。
          type: array
          items:
            type: string
        questionId:
          type: string
          format: uuid
          description: RN-859（ADR 0214）：一个用户已经回答过的未决问题——只有这样，`ready` story（没有执行中的任务）才能被修正。
      required:
        - projectId
    ConfirmacaoDeArquiteturaResponseDto:
      type: object
      properties:
        ok:
          type: boolean
          example: true
          enum:
            - true
        desfecho:
          type: string
          enum:
            - confirmado
            - ja_oferecido
          description: >-
            `confirmado`：Infra
            已被触发。`ja_oferecido`：它已有待处理的提议或已在项目中活跃——没有记录任何内容，也没有向 engine
            请求任何东西（双击、第二个标签页）。这里不再以 Dev Lead 为目标：一旦容器处于 `running`，由 Infra
            向其提议（RN-672）。
        jaAtendidos:
          type: array
          items:
            $ref: '#/components/schemas/AlvoJaAtendidoResponseDto'
      required:
        - ok
        - desfecho
        - jaAtendidos
    ConfirmProjectWorkspaceInternalDto:
      type: object
      properties:
        path:
          type: string
          example: /home/you/projects/store
          description: runner 在宿主机上确认的绝对路径——事实来源（RN-423）。写入之前在此进行词法重新校验；无效时返回 400，绝不写入。
        sessionId:
          type: string
          format: uuid
          example: 01JC4Z0000SESSAO00000000001
          description: >-
            项目最近的会话（`ProjectSession.latest_id/1`），在 engine 端解析。项目还没有任何会话时为
            `null`/省略——项目仍会被更新，只是跳过审计事件（已接受的缺口，RN-423）。
        actorId:
          type: string
          format: uuid
          example: 01JC4Z0000USUARIO0000000001
          description: >-
            请求 runner 票据的人（确认路径的套接字的所有者）——成为 `project.workspace_verified`
            事件的参与者。省略时：该事件（如果有会话）诞生时没有可识别的参与者。
      required:
        - path
    ConfirmProjectWorkspaceResponseDto:
      type: object
      properties:
        verified:
          type: boolean
          example: true
          description: 只要调用没有抛出异常就始终为 `true`——确认是幂等的。`changed` 说明这次调用是否改变了任何东西。
        workspacePath:
          type: string
          example: /home/you/projects/store
          description: 写入的（规范化后的）路径——可能与创建时的不同，因为 runner 是事实来源并会覆盖它。
        changed:
          type: boolean
          example: true
          description: 第一次确认时，或报告的路径与已写入的不同时为 `true`。重新连接时报告的路径与以往相同则为 `false`——没有重写任何内容。
      required:
        - verified
        - workspacePath
        - changed
    ContainerExecInternalDto:
      type: object
      properties:
        comando:
          type: string
          example: npm test
          description: shell 命令，由 `sh -c` 在容器内解释——与在容器外运行的终端操作约定相同。
        cwd:
          type: string
          example: /work/.worktrees/dev-api
          description: >-
            容器内的工作目录。已由 engine 从宿主机路径转换（RN-492）——此 DTO 不会重复该转换。省略时在
            `/work`（项目根目录）运行。broker 会重新校验它位于 `/work`
            内，否则拒绝（`DiretorioForaDoEscopoError`）。
        timeoutMs:
          type: number
          example: 300000
          description: 超时时间，以毫秒计。省略时使用 broker 的默认值。
        alvo:
          description: >-
            AT-612: run in the task QA instance instead of the work container.
            Set by the engine from the gate context, never by the model.
          allOf:
            - $ref: '#/components/schemas/AlvoQaInternalDto'
      required:
        - comando
    ContainerExecInternalResponseDto:
      type: object
      properties:
        sucesso:
          type: boolean
          example: true
          description: 当 broker 拒绝或没有响应时为 `false`——其他字段缺失，由 `motivo` 解释原因。
        exitCode:
          type: number
          example: 0
          description: '仅当 `sucesso: true` 时存在。'
        output:
          type: string
          example: |
            ok
          description: '仅当 `sucesso: true` 时存在。'
        timedOut:
          type: boolean
          example: false
          description: '仅当 `sucesso: true` 时存在。'
        motivo:
          type: string
          example: 'o broker de container não respondeu em http://broker:8090: timeout'
          description: '仅当 `sucesso: false` 时存在。'
      required:
        - sucesso
    ContainerOverviewItemResponseDto:
      type: object
      properties:
        projectId:
          type: string
          example: 01JC4Z0000PROJETO000001
        projectName:
          type: string
          example: exp002
        projectSlug:
          type: string
          example: exp002
        executionMode:
          type: string
          enum:
            - container
            - mounted
            - runner
          example: runner
          description: >-
            此项目的容器在哪里启动（RN-497/503）。`container` 和 `mounted` 通过服务器上的
            broker；`runner` 通过用户机器上的本地代理。正是它让启动操作在 `container_start` 和
            `container_start_via_runner` 之间分支。
        registrado:
          nullable: true
          description: >-
            `null` 表示此项目从未置备过容器——这是第三种状态，而不是 `status` 的一个值：它既不是
            `stopped`（曾经存在后来停止的容器），也不是“无法观测”（那是关于守护进程的）。请同时读取 `naoVerificado:
            "sem_container_registrado"`。
          type: object
          allOf:
            - $ref: '#/components/schemas/RegistroDeContainerResponseDto'
        temImagemDecidida:
          type: boolean
          example: true
          description: >-
            此项目是否存在任何 `artifact.project_image`——RN-105 gate，自 RN-494/ADR 0135
            起适用于全部三种模式。`false` 表示此刻无法启动容器，页面会说明这一点，而不是提议一个注定失败的操作。
        workspaceVerifiedAt:
          type: object
          nullable: true
          format: date-time
          description: >-
            runner 确认文件夹的时间（RN-423）——仅对 `executionMode: "runner"`
            有意义。它记录的是一次确认，绝不是心跳（RN-468）：非空并不能证明本地代理此刻已连接。它能证明的是否定的一面——值为 `null`
            时（在 `runner` 项目上）表示从未有 runner 连接过。
        observado:
          nullable: true
          description: >-
            broker 此刻报告的内容——为 `null`
            可能是因为没有容器、因为无法询问（`naoObservado`），或因为本次加载没有询问这一行（`naoVerificado`）。
          type: object
          allOf:
            - $ref: '#/components/schemas/ObservacaoDeContainerResponseDto'
        naoObservado:
          type: string
          enum:
            - broker-nao-configurado
            - broker-sem-resposta
            - broker-recusou
            - null
          nullable: true
        detalheDaObservacao:
          type: object
          nullable: true
          example: null
        naoVerificado:
          type: string
          enum:
            - fora_do_escopo_da_verificacao
            - teto_de_verificacoes_atingido
            - sem_container_registrado
            - null
          nullable: true
          example: null
          description: >-
            当本次加载没有向 broker 询问这一行时非空——绝不与 `naoObservado`
            混淆，后者表示询问过但失败了。`fora_do_escopo_da_verificacao`：状态为
            `stopped`/`failed`/`removed`，守护进程的确认无关紧要。`teto_de_verificacoes_atingido`：符合条件，但本次加载的
            broker
            调用预算已被其他行用完。`sem_container_registrado`：项目从未置备过容器，因此没有可观测的东西——它也从不消耗预算中的调用。
        acaoPendente:
          nullable: true
          description: >-
            此项目待处理的
            `container_start`/`container_stop`/`container_remove`/`container_start_via_runner`
            操作（如有）——在它的任何会话中。页面为其渲染内联的 `ApprovalCard`，而不是操作按钮，与 PR 标签页的模式相同。
          type: object
          allOf:
            - $ref: '#/components/schemas/ProposedActionResponseDto'
        decisaoVigente:
          type: object
          nullable: true
          example:
            version: 3
            image: node:22
            network: egress
          description: The current (highest version) image decision, or null.
        imagemDesatualizada:
          type: boolean
          example: false
          description: >-
            The registered container (running/stopped/provisioning) differs in
            CONTENT (image, network, resources) from the current decision; only
            recreating it (stop, remove, start) applies the new one.
        brokerConfigurado:
          type: boolean
          example: false
          description: >-
            此安装是否配置了容器 broker（已设置 `BROKER_URL`，ADR
            0130）——每一行的值都相同，因为它是安装配置而不是项目的属性。`container` 和 `mounted` 项目只通过
            broker 启动容器（ADR 0144），因此为 `false` 时，页面会在点击之前就为它们拒绝启动按钮并说明原因（ADR
            0161，RN-574）；`runner` 项目不受影响。它说明的是变量存在，而绝不是 broker 有响应——那是
            `naoObservado` 报告的内容。
      required:
        - projectId
        - projectName
        - projectSlug
        - executionMode
        - registrado
        - temImagemDecidida
        - workspaceVerifiedAt
        - observado
        - naoObservado
        - detalheDaObservacao
        - naoVerificado
        - acaoPendente
        - decisaoVigente
        - imagemDesatualizada
        - brokerConfigurado
    ContainerSpecInternalResponseDto:
      type: object
      properties:
        projectId:
          type: string
          example: f52be111-0000-4000-8000-000000000000
        projectSlug:
          type: string
          example: exp002
        workspaceId:
          type: string
          example: aaaaaaaa-0000-4000-8000-000000000000
        workspaceDirName:
          type: string
          example: exp002-f52be111
          description: >-
            在项目创建时冻结的文件夹名称（RN-109）。在全部三种模式中，它都是容器名称（`brabo-<workspaceDirName>`）的唯一来源。文件夹在哪里是另一个问题，由
            `localizacao`（RN-503）回答——api 仍然从不发送绝对路径，因为来自 api 容器内部的路径不是 Docker
            守护进程能够解析的路径。
        executionMode:
          type: string
          enum:
            - container
            - mounted
            - runner
          example: container
          description: >-
            代码所在的位置。broker 服务于 `container` 和 `mounted`（RN-503）：自 ADR 0141
            起，挂载的文件夹位于此服务器按身份挂载的一个基目录下，因此守护进程可以访问它。它仍然拒绝
            `runner`：那个文件夹在用户的机器上，这里没有任何根目录能看到它——在那里，启动容器的是 runner（ADR 0137）。
        localizacao:
          description: 项目文件夹的可辨识定位符（RN-503）：由 broker 两个根目录中的哪一个解析它，以及要拼接到该根目录的相对段。
          allOf:
            - $ref: '#/components/schemas/LocalizacaoDoProjetoResponseDto'
        usuarioDaPasta:
          type: object
          nullable: true
          properties:
            uid:
              type: integer
              example: 1000
            gid:
              type: integer
              example: 1000
          description: >-
            项目文件夹的所有者，由 api 测量（ADR 0180）：broker 以 `--user uid:gid` 启动容器，使 dev
            agent 在 `--cap-drop ALL` 下仍能写入该文件夹。为 `null` 的情况：文件夹无法测量、项目为
            `runner`，或所有者是 root——此时容器像以前一样启动。绝不接受来自对 broker 请求中的值。
        imagem:
          nullable: true
          description: >-
            在 Architect 尚未做出决定时为 `null`（RN-105）——此时 `start` 在 broker 端会以 409
            拒绝，其他四个操作仍然可用。
          type: object
          allOf:
            - $ref: '#/components/schemas/ImagemParaOBrokerResponseDto'
        imagemVersao:
          type: number
          example: 3
          description: 当前制品的版本；没有决定时为 0。它最终作为 `brabo.image.version` 标签出现在容器上。
        comandoDoApp:
          type: string
          nullable: true
          description: >-
            AT-610 (ADR 0228): the app command declared by the Infra in
            `run.app` (`artifact.run_instructions`), or `null`. Always `null`
            for `runner`.
        ports:
          type: array
          nullable: true
          items:
            type: object
            properties:
              host:
                type: integer
                example: 8080
              container:
                type: integer
                example: 3000
              protocol:
                type: string
                example: tcp
          description: >-
            AT-608: ports declared in `artifact.container_network`, published on
            127.0.0.1 only. `null` when undeclared or `runner`.
        dns:
          nullable: true
          description: 'AT-608: DNS servers (literal IPs), or `null`.'
          type: array
          items:
            type: string
        extraHosts:
          type: array
          nullable: true
          items:
            type: object
            properties:
              name:
                type: string
                example: api.local
              ip:
                type: string
                example: 10.0.0.5
          description: 'AT-608: `--add-host` entries (IPv4), or `null`.'
        qa:
          type: object
          properties:
            chave:
              type: string
              example: a1b2c3d4
            services:
              type: array
              items:
                type: object
          description: >-
            AT-612 (ADR 0229): ONLY with `papel=qa`. `chave` = 8 first [a-z0-9]
            of the task id; `services` = `run.qa.services` declared by the Infra
            (image with digest, env, tmpfs, user `<uid>:<gid>` never 0, command
            argv after the image, shared).
      required:
        - projectId
        - projectSlug
        - workspaceId
        - workspaceDirName
        - executionMode
        - localizacao
        - usuarioDaPasta
        - imagem
        - imagemVersao
        - comandoDoApp
        - ports
        - dns
        - extraHosts
    ContratoDeModuloInternalDto:
      type: object
      properties:
        modulo:
          type: string
          example: board-engine
          description: 模块名称——必须存在于当前的 module_map 中。
        expoe:
          description: 该模块向依赖它的一方暴露的内容。1 到 40 项。
          type: array
          items:
            $ref: '#/components/schemas/ItemDeContratoInternalDto'
      required:
        - modulo
        - expoe
    ContratoDeModuloResponseDto:
      type: object
      properties:
        modulo:
          type: string
          example: board-engine
        expoe:
          type: array
          items:
            $ref: '#/components/schemas/ItemDeContratoResponseDto'
      required:
        - modulo
        - expoe
    ContratoLidoDeModuloResponseDto:
      type: object
      properties:
        modulo:
          type: string
          example: game-session
        dependeDe:
          example:
            - board-engine
            - scoring
          description: 当前 module_map 的 `dependsOn`：此模块消费的内容。
          type: array
          items:
            type: string
        expoe:
          nullable: true
          description: 它暴露的内容。`null` = Architect 没有为此模块声明契约（不同于什么都不暴露）。
          type: array
          items:
            $ref: '#/components/schemas/ItemDeContratoResponseDto'
      required:
        - modulo
        - dependeDe
        - expoe
    ContratosDeclaradosResponseDto:
      type: object
      properties:
        contratos:
          type: array
          items:
            $ref: '#/components/schemas/ContratoDeModuloResponseDto'
        version:
          type: number
          example: 1
      required:
        - contratos
        - version
    ContratosDoProjetoResponseDto:
      type: object
      properties:
        status:
          type: string
          enum:
            - sem_contratos
            - declarados
        version:
          type: number
          example: 2
          description: 没有契约时为 0。
        modulos:
          description: 当前 module_map 的每个模块一条，按映射顺序。
          type: array
          items:
            $ref: '#/components/schemas/ContratoLidoDeModuloResponseDto'
        contratosForaDoMapa:
          example: []
          description: 当前 module_map 已不再包含的模块的契约——会被说明，绝不会挂到一个不存在的模块上。
          type: array
          items:
            type: string
      required:
        - status
        - version
        - modulos
        - contratosForaDoMapa
    ConvertExecutionModeDto:
      type: object
      properties:
        executionMode:
          type: string
          enum:
            - container
            - mounted
            - runner
          example: runner
          description: 新的执行模式。可以与项目当前的模式相同——这种情况下，真正改变的只有 `workspacePath`。
        workspacePath:
          type: string
          example: /home/you/projects/store
          description: >-
            用户文件夹的绝对路径——当 `executionMode` 为 `mounted` 或 `runner` 时必需，为
            `container` 时拒绝。与创建时的校验相同（RN-170/RN-422/RN-423）：在 `mounted`
            中，它必须存在且可从容器内写入；在 `runner` 中，现在只校验格式——真正的确认来自 runner 重新连接。
      required:
        - executionMode
    CoverageReportResponseDto:
      type: object
      properties:
        rules:
          type: array
          items:
            $ref: '#/components/schemas/RuleCoverageResponseDto'
        uncoveredCount:
          type: number
          example: 2
          description: 有多少条规则完全没有 story。每一条都是一项发现。
      required:
        - rules
        - uncoveredCount
    CreateActionInternalDto:
      type: object
      properties:
        projectId:
          type: string
          format: uuid
          example: 01JC4Z0000PROJETO0000000001
        actionType:
          type: string
          enum:
            - terminal
            - git_commit
            - git_push
            - pr_open
            - spend
            - git_repo_create
            - git_branch_create
            - git_branch_protect
            - write_file
            - open_adr_pr
            - git_merge
            - open_infra_pr
            - instruction_patch
            - parallelize
            - raise_max_parallel
            - propose_execution_plan
            - assess_implementability
            - container_start
            - container_stop
            - container_remove
            - container_recreate
            - container_start_via_runner
            - qa_environment_start
          example: terminal
        actor:
          description: 在此路由上始终是代理。
          allOf:
            - $ref: '#/components/schemas/ActorDto'
        payload:
          type: object
          additionalProperties: true
          example:
            command: pnpm test
      required:
        - projectId
        - actionType
        - actor
        - payload
    CreateC4DiagramInternalDto:
      type: object
      properties:
        projectId:
          type: string
          format: uuid
        systemName:
          type: string
          example: Brabo
          description: 系统/项目名称，用作图的标签。
        systemDescription:
          type: string
          example: Agent-orchestrated engineering platform.
        actors:
          description: >-
            Context 层级的外部参与者（Simon Brown）。Container 层级的容器不放在这里：它们来自项目当前的
            module_map。
          type: array
          items:
            $ref: '#/components/schemas/C4AtorInternalDto'
      required:
        - projectId
        - systemName
    CreatedStoryResponseDto:
      type: object
      properties:
        id:
          type: string
          example: 01JC4Z0000HISTORIA000000001
        epicId:
          type: string
          example: 01JC4Z0000EPICO000000000001
        projectId:
          type: string
          example: 01JC4Z0000PROJETO0000000001
        sessionId:
          type: string
          example: 01JC4Z8QK3M7YV2N5T9B0PXHRA
        title:
          type: string
          example: Add item to cart
        description:
          type: string
          example: As a buyer, I want to gather items before paying.
        rf:
          example:
            - The cart accepts up to 50 items
          description: 功能需求。
          type: array
          items:
            type: string
        rnf:
          example:
            - The response stays under 200 ms at p95
          description: 非功能需求。
          type: array
          items:
            type: string
        businessRuleIds:
          example:
            - RN-014
          description: 所覆盖的业务规则。规则→story 的覆盖情况就是根据这里在 `GET /projects/:id/coverage` 中计算的。
          type: array
          items:
            type: string
        dod:
          example:
            - Unit tests green
          description: 完成的定义（DoD）。
          type: array
          items:
            type: string
        dor:
          example:
            - Module defined
          description: 就绪的定义（DoR）。
          type: array
          items:
            type: string
        moduleIds:
          example:
            - api
          description: story 涉及的来自 `module_map` 的模块。没有模块或模块不存在的 story 会变成一项架构待办。
          type: array
          items:
            type: string
        status:
          type: string
          enum:
            - draft
            - ready
            - in_progress
            - done
          example: ready
        proposedReady:
          type: boolean
          example: true
          description: >-
            PO 已完成该 story，正在等待用户的决定（Phase 12c——RN-048）。与 `status: "draft"`
            共存：它是一个提议，不是一个状态。始终为 `false`，如果项目处于 `auto` 模式。
        returnedReason:
          type: object
          example: DoD too generic — spell out the acceptance criteria.
          nullable: true
          description: 用户为何把 story 退回给 PO。从未被退回时为 `null`。
        returnedAt:
          type: object
          example: '2026-08-02T14:00:00.000Z'
          format: date-time
          nullable: true
        archivedAt:
          type: string
          example: null
          format: date-time
          description: >-
            story 被归档的时间（RN-727）。已归档的 story 离开 backlog、覆盖、计划和认领；`null` 表示仍在进行中的
            story。
          nullable: true
        archivedReason:
          type: string
          example: null
          description: 归档的原因，如果有人说明的话（RN-727）。
          nullable: true
        createdAt:
          type: string
          example: '2026-07-25T09:00:00.000Z'
          format: date-time
        updatedAt:
          type: string
          example: '2026-07-25T09:40:00.000Z'
          format: date-time
        semanticDuplicate:
          $ref: '#/components/schemas/SemanticDuplicateCheckResponseDto'
      required:
        - id
        - epicId
        - projectId
        - sessionId
        - title
        - description
        - rf
        - rnf
        - businessRuleIds
        - dod
        - dor
        - moduleIds
        - status
        - proposedReady
        - returnedReason
        - returnedAt
        - archivedAt
        - archivedReason
        - createdAt
        - updatedAt
        - semanticDuplicate
    CreateEpicInternalDto:
      type: object
      properties:
        projectId:
          type: string
          format: uuid
          example: 01JC4Z0000PROJETO0000000001
        title:
          type: string
          example: Shopping cart
        description:
          type: string
          example: Everything the buyer does before paying.
      required:
        - projectId
        - title
    CreateHandoffInternalDto:
      type: object
      properties:
        projectId:
          type: string
          format: uuid
          example: 01JC4Z0000PROJETO0000000001
        fromAgent:
          type: string
          example: criativo
        toAgent:
          type: string
          example: po
        artifactId:
          type: string
          example: 01JC4Z8QK3M7YV2N5T9B0PXHRB
          description: 引发该 handoff 的制品对应的 session_event 的 ULID。
        seAusente:
          type: boolean
          example: true
          description: >-
            仅当还没有人拿到时（ADR 0182，RN-636）：如果项目的任何会话中已有给该目标的待处理提议，则返回它（`desfecho:
            ja_oferecido`）而不是替换它。AppSec 会发送这个字段。
      required:
        - projectId
        - fromAgent
        - toAgent
    CreateModuleMapInternalDto:
      type: object
      properties:
        projectId:
          type: string
          format: uuid
          example: 01JC4Z0000PROJETO0000000001
        modules:
          type: array
          items:
            type: object
            additionalProperties: true
          example:
            - name: api
              stack: NestJS
              responsibility: Rules and HTTP
              dependsOn:
                - db
              resources:
                cpus: 1
                memoryMb: 1024
                pidsLimit: 256
          description: >-
            模块图。存在环会使映射以 400 被拒绝——校验在领域层。每个模块可以声明
            `resources`（cpus、memoryMb、pidsLimit——三项全有或全无）；部分声明、超过容器上限的值，或模块总和超过上限，同样返回
            400（RN-683）。
      required:
        - projectId
        - modules
    CreateProjectDto:
      type: object
      properties:
        name:
          type: string
          example: Checkout
          minLength: 2
        slug:
          type: string
          example: checkout
          pattern: ^[a-z0-9]+(-[a-z0-9]+)*$
          description: kebab-case。在 workspace 内唯一。
        maxConsecutiveBlocked:
          type: number
          example: 3
          description: >-
            熔断器（Phase 12b——RN-047）：连续多少个任务以阻塞结束会让该模块的 dev agent 停在
            idle_tripped。省略时使用领域默认值（DEFAULT_MAX_CONSECUTIVE_BLOCKED）。从下一次激活执行起生效——不影响已在运行的代理。
        storyPromotion:
          type: string
          enum:
            - manual
            - auto
          example: auto
          description: >-
            谁把 story 从 `draft` 提升为 `ready`（Phase 12c——RN-048）。`auto`（自 RN-659
            起为新项目的默认值）：完整的 story 一出生就是 `ready`，没有人工步骤。`manual`：PO 提议，由你在 Backlog
            标签页中决定。更改默认值并没有改写任何已有项目。领域校验（DoD/DoR/RF/规则/模块）在两种模式下完全相同；模式只改变由谁触发。
        executionMode:
          type: string
          enum:
            - container
            - mounted
            - runner
          example: container
          description: >-
            此项目的命令在哪里执行（RN-169/RN-421——ADR 0072/0104）。`container`（默认）：由产品管理、位于
            PROJECT_WORKSPACES_ROOT 内的文件夹，即通常的行为。`mounted`：你自己的一个文件夹，在
            `workspacePath` 中给出，位于安装范围的基目录 `BRABO_PROJECTS_BASE` 内（ADR
            0141）——创建时只校验路径格式和该基目录，文件夹本身稍后在 Infra 启动容器时创建（RN-501，ADR
            0142）。`runner`：你自己的一个不需要 bind-mount
            的文件夹——创建时只校验路径格式，项目以“未验证”状态诞生；在你的机器上运行 `brabo-runner --project <id>
            --dir <folder>` 来确认它（RN-423）。
        workspacePath:
          type: string
          example: /home/you/projects/store
          description: >-
            文件夹的绝对路径，当 `executionMode` 为 `mounted` 或 `runner` 时必需，为 `container`
            时拒绝。在这两种模式中，现在都只校验格式——绝对路径、不含 `..`、不是系统根目录、不是系统文件夹、不与 Brabo
            检出目录重叠（RN-422/RN-423）。`mounted` 增加一条规则：它必须位于 `BRABO_PROJECTS_BASE`
            内，那是你机器上 Brabo 容器唯一能看到的文件夹（ADR
            0141）——没有配置基目录时，此安装不提供该模式，创建时会说明这一点。磁盘稍后才会被触及：在 `runner` 中由 runner
            触及，在 `mounted` 中在 Infra 启动容器时触及（RN-501，ADR 0142）。
        language:
          type: string
          example: pt-BR
          maxLength: 35
          description: >-
            项目的语言（RN-619）——共享制品和没有人类作者的回合使用它。服务器能识别的任何 BCP-47 代码，以规范形式存储；无法识别的返回
            400。省略时，项目以其创建者的有效回复语言开始（RN-618：账户选择 > 已确认的检测 > 界面语言）。
      required:
        - name
        - slug
    CreateSessionDto:
      type: object
      properties:
        kind:
          type: string
          enum:
            - consultiva
            - criativa
          example: criativa
          description: >-
            会话的意图。`consultiva`（咨询式）只是对话；`criativa`（创作式）是产出的那种——它与 Creative
            代理开启构思，并且是唯一会进入执行的。必需：没有它响应为 400，因为类型是开启者的选择并会被记录。
        name:
          type: string
          example: Cart checkout
          maxLength: 80
          description: 友好名称，可选。不会取代 id 的井号标签——界面会把两者组合起来。空白视为缺省。
      required:
        - kind
    CreateSocketTicketDto:
      type: object
      properties:
        scope:
          type: string
          enum:
            - heartbeat
            - terminal
          example: heartbeat
          description: >-
            `heartbeat` 需要 `viewer` 角色；`terminal` 需要 `developer`——与
            `MIN_ROLE_FOR_ACTION_TYPE.terminal` 的最低角色相同。
      required:
        - scope
    CreateStoryInternalDto:
      type: object
      properties:
        projectId:
          type: string
          format: uuid
          example: 01JC4Z0000PROJETO0000000001
        epicId:
          type: string
          format: uuid
          example: 01JC4Z0000EPICO000000000001
        title:
          type: string
          example: Add item to cart
        description:
          type: string
          example: As a buyer, I want to gather items before paying.
        rf:
          example:
            - The cart accepts up to 50 items
          type: array
          items:
            type: string
        rnf:
          example:
            - Response under 200ms at p95
          type: array
          items:
            type: string
        dod:
          example:
            - Unit tests green
          type: array
          items:
            type: string
        dor:
          example:
            - Module defined
          type: array
          items:
            type: string
        businessRuleIds:
          example:
            - RN-014
          description: 每个 id 都必须引用一个确实存在的 `artifact.business_rule` 事件——编造的 id 会被拒绝。
          type: array
          items:
            type: string
      required:
        - projectId
        - epicId
        - title
    CreateTaskInternalDto:
      type: object
      properties:
        projectId:
          type: string
          format: uuid
          example: 01JC4Z0000PROJETO0000000001
        storyId:
          type: string
          format: uuid
          example: 01JC4Z0000HISTORIA000000001
        title:
          type: string
          example: Endpoint POST /cart/items
        description:
          type: string
          example: Accepts SKU and quantity; validates stock.
      required:
        - projectId
        - storyId
        - title
    CreateWorkspaceDto:
      type: object
      properties:
        name:
          type: string
          example: Acme Corp
          minLength: 2
        slug:
          type: string
          example: acme-corp
          pattern: ^[a-z0-9]+(-[a-z0-9]+)*$
          description: kebab-case。在系统中唯一，并在 URL 中可见。
      required:
        - name
        - slug
    CredentialedProvidersResponseDto:
      type: object
      properties:
        providers:
          type: array
          example:
            - anthropic
            - openrouter
          description: >-
            LLM providers the workspace owner of record has a key for. Names
            only; `ollama` never appears.
          items:
            type: string
            enum:
              - ollama
              - anthropic
              - openai
              - openrouter
              - nvidia-nim
              - together
              - deepinfra
              - bitdeer
              - vultr
              - 9router
              - opencode-zen
      required:
        - providers
    CredentialSpendPorMesResponseDto:
      type: object
      properties:
        mes:
          type: string
          example: '2026-08-01T00:00:00.000Z'
          format: date-time
        costMicros:
          type: number
          example: 1250000
        chamadas:
          type: number
          example: 42
      required:
        - mes
        - costMicros
        - chamadas
    CredentialSpendPorProviderResponseDto:
      type: object
      properties:
        provider:
          type: string
          example: openrouter
        temCredencial:
          type: boolean
          example: true
          description: 该凭据今天仍然存在。`false` 是来自已被移除的密钥的历史花费——消耗确实发生过，不会因此从报告中消失。
        costMicros:
          type: number
          example: 1250000
        inputTokens:
          type: number
          example: 120000
        outputTokens:
          type: number
          example: 35000
        chamadas:
          type: number
          example: 42
        costMicrosAgentes:
          type: number
          example: 900000
          description: 由代理花费的部分——RN-058 开始记在所有者账上的那一部分。
        costMicrosPessoas:
          type: number
          example: 350000
          description: 由人在聊天中花费的部分。来自同一把密钥，回答的是另一个问题。
        porMes:
          type: array
          items:
            $ref: '#/components/schemas/CredentialSpendPorMesResponseDto'
      required:
        - provider
        - temCredencial
        - costMicros
        - inputTokens
        - outputTokens
        - chamadas
        - costMicrosAgentes
        - costMicrosPessoas
        - porMes
    CredentialSpendResponseDto:
      type: object
      properties:
        workspaceId:
          type: string
          example: 9b1c2d3e-4f50-4a61-8b72-0c3d4e5f6a7b
          format: uuid
        ownerId:
          type: string
          example: 9b1c2d3e-4f50-4a61-8b72-0c3d4e5f6a7b
          format: uuid
          description: 密钥的所有者——为此 workspace 的代理出资的人（RN-058）。
        meses:
          type: number
          example: 6
        totalMicros:
          type: number
          example: 1250000
        porProvider:
          type: array
          items:
            $ref: '#/components/schemas/CredentialSpendPorProviderResponseDto'
      required:
        - workspaceId
        - ownerId
        - meses
        - totalMicros
        - porProvider
    CredentialTestResultResponseDto:
      type: object
      properties:
        resultado:
          type: string
          enum:
            - ok
            - recusado
            - nao_suportado
          example: recusado
          description: >-
            `ok`——提供商接受了该凭据。`recusado`——提供商拒绝了它（密钥无效/已撤销、余额不足、网络问题）。`nao_suportado`——此提供商没有经过验证的测试端点（`ollama`、`anthropic`、`openai`）：什么都没有被验证，在这里说“ok”就是撒谎。
        motivo:
          type: string
          example: 'connection test failed for openrouter: openrouter responded 401'
          description: 仅在 `recusado` 时——提供商给出的原因。
      required:
        - resultado
    CriterioDoAceiteResponseDto:
      type: object
      properties:
        regras:
          type: number
          example: 4
          description: 项目中的业务规则。
        cobertas:
          type: number
          example: 4
          description: 至少被一个 story 引用的规则——等于 `regras`。
        repositorio:
          type: string
          enum:
            - local
            - a_provisionar_local
          example: a_provisionar_local
          description: >-
            `local`：项目已经有一个 `local` 仓库。`a_provisionar_local`：还没有——接受时会置备一个
            `local` 仓库。
      required:
        - regras
        - cobertas
        - repositorio
    DecideBootstrapPlanDto:
      type: object
      properties:
        planGeneratedAt:
          type: string
          example: '2026-08-01T23:45:00.000Z'
          format: date-time
          description: >-
            你所看到的计划的 `generatedAt`。乐观锁：如果计划自那以后已被重新生成（重新接入，或仓库发生了变化），决定会以 409
            拒绝，而不是把针对别的东西给出的同意应用上去。
      required:
        - planGeneratedAt
    DecideBootstrapPlanResponseDto:
      type: object
      properties:
        repository:
          $ref: '#/components/schemas/ProvisionedRepositoryResponseDto'
        bootstrap:
          description: 引导最终停在哪里。在 `skip` 时游标不会前进：没有任何步骤运行，记录也如此说明。
          allOf:
            - $ref: '#/components/schemas/PassoDeBootstrapResponseDto'
      required:
        - repository
        - bootstrap
    DecideProjectImageInternalDto:
      type: object
      properties:
        projectId:
          type: string
          format: uuid
        image:
          type: string
          example: node:22-bookworm-slim
          description: 带有显式 TAG 或 digest 的 OCI 引用。`latest` 会以 400 拒绝——该规则位于领域层。
        rationale:
          type: string
          example: >-
            The module_map is TypeScript on Node 22; the slim variant has the
            runtime and nothing else.
          description: 为什么选这个镜像。至少 10 个字符。
        network:
          type: string
          enum:
            - none
            - egress
          default: none
          description: 容器的网络姿态。`none` 是默认值，正是它让“在里面，代理是自由的”成为一句安全的话。
        resources:
          type: object
          additionalProperties: true
          example:
            cpus: 2
            memoryMb: 4096
            pidsLimit: 512
          description: 资源上限。超过最大值时，决定会被拒绝，绝不会被悄悄降级。
      required:
        - projectId
        - image
        - rationale
    DecisaoDeImagemResponseDto:
      type: object
      properties:
        image:
          type: string
          example: node:22-bookworm-slim
          description: 带有显式 TAG 或 digest 的 OCI 引用。`latest` 被拒绝：制品需要在六个月后仍然表达同样的意思。
        rationale:
          type: string
          example: >-
            The module_map is all TypeScript over Node; the slim variant is
            enough and reduces surface.
          description: 为什么是这个镜像。正是这一点让决定可以被评审。
        network:
          type: string
          enum:
            - none
            - egress
          example: none
          description: >-
            容器的网络姿态，在制品中一次性决定，而不是逐条命令决定。`egress` 意味着成本和攻击面：Architect
            提出请求，用户在置备时授权。
        resources:
          $ref: '#/components/schemas/RecursosDoContainerResponseDto'
      required:
        - image
        - rationale
        - network
        - resources
    DeclararRedeDto:
      type: object
      properties:
        ports:
          description: >-
            Up to 10 TCP ports published on 127.0.0.1 only. `host` 1024-65535,
            never an installation port, no duplicates.
          type: array
          items:
            $ref: '#/components/schemas/PortaDto'
        dns:
          description: Up to 3 literal IPs.
          type: array
          items:
            type: string
        extraHosts:
          description: >-
            Up to 10 `--add-host` entries: name in [a-z0-9.-], never localhost
            or an installation service, IPv4 literal.
          type: array
          items:
            $ref: '#/components/schemas/HostExtraDto'
    DeclareModuleContractsInternalDto:
      type: object
      properties:
        projectId:
          type: string
          format: uuid
        contratos:
          description: >-
            完整的契约列表：每次调用都是一个新版本，替换上一个版本。模块消费的内容不在这里——它在读取时由当前 module_map 的
            `dependsOn` 推导而来。
          type: array
          items:
            $ref: '#/components/schemas/ContratoDeModuloInternalDto'
      required:
        - projectId
        - contratos
    DelegationResponseDto:
      type: object
      properties:
        id:
          type: string
          format: uuid
          example: 01JC4Z0000DELEGACAO00000001
        projectId:
          type: string
          format: uuid
          example: 01JC4Z0000PROJETO0000000001
        sessionId:
          type: string
          format: uuid
          example: 01JC4Z0000SESSAO000000000001
        taskId:
          type: object
          format: uuid
          nullable: true
          example: 01JC4Z0000TAREFA00000000001
          description: 当区域背后没有 backlog 任务时为 null（Infra，Phase 8c）。
        area:
          type: string
          example: qa
        leadAgent:
          type: string
          example: qa-lead
        subagent:
          type: string
          example: qa-automacao
        status:
          type: string
          enum:
            - completed
            - failed
            - dispensed
          example: completed
        parecerArtifactId:
          type: object
          nullable: true
          example: evt_01jc4z0000parecer000000001
        failureOrigin:
          type: string
          enum:
            - infra
            - modelo
            - codigo
            - politica
          nullable: true
          example: null
        failureReason:
          type: object
          nullable: true
          example: null
        justification:
          type: object
          nullable: true
          example: null
        createdAt:
          type: string
          example: '2026-07-30T12:00:00.000Z'
      required:
        - id
        - projectId
        - sessionId
        - taskId
        - area
        - leadAgent
        - subagent
        - status
        - parecerArtifactId
        - failureOrigin
        - failureReason
        - justification
        - createdAt
    DenyActionDto:
      type: object
      properties:
        reason:
          type: string
          example: The command would delete the working directory.
          description: 与拒绝一起保存在事件日志中。可选，但建议填写。
    DevContextAdrResponseDto:
      type: object
      properties:
        title:
          type: string
          example: 0007 — Postgres queue instead of Redis
        content:
          type: string
          example: |-
            ## Context

            …
        securityRelevant:
          type: boolean
          example: false
          description: 由 Architect 标记。会成为 SecOps 裁决中的一份参考清单——没有逐行对应关系，声称有的话会歪曲检查的深度。
      required:
        - title
        - content
        - securityRelevant
    DevContextBusinessRuleResponseDto:
      type: object
      properties:
        title:
          type: string
          example: Cart accepts at most 50 items
        description:
          type: string
          example: Above that, the addition is refused with 409.
      required:
        - title
        - description
    DevContextDecisaoResponseDto:
      type: object
      properties:
        id:
          type: string
        questionId:
          type: string
          nullable: true
        pergunta:
          type: string
          description: 被决定的问题。
        decisao:
          type: string
          description: 用户的决定。
        decididaEm:
          type: string
          format: date-time
      required:
        - id
        - questionId
        - pergunta
        - decisao
        - decididaEm
    DevContextModuleOpenTaskResponseDto:
      type: object
      properties:
        id:
          type: string
          example: 01J0000000000000000000000
        title:
          type: string
          example: Expose POST /links
        status:
          type: string
          example: todo
        storyTitle:
          type: string
          example: Members create short links
      required:
        - id
        - title
        - status
        - storyTitle
    DevContextSiblingTaskResponseDto:
      type: object
      properties:
        id:
          type: string
          example: 01J0000000000000000000000
        title:
          type: string
          example: Expose POST /login returning a JWT
        status:
          type: string
          example: todo
      required:
        - id
        - title
        - status
    DevTaskContextResponseDto:
      type: object
      properties:
        task:
          $ref: '#/components/schemas/TaskResponseDto'
        story:
          $ref: '#/components/schemas/StoryResponseDto'
        businessRules:
          type: array
          items:
            $ref: '#/components/schemas/DevContextBusinessRuleResponseDto'
        adrs:
          description: 适用的 ADR：横切的那些（未声明模块）加上引用此代理模块的那些。
          type: array
          items:
            $ref: '#/components/schemas/DevContextAdrResponseDto'
        siblingTasks:
          description: >-
            同一 story 的其他任务及其状态。QA gate
            按属于此任务的内容评判交付；兄弟任务的需求只是一条观察，绝不是拒绝的理由（RN-765）。
          type: array
          items:
            $ref: '#/components/schemas/DevContextSiblingTaskResponseDto'
        moduleOpenTasks:
          description: >-
            同一模块中其他 story 尚未完成的任务，附 story 标题（最多 20 个）。依赖于另一个 story 将交付内容的需求，对 QA
            gate 而言只是一条观察，绝不是拒绝的理由（RN-785）。
          type: array
          items:
            $ref: '#/components/schemas/DevContextModuleOpenTaskResponseDto'
        moduleOpenTasksTotal:
          type: number
          description: 该模块在其他 story 中未完成任务的真实数量——当列表被截断时大于 `moduleOpenTasks.length`。
        moduleContract:
          nullable: true
          description: >-
            任务所属模块的当前契约（`artifact.module_contracts`）——QA gate 的接口来源。当 story
            与契约不一致时，裁决会指出这一差异，而不是拒绝遵循了契约的 dev（RN-786）。没有可解析的模块或契约时为 `null`。
          type: object
          allOf:
            - $ref: '#/components/schemas/ContratoDeModuloResponseDto'
        primeiraDoModulo:
          type: boolean
          description: >-
            这是否是该模块的第一个任务：项目中最早的那个 `backlog.task_claimed`（针对
            `module`）就是此任务。engine 会给它两倍的单任务预算（RN-774）。值为 `false`，如果没有 `module`。
        decisoes:
          description: >-
            RN-859：通过 `afeta` 与此任务、其 story 或其规则关联的用户决定——发生冲突时优先于 RF/DoR。最新的在前，最多
            10 条，每条文本截断为 1,500 个字符。
          type: array
          items:
            $ref: '#/components/schemas/DevContextDecisaoResponseDto'
        decisoesDoProjeto:
          description: >-
            RN-872（ADR 0215）：项目的其他决定——即 `afeta` 不涉及此任务的那些。单独的部分，与 `decisoes`
            共用同一个 10 条上限（任务决定优先占用上限），最新的在前。
          type: array
          items:
            $ref: '#/components/schemas/DevContextDecisaoResponseDto'
      required:
        - task
        - story
        - businessRules
        - adrs
        - siblingTasks
        - moduleOpenTasks
        - moduleOpenTasksTotal
        - moduleContract
        - primeiraDoModulo
        - decisoes
        - decisoesDoProjeto
    DiffLineResponseDto:
      type: object
      properties:
        kind:
          type: string
          enum:
            - add
            - del
            - ctx
          example: add
        content:
          type: string
          example: Always run the suite before opening the PR.
        lineNo:
          type: number
          example: 42
      required:
        - kind
        - content
    EpicComHistoriasResponseDto:
      type: object
      properties:
        id:
          type: string
          example: 01JC4Z0000EPICO000000000001
        projectId:
          type: string
          example: 01JC4Z0000PROJETO0000000001
        sessionId:
          type: string
          example: 01JC4Z8QK3M7YV2N5T9B0PXHRA
        title:
          type: string
          example: Shopping cart
        description:
          type: string
          example: Everything the buyer does before paying.
        createdAt:
          type: string
          example: '2026-07-25T08:30:00.000Z'
          format: date-time
        updatedAt:
          type: string
          example: '2026-07-25T08:30:00.000Z'
          format: date-time
        stories:
          type: array
          items:
            $ref: '#/components/schemas/StoryComTarefasResponseDto'
      required:
        - id
        - projectId
        - sessionId
        - title
        - description
        - createdAt
        - updatedAt
        - stories
    EpicResponseDto:
      type: object
      properties:
        id:
          type: string
          example: 01JC4Z0000EPICO000000000001
        projectId:
          type: string
          example: 01JC4Z0000PROJETO0000000001
        sessionId:
          type: string
          example: 01JC4Z8QK3M7YV2N5T9B0PXHRA
        title:
          type: string
          example: Shopping cart
        description:
          type: string
          example: Everything the buyer does before paying.
        createdAt:
          type: string
          example: '2026-07-25T08:30:00.000Z'
          format: date-time
        updatedAt:
          type: string
          example: '2026-07-25T08:30:00.000Z'
          format: date-time
      required:
        - id
        - projectId
        - sessionId
        - title
        - description
        - createdAt
        - updatedAt
    EstadoDoC4DiagramaResponseDto:
      type: object
      properties:
        status:
          type: string
          enum:
            - sem_diagrama
            - gerado
          example: sem_diagrama
          description: '`sem_diagrama` 是初始状态：Architect 还没有生成图。'
        diagrama:
          nullable: true
          description: 当前的图，或在还没有时为 `null`。
          type: object
          allOf:
            - $ref: '#/components/schemas/C4DiagramaResponseDto'
        version:
          type: number
          example: 0
          description: 当前制品的版本；没有图时为 0。修订意味着重新生成——历史从不被改写。
        eventId:
          type: object
          nullable: true
          example: 01JC4Z0000EVENTO000000002
        createdAt:
          type: object
          nullable: true
          format: date-time
      required:
        - status
        - diagrama
        - version
        - eventId
        - createdAt
    EstadoDoContainerResponseDto:
      type: object
      properties:
        status:
          type: string
          enum:
            - sem_decisao
            - decidido
          example: sem_decisao
          description: '`sem_decisao` 是每个项目的初始状态，正是它关闭了 RN-105 gate：没有镜像就没有容器，Code 标签页也无法打开。'
        decisao:
          nullable: true
          description: 当前的决定，或在还没有时为 `null`。
          type: object
          allOf:
            - $ref: '#/components/schemas/DecisaoDeImagemResponseDto'
        version:
          type: number
          example: 0
          description: 当前制品的版本；没有决定时为 0。修订意味着发出新版本——历史从不被改写。
        eventId:
          type: object
          nullable: true
          example: 01JC4Z0000EVENTO000000001
          description: 确定当前决定的事件 id，用于审计。
        decidedAt:
          type: object
          nullable: true
          format: date-time
      required:
        - status
        - decisao
        - version
        - eventId
        - decidedAt
    EstadoDoRoteamentoResponseDto:
      type: object
      properties:
        status:
          type: string
          enum:
            - sem_roteamento
            - roteado
          example: roteado
        roteamento:
          type: array
          items:
            $ref: '#/components/schemas/RoteamentoDeModuloResponseDto'
        version:
          type: number
          example: 1
        eventId:
          type: object
          example: 01JC4Z0000EVENTOROTEAMENTO01
          nullable: true
        createdAt:
          type: object
          example: '2026-08-30T12:00:00.000Z'
          nullable: true
      required:
        - status
        - roteamento
        - version
        - eventId
        - createdAt
    ExecucaoAtivadaResponseDto:
      type: object
      properties:
        sessionId:
          type: string
          example: 01JC4Z8QK3M7YV2N5T9B0PXHRA
          description: 已创建的执行会话。
        modules:
          example:
            - api
            - web
          description: 获得 dev agent 的 `module_map` 模块。每个模块一个代理，各自位于隔离的 worktree 中。
          type: array
          items:
            type: string
      required:
        - sessionId
        - modules
    FirstAccountInternalDto:
      type: object
      properties:
        email:
          type: string
          example: voce@exemplo.dev
          description: 所有者的邮箱，在 TTY 中输入。在写入任何内容之前进行规范化；安装器会把它记录在标记中，因为邮箱用于识别身份，并不是机密。
        senha:
          type: string
          example: uma frase longa e minha
          description: >-
            无回显读取，并由安装器确认。用完即弃——绝不写入
            `.env`、标记或日志，也绝不由任何代码生成：不存在账户以脚本选择的密码诞生的路径。最低要求是领域策略，与注册使用的是同一段代码；这里的最大长度是对
            argon2id 的保护，而不是策略。
        nome:
          type: string
          example: Fulana de Tal
          description: 显示名称。省略时：个人 workspace 的名称回落为邮箱的本地部分（RN-410）——不会编造任何东西。
      required:
        - email
        - senha
    FirstAccountInternalResponseDto:
      type: object
      properties:
        userId:
          type: string
          format: uuid
          example: 01JC4Z0000USUARIO0000000001
          description: 所创建的用户。
        email:
          type: string
          example: voce@exemplo.dev
          description: 规范化后的邮箱——实际写入的内容，可能与输入的不同。安装器会把它记录在标记中。
        workspaceId:
          type: string
          format: uuid
          example: 01JC4Z0000WORKSPACE00000001
          description: >-
            在同一事务中诞生的个人 workspace（RN-410），该账户为其
            `owner`。没有它，安装结束时会有一个能用的登录，而仪表盘中的“新建项目”却无处可建。
      required:
        - userId
        - email
        - workspaceId
    GateAbertoResponseDto:
      type: object
      properties:
        gateStatus:
          type: string
          enum:
            - awaiting_qa
            - awaiting_secops
            - awaiting_user
          example: awaiting_qa
      required:
        - gateStatus
    GateRegistryResponseDto:
      type: object
      properties:
        version:
          type: number
          example: 1
        gates:
          type: array
          items:
            $ref: '#/components/schemas/GateResponseDto'
      required:
        - version
        - gates
    GateResponseDto:
      type: object
      properties:
        id:
          type: string
          example: qa-verificada
        fluxo:
          type: string
          example: pr
        dono:
          type: string
          example: area-qa
        entrada:
          example:
            - pr-aberta
          type: array
          items:
            type: string
        entregavel:
          oneOf:
            - type: string
            - type: array
              items:
                type: string
          example: veredito-qa
        verificacao:
          type: string
          enum:
            - script
            - humana
        severidade:
          type: string
          enum:
            - block
            - warn
        aprovacao_humana:
          type: boolean
          description: 决定权在用户——要么是直接点击，要么是委托给用户自己编写的策略。在四个按宪法必须手动的 gate 中保持不变（RN-071）。
        status:
          type: string
          enum:
            - active
            - planned
        evidencia:
          type: object
          additionalProperties: true
          description: >-
            gate 通过的证据存放在哪里：`event_log`（类型 + 载荷过滤）、`teste` 或
            `ci`（目标路径）。`planned` gate 上不存在。
        backlog:
          type: string
          example: ADR 0053
      required:
        - id
        - fluxo
        - dono
        - entrada
        - entregavel
        - verificacao
        - severidade
        - aprovacao_humana
        - status
    GateResumoResponseDto:
      type: object
      properties:
        id:
          type: string
          example: qa-verificada
        fluxo:
          type: string
          example: pr
          description: gate 所属的流程。
        dono:
          type: string
          example: area-qa
          description: 由谁判定。
        entrada:
          example:
            - pr-aberta
          description: gate 打开所需存在的东西。
          type: array
          items:
            type: string
        entregavel:
          type: object
          example: veredito-qa
          description: gate 通过时产出的东西。
        aprovacaoHumana:
          type: boolean
          example: true
          description: 决定权在用户——点击时直接决定，或委托给用户自己编写的策略（见 RN-071）。
        severidade:
          type: string
          example: block
          enum:
            - block
            - warn
      required:
        - id
        - fluxo
        - dono
        - entrada
        - entregavel
        - aprovacaoHumana
        - severidade
    GatesResponseDto:
      type: object
      properties:
        version:
          type: number
          example: 1
          description: 注册表格式的版本。
        gates:
          description: 只有活跃的 gate。`planned` gate 描述的是未来的角色，不应出现在说明当下正在发生什么的界面上。
          type: array
          items:
            $ref: '#/components/schemas/GateResumoResponseDto'
      required:
        - version
        - gates
    GateVerdictResponseDto:
      type: object
      properties:
        nextAction:
          type: string
          enum:
            - correct
            - run_secops
            - done
            - blocked
            - await_user
          example: run_secops
          description: >-
            接下来会发生什么。`correct` 退回给 dev；`run_secops` 推进 gate；`done`
            将其交给用户；`await_user` 暂停周期直到未决问题得到回答（RN-860）；`blocked`
            表示修正上限已用尽，只有人才能解除阻塞。
        task:
          $ref: '#/components/schemas/TaskResponseDto'
        openQuestionId:
          type: string
          format: uuid
          description: 配合 `await_user`：周期正在等待的未决问题。
      required:
        - nextAction
        - task
    GitAuthorizeUrlResponseDto:
      type: object
      properties:
        authorizeUrl:
          type: string
          example: >-
            https://github.com/login/oauth/authorize?client_id=…&state=…&redirect_uri=…
          description: >-
            要把浏览器发送到哪里。`state` 用 `GIT_OAUTH_STATE_SECRET` 经 HMAC
            签名——正是这一点防止回调被伪造。
      required:
        - authorizeUrl
    HandoffResponseDto:
      type: object
      properties:
        id:
          type: string
          example: 01JC4Z0000HANDOFF00000000001
        sessionId:
          type: string
          example: 01JC4Z8QK3M7YV2N5T9B0PXHRA
        projectId:
          type: string
          example: 01JC4Z0000PROJETO0000000001
        fromAgent:
          type: string
          example: criativo
          description: 交出接力棒的代理的 slug。
        toAgent:
          type: string
          example: po
          description: 接收代理的 slug。
        artifactId:
          type: object
          example: 01JC4Z0000ARTEFATO000000001
          nullable: true
          description: 引发该 handoff 的制品（product_brief、module_map……）。
        status:
          type: string
          enum:
            - offered
            - accepted
            - completed
            - rejected
            - superseded
          example: offered
          description: >-
            此字段是可变的——它是当前状态。每次状态转换也会在日志中成为一个不可变的 `handoff.*`
            事件，历史就存放在那里。`superseded`（ADR
            0182，RN-635）：该提议不再是当前的——目标代理已通过其他路径被激活，或项目中给同一目标的更新提议取代了它（`handoff.superseded`）。只有
            `offered` 可以被接受。
        createdAt:
          type: string
          example: '2026-07-24T10:00:00.000Z'
          format: date-time
        updatedAt:
          type: string
          example: '2026-07-24T10:05:00.000Z'
          format: date-time
      required:
        - id
        - sessionId
        - projectId
        - fromAgent
        - toAgent
        - artifactId
        - status
        - createdAt
        - updatedAt
    HealthStatusResponseDto:
      type: object
      properties:
        service:
          type: string
          enum:
            - api
            - engine
          example: api
        status:
          type: string
          enum:
            - ok
            - error
          example: ok
        timestamp:
          type: string
          example: '2026-07-27T15:40:00.000Z'
          format: date-time
        details:
          type: object
          additionalProperties: true
          description: 仅当 `status=error` 时，附带失败消息。健康的响应刻意不带任何细节：存活检查不是暴露拓扑的地方。
      required:
        - service
        - status
        - timestamp
    HostExtraDto:
      type: object
      properties:
        name:
          type: string
          example: api.local
        ip:
          type: string
          example: 10.0.0.5
      required:
        - name
        - ip
    HuggingFaceModelResponseDto:
      type: object
      properties:
        repoId:
          type: string
          example: meta-llama/Llama-3.1-8B-Instruct-GGUF
          description: Hugging Face Hub 上的 `<publisher>/<modelo>`。
        publisher:
          type: string
          example: meta-llama
        downloads:
          type: number
          example: 182034
        likes:
          type: number
          example: 210
        official:
          type: boolean
          example: true
          description: >-
            该发布者在已知厂商的策展允许列表中（`domain/llm/huggingface-official-publishers.ts`）。`false`
            表示任何第三方的重新上传——当 `includeCommunity=true` 带来此结果时，界面会显示安全警告。
      required:
        - repoId
        - publisher
        - downloads
        - likes
        - official
    HybridSearchHitResponseDto:
      type: object
      properties:
        chunkId:
          type: string
        scope:
          type: string
          enum:
            - docs
            - adr
            - session
            - local
        content:
          type: string
        score:
          type: number
          description: >-
            组合值（RAG_SEARCH_WEIGHT_VECTOR*vector +
            RAG_SEARCH_WEIGHT_LEXICAL*lexical），已按阈值过滤——返回的每个命中都已通过阈值。
        vectorScore:
          type: number
          nullable: true
          description: 余弦相似度，0..1。当此分块没有向量时为 `null`。
        lexicalScore:
          type: number
          nullable: true
          description: 规范化的 `ts_rank`，0..1。当搜索词在此分块中没有匹配时为 `null`。
        origin:
          type: object
          additionalProperties: true
          description: >-
            按 `kind` 区分的联合类型：“file”（`sourcePath`/`headingPath`）或
            “session”（`sessionId`/`eventId`）。见 `domain/rag/rag-citation.ts`。
      required:
        - chunkId
        - scope
        - content
        - score
        - vectorScore
        - lexicalScore
        - origin
    HybridSearchRequestDto:
      type: object
      properties:
        query:
          type: string
          example: how does the PR gate work
          description: 2 到 500 个字符之间——与 Code 标签页搜索的规则相同。
        scopes:
          type: array
          description: 缺省时搜索全部四个范围（RN-219/454）。
          items:
            type: string
            enum:
              - docs
              - adr
              - session
              - local
        limit:
          type: number
          example: 10
          description: 1 到 10——缺省时使用上限。
      required:
        - query
    HybridSearchResponseDto:
      type: object
      properties:
        query:
          type: string
        searchId:
          type: string
          nullable: true
          format: uuid
          description: >-
            此次搜索留下的 `rag_searches` 记录（RN-479）——投票所关联的 id。`null`
            表示遥测记录没有写入（插入失败），这与“没有结果”不同：没有可关联投票的对象，界面需要区分两者，以免提供一个 api 会拒绝的控件。
        hits:
          type: array
          items:
            $ref: '#/components/schemas/HybridSearchHitResponseDto'
        vectorAvailable:
          type: boolean
          description: 当嵌入提供商没有响应时为 `false`——搜索只用词法信号运行（RN-233）。
        vectorUnavailableReason:
          type: string
      required:
        - query
        - searchId
        - hits
        - vectorAvailable
    HypothesisDraftDto:
      type: object
      properties:
        agenteAlvo:
          type: string
          example: dev-api
        observacao:
          type: string
          example: The agent reopened the same task three times.
        hipotese:
          type: string
          example: The instructions don't say when the task is done.
        sugestao:
          type: string
          example: Add an explicit definition-of-done criterion.
        confiancaPercent:
          type: number
          example: 72
          minimum: 0
          maximum: 100
          description: 由模型自报，并非测量所得。
        evidenceEventIds:
          example:
            - 01JC4Z8QK3M7YV2N5T9B0PXHRB
          description: 至少一个，且每一个都必须存在于本会话中。编造的证据会以 400 拒绝——这正是假设与意见的区别。
          type: array
          items:
            type: string
        terminationAnalysis:
          description: 当 `cause` 表示异常终止时必需。
          allOf:
            - $ref: '#/components/schemas/HypothesisTerminationAnalysisDto'
      required:
        - agenteAlvo
        - observacao
        - hipotese
        - sugestao
        - confiancaPercent
        - evidenceEventIds
    HypothesisResponseDto:
      type: object
      properties:
        id:
          type: string
          example: 01JC4Z0000HIPOTESE000000001
        projectId:
          type: string
          example: 01JC4Z0000PROJETO0000000001
        sessionId:
          type: string
          example: 01JC4Z8QK3M7YV2N5T9B0PXHRA
        analysisId:
          type: string
          example: 01JC4Z0000ANALISE0000000001
          description: 生成此假设的分析轮次。
        agenteAlvo:
          type: string
          example: dev-api
          description: 该假设针对的是哪个代理。
        observacao:
          type: string
          example: The agent reopened the same task three times.
        hipotese:
          type: string
          example: The instructions don't say when to consider the task done.
        sugestao:
          type: string
          example: Add an explicit definition-of-done criterion to the instruction.
        confiancaPercent:
          type: number
          example: 72
          minimum: 0
          maximum: 100
          description: 由模型自己声明的置信度。不是测量，而是自报。
        evidenceEventIds:
          example:
            - 01JC4Z8QK3M7YV2N5T9B0PXHRB
          description: 支持该假设的日志事件。正是这一点让证据可审计，而不是凭信任接受。
          type: array
          items:
            type: string
        terminationAnalysis:
          nullable: true
          description: 仅出现在关于异常终止会话的假设上。
          type: object
          allOf:
            - $ref: '#/components/schemas/AnaliseDeTerminoResponseDto'
        status:
          type: string
          enum:
            - proposed
            - accepted
            - dismissed
          example: proposed
        decidedBy:
          type: object
          example: null
          nullable: true
        decidedAt:
          type: object
          example: null
          format: date-time
          nullable: true
        createdAt:
          type: string
          example: '2026-07-26T18:00:00.000Z'
          format: date-time
        updatedAt:
          type: string
          example: '2026-07-26T18:00:00.000Z'
          format: date-time
      required:
        - id
        - projectId
        - sessionId
        - analysisId
        - agenteAlvo
        - observacao
        - hipotese
        - sugestao
        - confiancaPercent
        - evidenceEventIds
        - terminationAnalysis
        - status
        - decidedBy
        - decidedAt
        - createdAt
        - updatedAt
    HypothesisTerminationAnalysisDto:
      type: object
      properties:
        causa:
          type: string
          example: heartbeat_timeout
          description: 失败的来源。绝不通过排除法推断——ADR 0020 的教训。
        estadoDaSessao:
          type: string
          example: closed_abnormally
        analise:
          type: string
          example: The agent stopped responding after a 90s tool call.
      required:
        - causa
        - estadoDaSessao
        - analise
    IdiomaEfetivoResponseDto:
      type: object
      properties:
        language:
          type: string
          example: pt-BR
        origin:
          type: string
          enum:
            - sessao
            - conta
            - detectado
            - interface
          example: interface
      required:
        - language
        - origin
    ImagemDecididaResponseDto:
      type: object
      properties:
        decisao:
          $ref: '#/components/schemas/DecisaoDeImagemResponseDto'
        version:
          type: number
          example: 1
        containerDePe:
          type: object
          nullable: true
          example:
            status: running
            imageVersion: 2
          description: >-
            The `running`/`stopped` container registered for the project, or
            null. The new decision does NOT change it: it only applies once the
            container is recreated (stop, remove, start).
      required:
        - decisao
        - version
        - containerDePe
    ImagemParaOBrokerResponseDto:
      type: object
      properties:
        image:
          type: string
          example: node:22-bookworm-slim
          description: 带有显式 tag 或 digest 的 OCI 引用。
        network:
          type: string
          enum:
            - none
            - egress
          example: none
        resources:
          $ref: '#/components/schemas/RecursosDoContainerResponseDto'
      required:
        - image
        - network
        - resources
    ImportLlmCredentialDto:
      type: object
      properties:
        provider:
          type: string
          enum:
            - anthropic
            - openai
            - openrouter
            - nvidia-nim
            - together
            - deepinfra
            - bitdeer
            - vultr
            - 9router
            - opencode-zen
          example: anthropic
        apiKey:
          type: string
          maxLength: 512
          description: >-
            The API key in plain text. Encrypted before touching the database;
            NEVER logged and NEVER returned.
        origem:
          type: string
          example: 'codex: ~/.codex/auth.json'
          maxLength: 200
          description: Tool and path the key was read from. Audit log only; never a value.
        substituir:
          type: boolean
          description: Must be `true` to replace an existing credential of this provider.
      required:
        - provider
        - apiKey
        - origem
    ImportLlmCredentialResponseDto:
      type: object
      properties:
        provider:
          type: string
          enum:
            - anthropic
            - openai
            - openrouter
            - nvidia-nim
            - together
            - deepinfra
            - bitdeer
            - vultr
            - 9router
            - opencode-zen
        substituida:
          type: boolean
          description: Whether an existing credential was replaced.
      required:
        - provider
        - substituida
    IndexDocsReportResponseDto:
      type: object
      properties:
        filesScanned:
          type: number
        docsChunks:
          type: number
        adrChunks:
          type: number
        truncated:
          type: boolean
        embedding:
          $ref: '#/components/schemas/IndexEmbeddingResponseDto'
      required:
        - filesScanned
        - docsChunks
        - adrChunks
        - truncated
        - embedding
    IndexEmbeddingResponseDto:
      type: object
      properties:
        available:
          type: boolean
        embedded:
          type: number
        skipped:
          type: number
        reason:
          type: string
      required:
        - available
        - embedded
        - skipped
    InfraArtifactResponseDto:
      type: object
      properties:
        id:
          type: string
          example: 01JC4Z0000INFRAARTEFATO00001
        projectId:
          type: string
          example: 01JC4Z0000PROJETO0000000001
        sessionId:
          type: string
          example: 01JC4Z8QK3M7YV2N5T9B0PXHRA
        title:
          type: string
          example: api's production Dockerfile
        prActionId:
          type: string
          example: 01JC4Z8QK3M7YV2N5T9B0PXHRD
          description: >-
            打开该 PR 的 `open_infra_pr` `proposed_action` 的 id。这是 engine
            反过来知道的东西——没有单独的制品 id。
        gateStatus:
          type: string
          enum:
            - awaiting_qa
            - awaiting_secops
            - awaiting_user
          example: awaiting_qa
          description: 经过与 dev PR 相同的 QA 和 SecOps gate。
        gateCorrectionCount:
          type: number
          example: 0
        blocked:
          type: boolean
          example: false
        blockedReason:
          type: object
          example: null
          nullable: true
        createdAt:
          type: string
          example: '2026-07-26T09:00:00.000Z'
          format: date-time
        updatedAt:
          type: string
          example: '2026-07-26T09:20:00.000Z'
          format: date-time
      required:
        - id
        - projectId
        - sessionId
        - title
        - prActionId
        - gateStatus
        - gateCorrectionCount
        - blocked
        - blockedReason
        - createdAt
        - updatedAt
    InfraContextAdrResponseDto:
      type: object
      properties:
        title:
          type: string
          example: 0025 — Kubernetes deploy with Kustomize
        content:
          type: string
          example: |-
            ## Context

            …
      required:
        - title
        - content
    InfraContextResponseDto:
      type: object
      properties:
        moduleMap:
          nullable: true
          type: object
          allOf:
            - $ref: '#/components/schemas/ModuleMapResponseDto'
        adrs:
          type: array
          items:
            $ref: '#/components/schemas/InfraContextAdrResponseDto'
        gitProvider:
          type: string
          enum:
            - local
            - github
            - gitlab
          nullable: true
          example: github
          description: >-
            项目尚未置备仓库时为 null。Workflows 子代理（Phase 8c）据此决定 CI 流水线格式：“gitlab” 生成
            .gitlab-ci.yml，其他任何值都生成 GitHub Actions。
        moduleRouting:
          description: >-
            由 Architect 路由的候选项（route_modules_to_infra，ADR 0131）——Infra Lead 在提议
            container_start 时从中选择（ADR 0130/0133）。Architect 尚未路由任何内容时为
            “sem_roteamento”。
          allOf:
            - $ref: '#/components/schemas/EstadoDoRoteamentoResponseDto'
        projectImage:
          nullable: true
          description: >-
            RN-852：当前的 artifact.project_image 决定（镜像、网络、资源）——Dockerfile 的 FROM 以及
            Infra compose 所遵循的网络。尚未决定镜像时为 null。
          type: object
          allOf:
            - $ref: '#/components/schemas/DecisaoDeImagemResponseDto'
      required:
        - moduleMap
        - adrs
        - gitProvider
        - moduleRouting
        - projectImage
    InfraGateVerdictResponseDto:
      type: object
      properties:
        nextAction:
          type: string
          enum:
            - correct
            - run_secops
            - done
            - blocked
          example: done
        artifact:
          $ref: '#/components/schemas/InfraArtifactResponseDto'
      required:
        - nextAction
        - artifact
    InfraPrFileResponseDto:
      type: object
      properties:
        path:
          type: string
          example: docker/api/Dockerfile.prod
        content:
          type: string
          example: |-
            FROM node:24.11.1-alpine3.21 AS build
            …
      required:
        - path
        - content
    InfraPrFilesResponseDto:
      type: object
      properties:
        title:
          type: string
          example: api's production Dockerfile
        files:
          description: 内容来自 `proposed_action` 自身的载荷：基础设施制品绝不接触 worktree，与 ADR 相同。
          type: array
          items:
            $ref: '#/components/schemas/InfraPrFileResponseDto'
      required:
        - title
        - files
    InstructionVersionResponseDto:
      type: object
      properties:
        id:
          type: string
          example: 01JC4Z0000VERSAO00000000001
        version:
          type: number
          example: 4
        content:
          type: string
          example: |-
            # dev-api

            Always run the suite before opening the PR.
        createdBy:
          type: object
          example: null
          nullable: true
        sourceActionId:
          type: object
          example: null
          nullable: true
          description: 产生此版本的补丁 `proposed_action`（如有）。
        sourceHypothesisId:
          type: object
          example: 01JC4Z0000HIPOTESE000000001
          nullable: true
          description: 引发该补丁的 Psychologist 假设——闭合的循环。
        note:
          type: object
          example: Explicit definition-of-done criterion
          nullable: true
        createdAt:
          type: string
          example: '2026-07-26T14:00:00.000Z'
          format: date-time
        isCurrent:
          type: boolean
          example: true
          description: 这是否是当前生效的版本。
        diff:
          description: 此版本与上一版本的 diff，由服务端计算，因此界面不需要自己的比较器。最旧的版本与空内容比较，所以显示为全部新增。
          allOf:
            - $ref: '#/components/schemas/TextDiffResponseDto'
      required:
        - id
        - version
        - content
        - createdBy
        - sourceActionId
        - sourceHypothesisId
        - note
        - createdAt
        - isCurrent
        - diff
    IssuePatRequestDto:
      type: object
      properties:
        name:
          type: string
          example: laptop
          description: 供你以后认出这个令牌的名称——不要求唯一。
        expiresInDays:
          type: number
          example: 90
          description: N 天后过期。省略 = 不过期（随时可撤销，但绝不会自行过期）。
      required:
        - name
    IssuePatResponseDto:
      type: object
      properties:
        id:
          type: string
          example: 01JC4Z0000PAT000000000001
        name:
          type: string
          example: laptop
        projectId:
          type: string
          example: 01JC4Z0000PROJETO000000001
        createdAt:
          type: string
          example: '2026-08-22T12:00:00.000Z'
          format: date-time
        expiresAt:
          type: object
          example: null
          nullable: true
          description: 为空 = 不过期。
        revokedAt:
          type: object
          example: null
          nullable: true
        lastUsedAt:
          type: object
          example: null
          nullable: true
          description: 为空 = 从未使用。
        token:
          type: string
          example: brb_9f8a...
          description: 现在就保存——之后无法找回。用于 --token 或 BRABO_ACCOUNT_TOKEN。
      required:
        - id
        - name
        - projectId
        - createdAt
        - expiresAt
        - revokedAt
        - lastUsedAt
        - token
    ItemDeContratoInternalDto:
      type: object
      properties:
        tipo:
          type: string
          enum:
            - funcao
            - rota
            - evento
            - dado
          example: funcao
          description: 另一个模块如何使用该条目：调用函数、访问路由、订阅事件，或构建数据结构。
        assinatura:
          type: string
          example: 'placePiece(board: Board, piece: Piece, pos: Pos): Board'
          description: 另一个模块如何使用它。最多 300 个字符。
        descricao:
          type: string
          example: Returns a new board; never mutates the one passed in.
      required:
        - tipo
        - assinatura
    ItemDeContratoResponseDto:
      type: object
      properties:
        tipo:
          type: string
          enum:
            - funcao
            - rota
            - evento
            - dado
          example: funcao
        assinatura:
          type: string
          example: 'placePiece(board: Board, piece: Piece, pos: Pos): Board'
        descricao:
          type: string
          example: Returns a new board; never mutates the one passed in.
      required:
        - tipo
        - assinatura
        - descricao
    ItemDeDecisaoResponseDto:
      type: object
      properties:
        eventId:
          type: string
          example: evt_01JC4Z
        seq:
          type: number
          example: 43
        actorId:
          type: string
          example: criativo
        escolha:
          type: string
          nullable: true
          example: SQLite em arquivo
        contexto:
          type: string
          nullable: true
      required:
        - eventId
        - seq
        - actorId
        - escolha
        - contexto
    ItemDeRegraResponseDto:
      type: object
      properties:
        eventId:
          type: string
          example: evt_01JC4Z
        seq:
          type: number
          example: 42
        actorId:
          type: string
          example: po
        titulo:
          type: string
          nullable: true
          example: Login exige e-mail
        descricao:
          type: string
          nullable: true
        origens:
          type: number
          example: 2
          description: 该规则引用了多少个来源参考。
      required:
        - eventId
        - seq
        - actorId
        - titulo
        - descricao
        - origens
    JwksResponseDto:
      type: object
      properties:
        keys:
          description: 活跃的 Ed25519 公钥。轮换期间有两个。
          type: array
          items:
            type: object
      required:
        - keys
    LastCallResponseDto:
      type: object
      properties:
        inputTokens:
          type: number
          example: 42000
          description: Input tokens of that call.
        modelName:
          type: string
          example: qwen2.5-coder:7b
        contextWindow:
          type: number
          example: 128000
          nullable: true
          description: The model's context window, or null when unknown.
      required:
        - inputTokens
        - modelName
        - contextWindow
    LlmMessageResponseDto:
      type: object
      properties:
        role:
          type: string
          example: assistant
          enum:
            - assistant
        content:
          type: string
          example: I'll read the file before answering.
        toolCalls:
          type: array
          items:
            $ref: '#/components/schemas/ToolCallResponseDto'
      required:
        - role
        - content
        - toolCalls
    LLMProviderCapabilitiesResponseDto:
      type: object
      properties:
        streaming:
          type: boolean
          example: true
        toolCalling:
          type: boolean
          example: true
        listModels:
          type: boolean
          example: true
          description: 提供商可以列出自己的目录（目录同步）。
        embeddings:
          type: boolean
          example: false
          description: 文本 → 向量（ADR 0075）。只有经过验证时才为 `true`。
        routingPreference:
          type: boolean
          example: false
          description: >-
            提供商接受路由偏好（`price`、`throughput`、`latency`），用于在服务同一模型的多个上游之间进行选择（ADR
            0166）。只有在针对真实 API 的冒烟测试返回了所选上游之后才为 `true`——阅读文档不算数。
        stickySession:
          type: boolean
          example: false
          description: >-
            The provider pins a conversation to the upstream that served it when
            given a session key (ADR 0221). Only `true` after a smoke against
            the real API showed one upstream with the key and several without
            it.
        priceCeiling:
          type: boolean
          example: false
          description: >-
            The provider accepts a per-call PRICE CEILING (`provider.max_price`
            = catalog price x 1.5, ADR 0225): only an upstream within it serves
            the call. Only `true` after a smoke against the real API showed the
            served upstream within the ceiling.
        promptCaching:
          type: boolean
          example: false
          description: >-
            提供商接受在调用的稳定前缀上设置显式的提示词缓存断点（`cache_control`）（RN-892）。只有在针对真实 API
            的冒烟测试显示重复调用中出现了缓存的输入 token 之后才为 `true`。
      required:
        - streaming
        - toolCalling
        - listModels
        - embeddings
        - routingPreference
        - stickySession
        - priceCeiling
        - promptCaching
    LlmTurnResponseDto:
      type: object
      properties:
        message:
          $ref: '#/components/schemas/LlmMessageResponseDto'
        usage:
          $ref: '#/components/schemas/LlmUsageResponseDto'
        error:
          type: object
          example: null
          nullable: true
          description: >-
            提供商的失败。以 200 在响应体中返回，而不是作为 HTTP 错误，因为 `usage`
            中的计费仍然有效——即使失败，该回合也产生了花费。
        errorCode:
          type: object
          example: null
          nullable: true
          description: >-
            提供商失败的规范化 `code`（ADR
            0041）——`auth`、`rate_limit`、`insufficient_credit`（HTTP
            402，RN-730）、`model_not_found`、`context_length`、`timeout`、`connection`
            或 `upstream`。没有错误，或错误不属于提供商（预算、缺少 binding）时为 `null`。请依据它做判断，绝不依据
            `error` 的文本。
        modelName:
          type: object
          example: llama3.2:3b
          nullable: true
          description: >-
            生成响应的模型名称（问题 2 的发现）——当回合在解析模型之前就失败时（没有 binding，或 binding 指向不存在的模型）为
            `null`。
        truncated:
          type: boolean
          enum:
            - true
          description: >-
            仅当提供商在输出上限处截断了响应时出现（始终为 `true`）（`finish_reason: "length"` /
            `stop_reason: "max_tokens"`，RN-737）：文本和 `toolCalls` 的参数都被截断。engine
            拒绝执行被截断的工具调用，并叙述这一截断。
        toolRouting:
          description: >-
            Jev 选择工具的那一步（ADR 0179，RN-625）。未咨询路由器时缺省（提供商不是
            OpenRouter、工具少于两个、workspace 开关关闭）。engine 将其叙述为
            `tool_router.decided`。
          allOf:
            - $ref: '#/components/schemas/ToolRoutingResponseDto'
      required:
        - message
        - usage
        - error
        - errorCode
        - modelName
    LlmTurnStreamEventResponseDto:
      type: object
      properties:
        type:
          type: string
          enum:
            - delta
            - done
            - error
          example: delta
          description: '`delta` 携带 `text`；`done` 以 `usage` 收尾；`error` 携带 `message`。'
        text:
          type: string
          example: I'll read the
        usage:
          $ref: '#/components/schemas/LlmUsageResponseDto'
        message:
          type: string
          example: provider unavailable
        modelName:
          type: object
          example: llama3.2:3b
          nullable: true
          description: 仅在 `done`/`final` 帧中——生成响应的模型名称（问题 2 的发现）。当回合在解析模型之前就失败时为 `null`。
        truncated:
          type: boolean
          enum:
            - true
          description: 仅在 `final` 帧中，且仅当提供商在输出上限处截断了响应时（RN-737）。
      required:
        - type
    LlmUsageResponseDto:
      type: object
      properties:
        inputTokens:
          type: number
          example: 1820
        outputTokens:
          type: number
          example: 340
        costMicros:
          type: number
          example: 52700
          description: >-
            成本，以微美元计：提供商返回真实成本时使用它（OpenRouter 的 `usage.cost`），否则使用冻结的目录价格（ADR
            0188，RN-665）。
        estimated:
          type: boolean
          example: false
          description: 当提供商没有返回计数、成本是估算出来的时候为 `true`。这个数字的价值较低，使用它的一方需要能够说明这一点。
      required:
        - inputTokens
        - outputTokens
        - costMicros
        - estimated
    LocalFolderFileDto:
      type: object
      properties:
        path:
          type: string
          example: src/domain/rag/chunking.ts
          description: 所选文件夹内的相对路径——绝不是绝对路径，绝不包含 `..`。
        content:
          type: string
          description: 纯 UTF-8 文本——绝不是 base64。
      required:
        - path
        - content
    LocalizacaoDoProjetoResponseDto:
      type: object
      properties:
        tipo:
          type: string
          enum:
            - gerenciada
            - montada
            - indisponivel
          example: gerenciada
          description: >-
            由 broker 的哪个根目录解析此文件夹。`gerenciada` →
            `PROJECT_WORKSPACES_HOST_ROOT`（产品管理的文件夹，由 `workspaceDirName`
            命名）；`montada` → `BRABO_PROJECTS_HOST_BASE`（Mounted 项目的唯一基目录，ADR
            0141）；`indisponivel` → 此服务器没有任何根目录能访问它，`motivo`
            说明原因。三种状态而不是两种：`runner` 项目和在基目录之外创建的旧版 `mounted`
            项目都是拒绝，但修复方式不同，把它们合并成一个 null 会让调用方去猜自己遇到的是哪一种。
        segmento:
          type: string
          example: loja
          description: >-
            broker 的根目录没有覆盖的部分，以 `<root>/<segmento>` 的形式拼接上去。对 `gerenciada` 而言是
            `workspaceDirName`；对 `montada` 而言，是基目录下的相对路径（可能包含 `/`）。缺省——而不是
            null——当 `tipo` 为 `indisponivel` 时：这两种变体在 api
            端是可辨识联合，线上传输也表达同样的意思，因此读取 `segmento` 却不检查 `tipo` 的调用方会明显地出错，而不是拼出
            `<root>/null`。
        motivo:
          type: string
          example: o projeto está no modo "runner"…
          description: >-
            为什么没有任何根目录能访问此文件夹。仅当 `tipo` 为 `indisponivel`
            时出现。这是一条要转述给运营者的消息，因此会指明模式或基目录。
      required:
        - tipo
    LoginDto:
      type: object
      properties:
        email:
          type: string
          example: fulano@brabo.dev
        senha:
          type: string
          example: correct horse battery staple
      required:
        - email
        - senha
    MachineDeviceKeyInternalDto:
      type: object
      properties:
        name:
          type: string
          example: servidor-de-casa
          description: 供人以后认出这台机器的名称，显示在它所服务的每个项目的设备密钥列表中。不要求唯一。安装器会建议主机名；api 不会从中推导任何东西。
        publicKeyJwk:
          type: string
          example: '{"kty":"OKP","crv":"Ed25519","x":"…"}'
          description: >-
            Ed25519 公钥 JWK（RFC 8037），序列化为 JSON。密钥对在机器上生成，私有的一半从不传输——携带 `d` 的 JWK
            会以 400 拒绝并说明原因，绝不存储。
      required:
        - name
        - publicKeyJwk
    MachineDeviceKeyInternalResponseDto:
      type: object
      properties:
        id:
          type: string
          format: uuid
          example: 01JC4Z0000CHAVE000000000001
          description: >-
            注册 id。在保存文件之前把它作为 `kid` 写入私有
            JWK（RN-475）：它是磁盘上的密钥与服务器上公开一半之间唯一的关联，下游的每一步都只是传递它。
        userId:
          type: string
          format: uuid
          example: 01JC4Z0000USUARIO0000000001
          description: api 解析出的所有者——该安装的唯一用户。调用方不选择也不发送它；此字段存在是为了让安装器能与刚创建的账户核对，而不是想当然。
        name:
          type: string
          example: servidor-de-casa
        createdAt:
          type: string
          example: '2026-09-12T12:00:00.000Z'
          format: date-time
        replacedKeyIds:
          example: []
          description: >-
            被这把密钥替换的机器密钥
            id——在同一事务中被撤销。为空是正常情况（全新安装）。非空表示仍持有其中某把密钥的本地代理将不再获得票据，安装器应当说明这一点，而不是让人自己发现。项目密钥（ADR
            0118）不受影响。
          type: array
          items:
            type: string
      required:
        - id
        - userId
        - name
        - createdAt
        - replacedKeyIds
    MarkTaskInternalDto:
      type: object
      properties:
        projectId:
          type: string
          format: uuid
          example: 01JC4Z0000PROJETO0000000001
        agentId:
          type: string
          example: dev-api
          description: 只有认领了任务的代理才能移动它。
        status:
          type: string
          enum:
            - todo
            - in_progress
            - in_review
            - done
          example: in_review
      required:
        - projectId
        - agentId
        - status
    MensagemAoAgenteResponseDto:
      type: object
      properties:
        ok:
          type: boolean
          example: true
          description: 始终为 `true`；失败会变成 HTTP 错误。
        mensagemId:
          type: string
          example: 01JC4Z0000EVENTO000000000001
          description: 为这条消息记录的 `chat.message` 事件的 id——在它于队列中等待时用来取消它的 id。
        entrega:
          type: string
          enum:
            - lida
            - enfileirada
          example: enfileirada
          description: >-
            `lida`：代理立即用它开始了一个回合。`enfileirada`：代理正处于回合中，因此消息进入了它的队列，会在当前回合结束时与其他排队的消息在一个回合中一起被读取（RN-673）。会话日志会记录
            `chat.message_queued` / `chat.message_delivered` /
            `chat.message_cancelled`。
        posicao:
          type: number
          example: 2
          description: 在队列中的位置（1 = 下一个）。仅当 `entrega` 为 `enfileirada` 时出现。
      required:
        - ok
        - mensagemId
        - entrega
    MirrorSyncResultInternalDto:
      type: object
      properties:
        ok:
          type: boolean
          example: true
          description: 复制的真实结果，由本地代理在完成后报告——绝不是在这一轮结束前乐观地报“ok”。
        destination:
          type: string
          example: /home/you/mirrors/store
          description: >-
            这一轮实际解析到的目标（成功时）或尝试使用的目标（失败时），位于用户的机器上。在记录中冻结：把项目当前的目标显示在昨天的复制旁边，等于界面在对一个那轮从未触及的文件夹做出断言。当失败与目标本身有关时省略。
        filesCopied:
          type: number
          example: 412
          description: 这一轮复制的普通文件数。`0` 是一个数字，而不是缺失——“已同步但什么也没复制”是一种独立的状态（RN-088）。
        filesSkipped:
          type: number
          example: 3
          description: >-
            git 列出的、不是普通文件的条目——嵌套仓库（dev agent 的 worktree
            就是一个）、符号链接，或在列出和复制之间消失的文件。
        filesRefused:
          type: number
          example: 0
          description: 被逐文件防护拒绝的目标（逃出目标目录的符号链接）。会被计数，绝不吞掉。
        error:
          type: string
          example: >-
            o espelho não conseguiu listar o trabalho com o git: not a git
            repository
          description: 具名的失败消息。很长时会截断存储，并说明已截断。它绝不会抹去最近一次成功的同步——哪条是当前的，由比较两个时间戳决定。
      required:
        - ok
    MirrorSyncResultResponseDto:
      type: object
      properties:
        recorded:
          type: boolean
          example: true
        status:
          type: string
          example: synced
          enum:
            - never
            - synced
            - failed
          description: >-
            此次写入后三种状态中哪一种是当前的。`never`
            在这里不可能出现（每次写入都会标记两个时间戳之一），列出它是因为词汇与界面读取的相同。
      required:
        - recorded
        - status
    ModelBindingResponseDto:
      type: object
      properties:
        id:
          type: string
          example: 01JC4Z0000BINDING00000000001
        scope:
          type: string
          enum:
            - workspace
            - project
            - area
            - agent
            - session
          example: project
        scopeId:
          type: string
          example: 01JC4Z0000PROJETO0000000001
          description: >-
            binding 所固定的 workspace、项目或会话的 id。在按项目存在的 `agent` 和 `area`
            范围中，它是复合的：`<projectId>:<agent slug|area key>`（ADR 0064）。
        modelId:
          type: string
          example: 01JC4Z0000MODELO00000000001
        routingPreference:
          type: string
          enum:
            - price
            - throughput
            - latency
          nullable: true
          example: null
          description: >-
            中转平台如何为此 binding 选择上游（ADR 0166，RN-583）。`null` = 由中转平台自行决定。只会为声明了
            `routingPreference` 能力的提供商设置。
        createdBy:
          type: string
          example: 01JC4Z0000USUARIO0000000001
        createdAt:
          type: string
          example: '2026-07-11T09:00:00.000Z'
          format: date-time
        updatedAt:
          type: string
          example: '2026-07-11T09:00:00.000Z'
          format: date-time
      required:
        - id
        - scope
        - scopeId
        - modelId
        - routingPreference
        - createdBy
        - createdAt
        - updatedAt
    ModelComCuradoriaResponseDto:
      type: object
      properties:
        id:
          type: string
          example: 01JC4Z0000MODELO00000000001
        provider:
          type: string
          enum:
            - ollama
            - anthropic
            - openai
          example: anthropic
        name:
          type: string
          example: claude-opus-4-8
          description: 在提供商处的标识符。
        displayName:
          type: string
          example: Claude Opus 4.8
        inputPricePerMillionMicros:
          type: number
          example: 15000000
          description: 每百万输入 token 的价格，以微美元计。
        outputPricePerMillionMicros:
          type: number
          example: 75000000
          description: 每百万输出 token 的价格，以微美元计。
        contextWindow:
          type: object
          example: 200000
          nullable: true
          description: 同时也是模型的 `context_length` 能力。
        supportsToolCalling:
          type: boolean
          example: true
          description: 原生工具调用。没有它，模型只能聊天，不能绑定到代理（RN-040）。
        supportsStreaming:
          type: boolean
          example: true
        supportsVision:
          type: boolean
          example: false
          description: 接受图像输入。`false` 表示提供商没有声明该模态——而不是模型做不到（ADR 0041）。
        supportsReasoning:
          type: boolean
          example: true
          description: '接受显式推理（thinking）。在 OpenRouter 上来自 `supported_parameters: reasoning`。'
        generatesImage:
          type: boolean
          example: false
          description: 生成图像——与接受图像输入是不同的维度。具备的模型很少，把两者混淆会让用户选错模型。
        reasoningBudget:
          type: number
          nullable: true
          example: 2048
          description: >-
            Reasoning budget (tokens) the model is PROVEN to respect, curated by
            hand (ADR 0223). Null = not proven; the field is not sent.
        manualPricing:
          type: boolean
          example: true
          description: 根据提供商文档手动输入的价格，而不是同步而来。价格同步在没有明确决定的情况下不会覆盖被标记的记录。
        availability:
          type: string
          enum:
            - available
            - unavailable
          example: available
          description: >-
            同步观测到的远程现实，与策展相互独立的维度。`unavailable` 是从提供商目录中消失的模型——它绝不会被删除，因为
            binding 和成本历史都指向它。
        billing:
          type: string
          enum:
            - api
            - subscription
            - free
          example: api
          description: >-
            CURATED billing mark (AT-632, RN-954): `subscription` is a seat —
            its cost is shown apart from the US$ paid. Never set by sync.
        lastSeenAt:
          type: object
          example: '2026-07-30T03:00:00.000Z'
          format: date-time
          nullable: true
          description: 同步最后一次在提供商目录中看到该模型的时间。
        createdAt:
          type: string
          example: '2026-07-01T00:00:00.000Z'
          format: date-time
        updatedAt:
          type: string
          example: '2026-07-01T00:00:00.000Z'
          format: date-time
        isActive:
          type: boolean
          example: true
          description: >-
            **此 workspace**
            所有者的策展：被停用的模型会从选择器中消失，但保留在历史成本中。同步发现的模型没有策展记录，没有记录即表示 `false`。
        uses:
          type: array
          example:
            - codigo
          description: >-
            此 workspace
            将该模型用于什么。是运营者的意见，而不是提供商的能力——没有任何目录会公布“适合写代码”。空列表表示“没有人有意见”，而不是“不适合”。
          items:
            type: string
            enum:
              - codigo
              - documentacao
              - analise
              - imagem
              - conversa
        freeRoutingAlias:
          type: boolean
          example: false
          description: >-
            OpenRouter 免费路由别名（以 `~` 开头的
            id）：目录价格只是展示价，账单来自实际处理调用的那个上游。策展拒绝激活它（422
            `alias_de_roteamento_livre`）；在该规则之前激活的保持激活，并以此标志标记，一旦停用就无法再恢复。读取时根据提供商和名称推导，从不存储。
      required:
        - id
        - provider
        - name
        - displayName
        - inputPricePerMillionMicros
        - outputPricePerMillionMicros
        - contextWindow
        - supportsToolCalling
        - supportsStreaming
        - supportsVision
        - supportsReasoning
        - generatesImage
        - reasoningBudget
        - manualPricing
        - availability
        - billing
        - lastSeenAt
        - createdAt
        - updatedAt
        - isActive
        - uses
        - freeRoutingAlias
    ModeloDoCatalogoResponseDto:
      type: object
      properties:
        id:
          type: string
          example: 01JC4Z0000MODELO00000000001
        provider:
          type: string
          enum:
            - ollama
            - anthropic
            - openai
          example: anthropic
        name:
          type: string
          example: claude-opus-4-8
          description: 在提供商处的标识符。
        displayName:
          type: string
          example: Claude Opus 4.8
        inputPricePerMillionMicros:
          type: number
          example: 15000000
          description: 每百万输入 token 的价格，以微美元计。
        outputPricePerMillionMicros:
          type: number
          example: 75000000
          description: 每百万输出 token 的价格，以微美元计。
        contextWindow:
          type: object
          example: 200000
          nullable: true
          description: 同时也是模型的 `context_length` 能力。
        supportsToolCalling:
          type: boolean
          example: true
          description: 原生工具调用。没有它，模型只能聊天，不能绑定到代理（RN-040）。
        supportsStreaming:
          type: boolean
          example: true
        supportsVision:
          type: boolean
          example: false
          description: 接受图像输入。`false` 表示提供商没有声明该模态——而不是模型做不到（ADR 0041）。
        supportsReasoning:
          type: boolean
          example: true
          description: '接受显式推理（thinking）。在 OpenRouter 上来自 `supported_parameters: reasoning`。'
        generatesImage:
          type: boolean
          example: false
          description: 生成图像——与接受图像输入是不同的维度。具备的模型很少，把两者混淆会让用户选错模型。
        reasoningBudget:
          type: number
          nullable: true
          example: 2048
          description: >-
            Reasoning budget (tokens) the model is PROVEN to respect, curated by
            hand (ADR 0223). Null = not proven; the field is not sent.
        manualPricing:
          type: boolean
          example: true
          description: 根据提供商文档手动输入的价格，而不是同步而来。价格同步在没有明确决定的情况下不会覆盖被标记的记录。
        availability:
          type: string
          enum:
            - available
            - unavailable
          example: available
          description: >-
            同步观测到的远程现实，与策展相互独立的维度。`unavailable` 是从提供商目录中消失的模型——它绝不会被删除，因为
            binding 和成本历史都指向它。
        billing:
          type: string
          enum:
            - api
            - subscription
            - free
          example: api
          description: >-
            CURATED billing mark (AT-632, RN-954): `subscription` is a seat —
            its cost is shown apart from the US$ paid. Never set by sync.
        lastSeenAt:
          type: object
          example: '2026-07-30T03:00:00.000Z'
          format: date-time
          nullable: true
          description: 同步最后一次在提供商目录中看到该模型的时间。
        createdAt:
          type: string
          example: '2026-07-01T00:00:00.000Z'
          format: date-time
        updatedAt:
          type: string
          example: '2026-07-01T00:00:00.000Z'
          format: date-time
        isActive:
          type: boolean
          example: true
          description: >-
            **此 workspace**
            所有者的策展：被停用的模型会从选择器中消失，但保留在历史成本中。同步发现的模型没有策展记录，没有记录即表示 `false`。
        uses:
          type: array
          example:
            - codigo
          description: >-
            此 workspace
            将该模型用于什么。是运营者的意见，而不是提供商的能力——没有任何目录会公布“适合写代码”。空列表表示“没有人有意见”，而不是“不适合”。
          items:
            type: string
            enum:
              - codigo
              - documentacao
              - analise
              - imagem
              - conversa
        freeRoutingAlias:
          type: boolean
          example: false
          description: >-
            OpenRouter 免费路由别名（以 `~` 开头的
            id）：目录价格只是展示价，账单来自实际处理调用的那个上游。策展拒绝激活它（422
            `alias_de_roteamento_livre`）；在该规则之前激活的保持激活，并以此标志标记，一旦停用就无法再恢复。读取时根据提供商和名称推导，从不存储。
        sugestao:
          nullable: true
          description: >-
            SUGGESTION only (derived on read, never stored): billing band from
            the 9Router alias and the manufacturer price from a curated table.
            Applying it is a curation gesture. Null for OpenRouter and unlisted
            ids.
          type: object
          allOf:
            - $ref: '#/components/schemas/SugestaoDoCatalogoResponseDto'
      required:
        - id
        - provider
        - name
        - displayName
        - inputPricePerMillionMicros
        - outputPricePerMillionMicros
        - contextWindow
        - supportsToolCalling
        - supportsStreaming
        - supportsVision
        - supportsReasoning
        - generatesImage
        - reasoningBudget
        - manualPricing
        - availability
        - billing
        - lastSeenAt
        - createdAt
        - updatedAt
        - isActive
        - uses
        - freeRoutingAlias
        - sugestao
    ModelPriceChangeResponseDto:
      type: object
      properties:
        id:
          type: string
          example: 01JC4Z0000PRECO000000000001
        modelId:
          type: string
          example: 01JC4Z0000MODELO00000000001
        inputBeforeMicros:
          type: number
          example: 2500000
          description: 每百万的微美元数，变更前。
        inputAfterMicros:
          type: number
          example: 3000000
          description: 每百万的微美元数，变更后。
        outputBeforeMicros:
          type: number
          example: 10000000
        outputAfterMicros:
          type: number
          example: 12000000
        source:
          type: string
          enum:
            - manual
            - sync
          example: manual
          description: '`manual` 是有人根据提供商文档手动输入；`sync` 来自远程目录。'
        changedBy:
          type: object
          example: 01JC4Z0000USUARIO0000000001
          nullable: true
          description: 来自同步时为 `null`——背后没有人。
        createdAt:
          type: string
          example: '2026-08-01T12:00:00.000Z'
          format: date-time
      required:
        - id
        - modelId
        - inputBeforeMicros
        - inputAfterMicros
        - outputBeforeMicros
        - outputAfterMicros
        - source
        - changedBy
        - createdAt
    ModelPullRequestResponseDto:
      type: object
      properties:
        id:
          type: string
          example: 9b1c2d3e-4f50-4a61-8b72-0c3d4e5f6a7b
        workspaceId:
          type: string
          example: 9b1c2d3e-4f50-4a61-8b72-0c3d4e5f6a7b
        requestedBy:
          type: string
          example: 9b1c2d3e-4f50-4a61-8b72-0c3d4e5f6a7b
        repoId:
          type: string
          example: meta-llama/Llama-3.1-8B-Instruct-GGUF
        estimatedSizeBytes:
          type: object
          example: null
          nullable: true
          description: 仅当 Hub 在搜索中公布了 `usedStorage` 时出现——绝不凭猜测估算。
        status:
          type: string
          enum:
            - pending_confirmation
            - confirmed
            - pulling
            - active
            - failed
          example: pending_confirmation
          description: >-
            `pending_confirmation` → `confirmed` → `pulling` → `active` |
            `failed`。前两个状态就是产品在任何下载运行之前要求的第二次显式确认。
        confirmedAt:
          type: object
          example: null
          nullable: true
          format: date-time
          description: 确认点击发生的时间。
        failedReason:
          type: object
          example: null
          nullable: true
          description: 仅在 `failed` 时，以来源为前缀（infra | 模型 | 代码 | 策略）——绝不静默失败。
        createdAt:
          type: string
          example: '2026-08-26T12:00:00.000Z'
          format: date-time
        updatedAt:
          type: string
          example: '2026-08-26T12:00:00.000Z'
          format: date-time
      required:
        - id
        - workspaceId
        - requestedBy
        - repoId
        - estimatedSizeBytes
        - status
        - confirmedAt
        - failedReason
        - createdAt
        - updatedAt
    ModelResponseDto:
      type: object
      properties:
        id:
          type: string
          example: 01JC4Z0000MODELO00000000001
        provider:
          type: string
          enum:
            - ollama
            - anthropic
            - openai
          example: anthropic
        name:
          type: string
          example: claude-opus-4-8
          description: 在提供商处的标识符。
        displayName:
          type: string
          example: Claude Opus 4.8
        inputPricePerMillionMicros:
          type: number
          example: 15000000
          description: 每百万输入 token 的价格，以微美元计。
        outputPricePerMillionMicros:
          type: number
          example: 75000000
          description: 每百万输出 token 的价格，以微美元计。
        contextWindow:
          type: object
          example: 200000
          nullable: true
          description: 同时也是模型的 `context_length` 能力。
        supportsToolCalling:
          type: boolean
          example: true
          description: 原生工具调用。没有它，模型只能聊天，不能绑定到代理（RN-040）。
        supportsStreaming:
          type: boolean
          example: true
        supportsVision:
          type: boolean
          example: false
          description: 接受图像输入。`false` 表示提供商没有声明该模态——而不是模型做不到（ADR 0041）。
        supportsReasoning:
          type: boolean
          example: true
          description: '接受显式推理（thinking）。在 OpenRouter 上来自 `supported_parameters: reasoning`。'
        generatesImage:
          type: boolean
          example: false
          description: 生成图像——与接受图像输入是不同的维度。具备的模型很少，把两者混淆会让用户选错模型。
        reasoningBudget:
          type: number
          nullable: true
          example: 2048
          description: >-
            Reasoning budget (tokens) the model is PROVEN to respect, curated by
            hand (ADR 0223). Null = not proven; the field is not sent.
        manualPricing:
          type: boolean
          example: true
          description: 根据提供商文档手动输入的价格，而不是同步而来。价格同步在没有明确决定的情况下不会覆盖被标记的记录。
        availability:
          type: string
          enum:
            - available
            - unavailable
          example: available
          description: >-
            同步观测到的远程现实，与策展相互独立的维度。`unavailable` 是从提供商目录中消失的模型——它绝不会被删除，因为
            binding 和成本历史都指向它。
        billing:
          type: string
          enum:
            - api
            - subscription
            - free
          example: api
          description: >-
            CURATED billing mark (AT-632, RN-954): `subscription` is a seat —
            its cost is shown apart from the US$ paid. Never set by sync.
        lastSeenAt:
          type: object
          example: '2026-07-30T03:00:00.000Z'
          format: date-time
          nullable: true
          description: 同步最后一次在提供商目录中看到该模型的时间。
        createdAt:
          type: string
          example: '2026-07-01T00:00:00.000Z'
          format: date-time
        updatedAt:
          type: string
          example: '2026-07-01T00:00:00.000Z'
          format: date-time
      required:
        - id
        - provider
        - name
        - displayName
        - inputPricePerMillionMicros
        - outputPricePerMillionMicros
        - contextWindow
        - supportsToolCalling
        - supportsStreaming
        - supportsVision
        - supportsReasoning
        - generatesImage
        - reasoningBudget
        - manualPricing
        - availability
        - billing
        - lastSeenAt
        - createdAt
        - updatedAt
    ModuleMapResponseDto:
      type: object
      properties:
        id:
          type: string
          example: 01JC4Z0000MODULEMAP00000001
        projectId:
          type: string
          example: 01JC4Z0000PROJETO0000000001
        sessionId:
          type: string
          example: 01JC4Z8QK3M7YV2N5T9B0PXHRA
        modules:
          type: array
          items:
            $ref: '#/components/schemas/ModuleNodeResponseDto'
        version:
          type: number
          example: 3
          description: 历史不可变：每个新映射都是新增一个版本，当前版本是 `version` 最高的那个。
        createdAt:
          type: string
          example: '2026-07-24T16:00:00.000Z'
          format: date-time
      required:
        - id
        - projectId
        - sessionId
        - modules
        - version
        - createdAt
    ModuleNodeResponseDto:
      type: object
      properties:
        name:
          type: string
          example: api
        stack:
          type: string
          example: NestJS + Drizzle
        responsibility:
          type: string
          example: Business rules and HTTP surface.
        dependsOn:
          example:
            - db
          description: 其他模块的名称。这里出现环会让映射在写入时被拒绝。
          type: array
          items:
            type: string
        resources:
          description: >-
            此模块单独在项目容器内所需的资源（RN-683，ADR 0199）。Architect 未声明时缺省。Infra
            以所有模块的总和启动容器，只要有任何模块未声明，就以默认值为下限。
          allOf:
            - $ref: '#/components/schemas/RecursosDoContainerResponseDto'
      required:
        - name
        - stack
        - responsibility
        - dependsOn
    MySpendResponseDto:
      type: object
      properties:
        projectId:
          type: string
          example: 9b1c2d3e-4f50-4a61-8b72-0c3d4e5f6a7b
          format: uuid
        dias:
          type: number
          example: 30
        actorId:
          type: string
          example: 9b1c2d3e-4f50-4a61-8b72-0c3d4e5f6a7b
          format: uuid
          description: 报告的参与者——始终是调用者本人。其他参与者的记录不会进来。
        totalMicros:
          type: number
          example: 1250000
        assinaturaMicros:
          type: number
          example: 0
          description: >-
            Part of the cost from models curated as `subscription` (RN-954): a
            seat, shown apart from the US$ paid (`cost - subscription`).
        inputTokens:
          type: number
          example: 120000
        outputTokens:
          type: number
          example: 35000
        chamadas:
          type: number
          example: 42
        porSessao:
          type: array
          items:
            $ref: '#/components/schemas/SpendLinhaResponseDto'
        porDia:
          type: array
          items:
            $ref: '#/components/schemas/SpendPorDiaResponseDto'
      required:
        - projectId
        - dias
        - actorId
        - totalMicros
        - assinaturaMicros
        - inputTokens
        - outputTokens
        - chamadas
        - porSessao
        - porDia
    MyWorkspaceSpendResponseDto:
      type: object
      properties:
        workspaceId:
          type: string
          format: uuid
        dias:
          type: number
          example: 30
        actorId:
          type: string
          format: uuid
          description: Always whoever called.
        totalMicros:
          type: number
          example: 1250000
        assinaturaMicros:
          type: number
          example: 0
          description: Seat part of the total (RN-954).
        inputTokens:
          type: number
          example: 120000
        outputTokens:
          type: number
          example: 35000
        chamadas:
          type: number
          example: 42
        porProjeto:
          description: By PROJECT.
          type: array
          items:
            $ref: '#/components/schemas/SpendLinhaResponseDto'
        porDia:
          type: array
          items:
            $ref: '#/components/schemas/SpendPorDiaResponseDto'
      required:
        - workspaceId
        - dias
        - actorId
        - totalMicros
        - assinaturaMicros
        - inputTokens
        - outputTokens
        - chamadas
        - porProjeto
        - porDia
    ObservacaoDeContainerResponseDto:
      type: object
      properties:
        containerId:
          type: string
          example: c0ffeebabe...
        nome:
          type: string
          example: brabo-exp002-f52be111
        estado:
          type: string
          enum:
            - created
            - running
            - paused
            - restarting
            - removing
            - exited
            - dead
          example: running
          description: 守护进程自身的状态，不做翻译也不做合并。
        imagem:
          type: string
          example: node:22-bookworm-slim
        iniciadoEm:
          type: object
          nullable: true
          format: date-time
          description: 容器从未启动过时为 `null`。
      required:
        - containerId
        - nome
        - estado
        - imagem
        - iniciadoEm
    OfertaDeHandoffResponseDto:
      type: object
      properties:
        id:
          type: string
          example: 01JC4Z0000HANDOFF00000000001
        sessionId:
          type: string
          example: 01JC4Z8QK3M7YV2N5T9B0PXHRA
        projectId:
          type: string
          example: 01JC4Z0000PROJETO0000000001
        fromAgent:
          type: string
          example: criativo
          description: 交出接力棒的代理的 slug。
        toAgent:
          type: string
          example: po
          description: 接收代理的 slug。
        artifactId:
          type: object
          example: 01JC4Z0000ARTEFATO000000001
          nullable: true
          description: 引发该 handoff 的制品（product_brief、module_map……）。
        status:
          type: string
          enum:
            - offered
            - accepted
            - completed
            - rejected
            - superseded
          example: offered
          description: >-
            此字段是可变的——它是当前状态。每次状态转换也会在日志中成为一个不可变的 `handoff.*`
            事件，历史就存放在那里。`superseded`（ADR
            0182，RN-635）：该提议不再是当前的——目标代理已通过其他路径被激活，或项目中给同一目标的更新提议取代了它（`handoff.superseded`）。只有
            `offered` 可以被接受。
        createdAt:
          type: string
          example: '2026-07-24T10:00:00.000Z'
          format: date-time
        updatedAt:
          type: string
          example: '2026-07-24T10:05:00.000Z'
          format: date-time
        desfecho:
          type: string
          enum:
            - criado
            - substituiu_oferta
            - ja_oferecido
          example: criado
          description: >-
            `criado`：新提议。`substituiu_oferta`：新提议，且给同一目标的待处理提议变为
            `superseded`。`ja_oferecido`：没有新记录——返回已存在的待处理提议（同一会话且没有新制品，或
            `seAusente`）。
      required:
        - id
        - sessionId
        - projectId
        - fromAgent
        - toAgent
        - artifactId
        - status
        - createdAt
        - updatedAt
        - desfecho
    OfertaInternaDeHandoffResponseDto:
      type: object
      properties:
        id:
          type: string
          example: 01JC4Z0000HANDOFF00000000001
        sessionId:
          type: string
          example: 01JC4Z8QK3M7YV2N5T9B0PXHRA
        projectId:
          type: string
          example: 01JC4Z0000PROJETO0000000001
        fromAgent:
          type: string
          example: criativo
          description: 交出接力棒的代理的 slug。
        toAgent:
          type: string
          example: po
          description: 接收代理的 slug。
        artifactId:
          type: object
          example: 01JC4Z0000ARTEFATO000000001
          nullable: true
          description: 引发该 handoff 的制品（product_brief、module_map……）。
        status:
          type: string
          enum:
            - offered
            - accepted
            - completed
            - rejected
            - superseded
          example: offered
          description: >-
            此字段是可变的——它是当前状态。每次状态转换也会在日志中成为一个不可变的 `handoff.*`
            事件，历史就存放在那里。`superseded`（ADR
            0182，RN-635）：该提议不再是当前的——目标代理已通过其他路径被激活，或项目中给同一目标的更新提议取代了它（`handoff.superseded`）。只有
            `offered` 可以被接受。
        createdAt:
          type: string
          example: '2026-07-24T10:00:00.000Z'
          format: date-time
        updatedAt:
          type: string
          example: '2026-07-24T10:05:00.000Z'
          format: date-time
        desfecho:
          type: string
          enum:
            - criado
            - substituiu_oferta
            - ja_oferecido
          example: criado
          description: >-
            `criado`：新提议。`substituiu_oferta`：新提议，且给同一目标的待处理提议变为
            `superseded`。`ja_oferecido`：没有新记录——返回已存在的待处理提议（同一会话且没有新制品，或
            `seAusente`）。
        aceiteAutomatico:
          $ref: '#/components/schemas/AceiteAutomaticoResponseDto'
      required:
        - id
        - sessionId
        - projectId
        - fromAgent
        - toAgent
        - artifactId
        - status
        - createdAt
        - updatedAt
        - desfecho
        - aceiteAutomatico
    OkResponseDto:
      type: object
      properties:
        ok:
          type: boolean
          example: true
          description: 始终为 `true`；失败会变成 HTTP 错误。
      required:
        - ok
    OpenGateInternalDto:
      type: object
      properties:
        projectId:
          type: string
          format: uuid
          example: 01JC4Z0000PROJETO0000000001
        agentId:
          type: string
          example: dev-api
      required:
        - projectId
        - agentId
    OpenQuestionAffectsDto:
      type: object
      properties:
        regras:
          type: array
          items:
            type: string
        historias:
          type: array
          items:
            type: string
        tarefas:
          type: array
          items:
            type: string
      required:
        - regras
        - historias
        - tarefas
    OpenQuestionAnswerDto:
      type: object
      properties:
        texto:
          type: string
        aceitouSugestao:
          type: boolean
        decisionEventId:
          type: string
        respondidaPor:
          type: string
        respondidaEm:
          type: string
          format: date-time
      required:
        - texto
        - aceitouSugestao
        - decisionEventId
        - respondidaPor
        - respondidaEm
    OpenQuestionResponseDto:
      type: object
      properties:
        questionId:
          type: string
          format: uuid
        eventId:
          type: string
        sessionId:
          type: string
        pergunta:
          type: string
        sugestao:
          type: string
          nullable: true
        afeta:
          $ref: '#/components/schemas/OpenQuestionAffectsDto'
        autor:
          type: string
          example: criativo
          description: 提问的代理。
        criadaEm:
          type: string
          format: date-time
        status:
          type: string
          enum:
            - open
            - answered
        autorDaSessao:
          type: string
          nullable: true
          description: 创建来源会话的用户——唯一可以回答的人。
        resposta:
          nullable: true
          type: object
          allOf:
            - $ref: '#/components/schemas/OpenQuestionAnswerDto'
      required:
        - questionId
        - eventId
        - sessionId
        - pergunta
        - sugestao
        - afeta
        - autor
        - criadaEm
        - status
        - autorDaSessao
        - resposta
    OpenQuestionsListResponseDto:
      type: object
      properties:
        itens:
          type: array
          items:
            $ref: '#/components/schemas/OpenQuestionResponseDto'
        total:
          type: number
          description: 符合过滤条件的总数；`itens` 上限为 50（RN-180）。
      required:
        - itens
        - total
    PaginaDeAcoesResponseDto:
      type: object
      properties:
        items:
          type: array
          items:
            $ref: '#/components/schemas/ProposedActionResponseDto'
        nextCursor:
          type: object
          example: 7
          nullable: true
          description: 在下一页作为 `afterSeq` 传入；`null` 表示列表结束。
      required:
        - items
        - nextCursor
    PaginaDeEventosResponseDto:
      type: object
      properties:
        items:
          type: array
          items:
            $ref: '#/components/schemas/SessionEventResponseDto'
        nextCursor:
          type: object
          example: 41
          nullable: true
          description: 在下一页作为 `afterSeq` 传入。`null` 表示日志结束。
      required:
        - items
        - nextCursor
    PassoDeBootstrapResponseDto:
      type: object
      properties:
        step:
          type: string
          enum:
            - commit_pr_template
            - commit_branching_policy
            - commit_gitignore
            - create_dev_branch
            - create_qa_branch
            - create_rc_branch
            - protect_branches
          example: create_dev_branch
        status:
          type: string
          enum:
            - pending
            - running
            - done
            - failed
          example: running
      required:
        - step
        - status
    PedidoDeParalelismoResponseDto:
      type: object
      properties:
        estado:
          type: string
          enum:
            - executado
            - aguardando_autorizacao
            - recusado
          example: aguardando_autorizacao
          description: >-
            `executado`：代理已在 lead 的上限内启动。`aguardando_autorizacao`：请求变成了
            `proposed_action`，在你决定之前什么都不会启动。`recusado`：该区域的 `max_parallel` 无效。
        actionId:
          type: string
          example: 01JC4Z8QK3M7YV2N5T9B0PXHRA
          description: 需要决定的操作。仅在 `aguardando_autorizacao` 中。
        ativosNaSessao:
          type: number
          example: 2
          description: 会话中已在运行的 dev agent，所有模块求和。
        maxParallel:
          type: number
          example: 2
          description: lead 无需询问即可使用的上限。
        motivo:
          type: string
          description: 请求被拒绝的原因。仅在 `recusado` 中。
      required:
        - estado
    PerfilApagadoResponseDto:
      type: object
      properties:
        deleted:
          type: number
          example: 2
          description: 删除了多少个画像。
        optedOut:
          type: boolean
          example: true
          description: 始终为 `true`：删除而不记录选择退出只是装样子，因为下一轮会重新推导一切。
      required:
        - deleted
        - optedOut
    PerfilOptInResponseDto:
      type: object
      properties:
        optedOut:
          type: boolean
          example: false
      required:
        - optedOut
    PermissionsFileResponseDto:
      type: object
      properties:
        allow:
          example:
            - Terminal(pnpm test:*)
            - Terminal(pnpm lint)
          description: 自动批准的模式。
          type: array
          items:
            type: string
        deny:
          example:
            - Terminal(rm -rf *)
            - Terminal(git push --force*)
          description: 禁止的模式。`deny` 优先于 `allow` 和代理的自主权——没有任何配置组合能放行这里列出的内容。
          type: array
          items:
            type: string
        ask:
          example:
            - Terminal(pnpm add *)
          description: 始终需要人工决定的模式。
          type: array
          items:
            type: string
      required:
        - allow
        - deny
        - ask
    PersonalAccessTokenAdminResponseDto:
      type: object
      properties:
        id:
          type: string
          example: 01JC4Z0000PAT000000000001
        name:
          type: string
          example: laptop
        projectId:
          type: string
          example: 01JC4Z0000PROJETO000000001
        createdAt:
          type: string
          example: '2026-08-22T12:00:00.000Z'
          format: date-time
        expiresAt:
          type: object
          example: null
          nullable: true
          description: 为空 = 不过期。
        revokedAt:
          type: object
          example: null
          nullable: true
        lastUsedAt:
          type: object
          example: null
          nullable: true
          description: 为空 = 从未使用。
        userId:
          type: string
          example: 01JC4Z0000USUARIO000000001
        userEmail:
          type: string
          example: dev@exemplo.com
      required:
        - id
        - name
        - projectId
        - createdAt
        - expiresAt
        - revokedAt
        - lastUsedAt
        - userId
        - userEmail
    PersonalAccessTokenResponseDto:
      type: object
      properties:
        id:
          type: string
          example: 01JC4Z0000PAT000000000001
        name:
          type: string
          example: laptop
        projectId:
          type: string
          example: 01JC4Z0000PROJETO000000001
        createdAt:
          type: string
          example: '2026-08-22T12:00:00.000Z'
          format: date-time
        expiresAt:
          type: object
          example: null
          nullable: true
          description: 为空 = 不过期。
        revokedAt:
          type: object
          example: null
          nullable: true
        lastUsedAt:
          type: object
          example: null
          nullable: true
          description: 为空 = 从未使用。
      required:
        - id
        - name
        - projectId
        - createdAt
        - expiresAt
        - revokedAt
        - lastUsedAt
    PortaDto:
      type: object
      properties:
        host:
          type: number
          example: 8080
        container:
          type: number
          example: 3000
        protocol:
          type: string
          example: tcp
      required:
        - host
        - container
        - protocol
    PrecoSugeridoResponseDto:
      type: object
      properties:
        fabricante:
          type: string
          example: Anthropic
        entradaMicros:
          type: number
          example: 100000
          description: micro-USD per 1M tokens.
        saidaMicros:
          type: number
          example: 500000
          description: micro-USD per 1M tokens.
        fonte:
          type: string
          example: https://docs.claude.com/en/docs/about-claude/pricing.md
        conferidoEm:
          type: string
          example: '2026-11-01'
          description: Date the row was checked.
        observacao:
          type: string
          example: Prompt up to 100,000 tokens.
      required:
        - fabricante
        - entradaMicros
        - saidaMicros
        - fonte
        - conferidoEm
    PriorHypothesisResponseDto:
      type: object
      properties:
        agenteAlvo:
          type: string
          example: dev-api
        hipotese:
          type: string
          example: The instructions don't say when the task is done.
        sugestao:
          type: string
          example: Add an explicit definition-of-done criterion.
        confiancaPercent:
          type: number
          example: 72
      required:
        - agenteAlvo
        - hipotese
        - sugestao
        - confiancaPercent
    ProductMetricsDeploymentDayResponseDto:
      type: object
      properties:
        dia:
          type: string
          example: '2026-08-01'
        merges:
          type: number
          example: 2
      required:
        - dia
        - merges
    ProductMetricsFunnelResponseDto:
      type: object
      properties:
        etapas:
          type: array
          items:
            $ref: '#/components/schemas/ProductMetricsFunnelStageResponseDto'
        sessoesComCommit:
          type: array
          items:
            type: string
        sessoesComPr:
          type: array
          items:
            type: string
        sessoesComMerge:
          type: array
          items:
            type: string
      required:
        - etapas
        - sessoesComCommit
        - sessoesComPr
        - sessoesComMerge
    ProductMetricsFunnelStageResponseDto:
      type: object
      properties:
        etapa:
          type: string
          example: sessão produziu commit
        sessoes:
          type: number
          example: 3
        taxaDaEtapaAnterior:
          type: object
          example: 0.67
          nullable: true
          description: 第一个阶段为 `null`——不存在从任何东西“转化而来”。
      required:
        - etapa
        - sessoes
        - taxaDaEtapaAnterior
    ProductMetricsLeadTimeResponseDto:
      type: object
      properties:
        sessionId:
          type: string
          example: 01JC4Z0000SESSAO000000000001
        primeiroCommitEm:
          type: string
          example: '2026-08-01T10:00:00.000Z'
        primeiroMergeEm:
          type: string
          example: '2026-08-01T12:00:00.000Z'
        leadTimeMs:
          type: number
          example: 7200000
      required:
        - sessionId
        - primeiroCommitEm
        - primeiroMergeEm
        - leadTimeMs
    ProductMetricsLeadTimesResponseDto:
      type: object
      properties:
        perSession:
          type: array
          items:
            $ref: '#/components/schemas/ProductMetricsLeadTimeResponseDto'
        averageMs:
          type: object
          example: 7200000
          nullable: true
          description: 简单平均值，以毫秒计——没有任何会话同时具有提交和合并时为 `null`。
      required:
        - perSession
        - averageMs
    ProductMetricsProjectResponseDto:
      type: object
      properties:
        id:
          type: string
          format: uuid
          example: 01JC4Z0000PROJETO0000000001
        name:
          type: string
          example: exp003
      required:
        - id
        - name
    ProductMetricsResponseDto:
      type: object
      properties:
        project:
          $ref: '#/components/schemas/ProductMetricsProjectResponseDto'
        totalActionsConsidered:
          type: number
          example: 18
          description: 纳入统计的 git 操作（git_commit/pr_open/git_merge，任意状态）。
        funnel:
          $ref: '#/components/schemas/ProductMetricsFunnelResponseDto'
        leadTimes:
          $ref: '#/components/schemas/ProductMetricsLeadTimesResponseDto'
        deploymentFrequency:
          type: array
          items:
            $ref: '#/components/schemas/ProductMetricsDeploymentDayResponseDto'
      required:
        - project
        - totalActionsConsidered
        - funnel
        - leadTimes
        - deploymentFrequency
    ProficiencyDraftDto:
      type: object
      properties:
        userId:
          type: string
          format: uuid
          example: 01JC4Z0000USUARIO0000000002
        competency:
          type: string
          example: TypeScript
          description: >-
            必须在 `anamnese-context` 返回的目录中——编造的能力会被拒绝。目录取决于
            `module_map`，这就是为什么校验在领域层而不是 DTO 层。
        level:
          type: string
          enum:
            - iniciante
            - intermediario
            - avancado
          example: avancado
        rationale:
          type: string
          example: Fixed three generic typing errors without help.
        evidenceEventIds:
          example:
            - 01JC4Z8QK3M7YV2N5T9B0PXHRB
          description: 每个 id 都必须来自此项目的会话事件——引用其他项目的事件会被拒绝。
          type: array
          items:
            type: string
      required:
        - userId
        - competency
        - level
        - rationale
        - evidenceEventIds
    ProficiencyProfileResponseDto:
      type: object
      properties:
        id:
          type: string
          example: 01JC4Z0000PERFIL00000000001
        projectId:
          type: string
          example: 01JC4Z0000PROJETO0000000001
        userId:
          type: string
          example: 01JC4Z0000USUARIO0000000002
        competency:
          type: string
          example: TypeScript
        level:
          type: string
          enum:
            - iniciante
            - intermediario
            - avancado
          example: avancado
        rationale:
          type: string
          example: Fixed three generic typing errors without help.
          description: 该等级背后的“为什么”。没有它，画像就是一个无法申诉的裁决。
        evidenceEventIds:
          example:
            - 01JC4Z8QK3M7YV2N5T9B0PXHRB
          description: 支持该等级的日志事件。
          type: array
          items:
            type: string
        createdAt:
          type: string
          example: '2026-07-26T12:00:00.000Z'
          format: date-time
        updatedAt:
          type: string
          example: '2026-07-26T12:00:00.000Z'
          format: date-time
        userName:
          type: object
          example: Senior Dev
          nullable: true
        userEmail:
          type: object
          example: dev@brabo.dev
          nullable: true
          description: 当拥有该画像的人已不再是项目成员时为 `null`——画像在成员被移除后仍然保留。
      required:
        - id
        - projectId
        - userId
        - competency
        - level
        - rationale
        - evidenceEventIds
        - createdAt
        - updatedAt
        - userName
        - userEmail
    ProjectBlockedStatusResponseDto:
      type: object
      properties:
        projectId:
          type: string
          example: 01JC4Z0000PROJETO0000000001
        blockedTaskCount:
          type: number
          example: 2
      required:
        - projectId
        - blockedTaskCount
    ProjectBusinessRuleResponseDto:
      type: object
      properties:
        id:
          type: string
          example: evt_01jc4z0000regra0000000001
          description: >-
            `artifact.business_rule` 事件的 id。这是创建 story 时放入 `business_rule_ids`
            的值——没有规则表。
        title:
          type: string
          example: Cart accepts at most 50 items
        description:
          type: string
          example: Above that, the addition is refused with 409.
          description: 规则的内容，而不只是陈述：story 的需求来源于它。正是这个字段把这次读取与界面的覆盖率区分开来。
        coveredByStoryIds:
          example:
            - 01JC4Z0000HISTORIA000000001
          description: 已引用此规则的 story。
          type: array
          items:
            type: string
        covered:
          type: boolean
          example: true
      required:
        - id
        - title
        - description
        - coveredByStoryIds
        - covered
    ProjectBusinessRulesResponseDto:
      type: object
      properties:
        rules:
          type: array
          items:
            $ref: '#/components/schemas/ProjectBusinessRuleResponseDto'
        uncoveredCount:
          type: number
          example: 2
          description: >-
            有多少条规则没有任何 story 覆盖。这是 PO 的待办项，也是 `listar_regras_de_negocio`
            工具摆在模型面前的数字。
      required:
        - rules
        - uncoveredCount
    ProjectCardBudgetResponseDto:
      type: object
      properties:
        limitMicros:
          type: number
          example: 50000000
        spentMicros:
          type: number
          example: 12500000
      required:
        - limitMicros
        - spentMicros
    ProjectCardSummaryResponseDto:
      type: object
      properties:
        projectId:
          type: string
          example: 01JC4Z0000PROJETO0000000001
        provider:
          type: string
          enum:
            - local
            - github
            - gitlab
          example: github
          description: 项目还没有已置备的仓库时为 `local`。
        provisioningStatus:
          type: string
          enum:
            - provisioning
            - provisioned
            - provision_failed
            - awaiting_plan_decision
          nullable: true
          example: provisioned
          description: 引导从未开始时为 `null`。由游标推导。
        budget:
          nullable: true
          description: 项目从未设置过预算时为 `null`——不同于被清零的记录，正是这一点让卡片提供“设置预算”。
          type: object
          allOf:
            - $ref: '#/components/schemas/ProjectCardBudgetResponseDto'
        latestSessionId:
          type: object
          example: 01JC4Z0000SESSAO00000000001
          nullable: true
        latestSeq:
          type: number
          example: 41
          description: 最近会话中已记录的最后一个 `seq`（`nextSeq - 1`）；没有会话时为 0。Web 端将其与已看过的内容比较来统计未读数。
        lastEvent:
          nullable: true
          description: 最近会话的最后一个事件——卡片的页脚行。
          type: object
          allOf:
            - $ref: '#/components/schemas/SessionEventResponseDto'
        storiesAwaitingPromotion:
          type: number
          example: 2
          description: PO 已完成、等待用户提升的 story（RN-048）。
        pendingApprovalsCount:
          type: number
          example: 8
          description: >-
            `proposed_actions`，其 `status =
            pending`，覆盖整个项目所有会话——而不仅是最近的会话。这就是侧边栏显示为项目徽标的内容（RN-151）。
        onlineAgentCount:
          type: number
          example: 2
          description: >-
            此刻有多少代理在线——正在工作或有等待决定的待办项（RN-409）。绝不是团队规模：`idle`/`idle_tripped` 的
            dev agent 不计入，`idle` 的对话式代理不计入，QA/SecOps 永不计入（每次调用只有一个裁决）。
        roster:
          $ref: '#/components/schemas/RosterFactsResponseDto'
      required:
        - projectId
        - provider
        - provisioningStatus
        - budget
        - latestSessionId
        - latestSeq
        - lastEvent
        - storiesAwaitingPromotion
        - pendingApprovalsCount
        - onlineAgentCount
        - roster
    ProjectFoldersResponseDto:
      type: object
      properties:
        base:
          type: object
          example: /home/voce/brabo
          nullable: true
          description: >-
            此安装的挂载项目基目录（`BRABO_PROJECTS_BASE`，ADR 0141）——与 `GET
            .../projects-base` 返回的值相同，在此重复，以便客户端无需第二次调用即可渲染面包屑。`null` 表示此安装不提供
            Mounted 模式。
        path:
          type: object
          example: /home/voce/brabo/clientes
          nullable: true
          description: >-
            实际列出的文件夹，已规范化。等于 `base`，当请求省略 `path` 时。仅当完全没有基目录时为
            `null`：什么都没列出，因为无处可列。
        entries:
          example:
            - api
            - loja
            - website
          description: >-
            `path` 正下方的子目录名称，已排序，绝不递归。文件、符号链接和以点开头的条目会被排除——见
            `arquivos`、`simbolicos` 和 `truncado`。
          type: array
          items:
            type: string
        truncado:
          type: boolean
          example: false
          description: >-
            该文件夹包含超过 500 个子目录，只返回了前 500
            个。排序在截断之前进行，因此截断是确定性的，而不是“文件系统先返回什么就是什么”。
        arquivos:
          type: number
          example: 12
          description: 有多少个非目录条目被略去。满是代码的文件夹绝不能看起来是空的（RN-180）。
        simbolicos:
          type: number
          example: 1
          description: 有多少个符号链接被略去。浏览器从不跟随链接——链接只报告而绝不进入，因此指向基目录之外的链接不是逃出基目录的途径。
      required:
        - base
        - path
        - entries
        - truncado
        - arquivos
        - simbolicos
    ProjectGitRemoteResponseDto:
      type: object
      properties:
        kind:
          type: string
          example: remote
          enum:
            - local
            - remote
          description: '`local` 是磁盘上没有凭据的裸仓库路径；`remote` 是需要认证的 URL。'
        origin:
          type: string
          example: https://github.com/daneiel/hello-api.git
          description: >-
            会变成 `git remote add origin <this>` 的内容。**按设计不嵌入凭据**：这个值会被写入
            workspace 的 `.git/config`，任何能访问项目目录的人都能读取。
        defaultBranch:
          type: string
          example: main
        token:
          type: string
          nullable: true
          description: 仅在 `remote` 时。workspace 所有者的令牌（RN-058），在调用时解密。接收方按每次调用注入并随即丢弃。
        username:
          type: string
          nullable: true
          example: x-access-token
          description: HTTP Basic 凭据对中的用户；令牌就是密码。
      required:
        - kind
        - origin
        - defaultBranch
    ProjectMemberComUsuarioResponseDto:
      type: object
      properties:
        userId:
          type: string
          example: 01JC4Z0000USUARIO0000000002
        role:
          type: string
          enum:
            - viewer
            - developer
            - maintainer
            - owner
          example: maintainer
          description: >-
            有效角色：有项目角色时取项目角色，否则取从 workspace 继承的角色（RN-471）——项目角色在两个方向上都覆盖
            workspace 角色，无论更高还是更低。
        name:
          type: object
          example: Senior Dev
          nullable: true
        email:
          type: string
          example: dev@brabo.dev
      required:
        - userId
        - role
        - name
        - email
    ProjectMemberResponseDto:
      type: object
      properties:
        projectId:
          type: string
          example: 01JC4Z0000PROJETO0000000001
        userId:
          type: string
          example: 01JC4Z0000USUARIO0000000002
        role:
          type: string
          enum:
            - viewer
            - developer
            - maintainer
            - owner
          example: maintainer
          description: 线性层级：owner > maintainer > developer > viewer。每个角色都包含其后角色的权限。
        createdAt:
          type: string
          example: '2026-07-22T08:05:00.000Z'
          format: date-time
      required:
        - projectId
        - userId
        - role
        - createdAt
    ProjectMirrorStateResponseDto:
      type: object
      properties:
        mirrorPath:
          type: object
          example: /home/you/mirrors/store
          nullable: true
          description: >-
            今天声明的目标（`projects.mirror_path`，RN-515）。`null`
            表示项目没有镜像——这是正常状态，也是界面完全隐藏这一行而不是凭空编造缺失的原因。
        status:
          type: string
          example: synced
          enum:
            - never
            - synced
            - failed
          description: >-
            `never`——还没有 runner 报告过任何一轮（没有记录）。`synced`——最近一轮已复制；`filesCopied`
            可能为 `0`，表示“看过了，没有需要复制的”，有自己专门的语句。`failed`——最近一轮失败，且错误比最近一次成功更新。API
            根据两个时间戳推导出它，让这条规则只有一个来源；界面仍会拿到原始字段来组织语句。
        lastSyncedAt:
          type: object
          example: '2026-09-07T12:04:00.000Z'
          nullable: true
          description: 最近一次成功的同步。之后的失败绝不会抹去它——在镜像损坏期间，这是此界面最有用的信息。
        filesCopied:
          type: object
          example: 412
          nullable: true
        filesSkipped:
          type: object
          example: 3
          nullable: true
        filesRefused:
          type: object
          example: 0
          nullable: true
        lastDestination:
          type: object
          example: /home/you/mirrors/store
          nullable: true
          description: >-
            最近一轮实际写入的位置——已冻结。在有人修改目标后它会与 `mirrorPath` 不同，因为授权随 join 传递，只在 runner
            重连时才会改变（RN-516）。
        lastError:
          type: object
          example: null
          nullable: true
        lastErrorAt:
          type: object
          example: null
          nullable: true
      required:
        - mirrorPath
        - status
        - lastSyncedAt
        - filesCopied
        - filesSkipped
        - filesRefused
        - lastDestination
        - lastError
        - lastErrorAt
    ProjectResponseDto:
      type: object
      properties:
        id:
          type: string
          example: 01JC4Z0000PROJETO0000000001
        workspaceId:
          type: string
          example: 01JC4Z0000WORKSPACE00000001
        name:
          type: string
          example: Checkout
        slug:
          type: string
          example: checkout
          description: 在 workspace 内唯一。
        workspaceDirName:
          type: string
          example: checkout-3f2b1c8e
          description: >-
            此项目在 PROJECT_WORKSPACES_ROOT 中的物理 workspace 文件夹名称（RN-109）。新项目为
            `<slug>-<8 id chars>`；在此列存在之前创建的项目为原始 UUID。在创建时冻结——之后编辑 `slug`
            不会重新计算它。
        executionMode:
          type: string
          enum:
            - container
            - mounted
            - runner
          example: container
          description: >-
            命令在哪里执行（RN-169/RN-421——ADR 0072/0104）。`container`：在
            PROJECT_WORKSPACES_ROOT 中托管的文件夹。`mounted`：`workspacePath` 中的用户文件夹，位于
            `BRABO_PROJECTS_BASE` 内，通过基目录的 bind-mount 访问（ADR 0141）。`runner`：由
            runner 确认的用户文件夹。`mounted` 和 `runner` 都会在 `workspaceVerifiedAt`
            中报告确认。
        workspacePath:
          type: object
          example: null
          nullable: true
          description: 用户文件夹的绝对路径——`mounted`/`runner` 时填写，始终为 `null`，在 `container` 模式下。
        workspaceVerifiedAt:
          type: object
          example: null
          nullable: true
          description: >-
            路径在磁盘上被确认的时间。`null` = 尚未验证。在 `runner` 中，确认来自 CLI 连接（RN-423）；在
            `mounted` 中，在 Infra 启动容器、文件夹被物化时打上时间戳（RN-501，ADR 0142）。`container`
            从不填写此字段。它记录的是一次确认，绝不是心跳。
        mirrorPath:
          type: object
          example: null
          nullable: true
          description: >-
            用户机器上的镜像目标——位于挂载基目录之外、本地代理把工作复制到的文件夹（RN-515，ADR 0147）。`null`
            表示项目没有镜像，这是正常状态，绝不是错误。只有 `mounted`/`runner` 可以有。通过 `PUT
            /projects/:projectId/mirror-path`
            设置；它随每次项目读取一并返回，因此不需要专用端点来获知。该值只经过词法校验——文件夹是否存在由本地代理知道，而不是此 API。
        language:
          type: string
          example: pt-BR
          description: >-
            项目的语言（RN-619）：所有没有人类作者的内容所使用的语言——共享制品（产品简介、业务规则、ADR）以及没有人输入的回合（启动、dev
            agent、gate、提交信息、PR 正文）。规范的 BCP-47
            代码，开放列表。新项目以其创建者的有效回复语言开始（RN-618）。通过 `PATCH
            /projects/:projectId`（`maintainer`）设置。engine 在每个没有人类作者的回合的每次 LLM
            调用末尾，以临时系统消息的形式把它发送给模型（RN-622）；在某人的回合中发出的制品仍遵循此人的回复语言。
        createdBy:
          type: string
          example: 01JC4Z0000USUARIO0000000001
        taskBudgetMicros:
          type: object
          example: 500000
          nullable: true
          description: 每个 dev agent 任务的 token 上限，以微美元计。`null` 使用领域默认值。
        maxConsecutiveBlocked:
          type: object
          example: 3
          nullable: true
          description: >-
            每个 dev agent 的熔断器（Phase 12b——RN-047）：在停到 idle_tripped
            之前，连续以阻塞结束的任务数。`null` 使用领域默认值。
        storyPromotion:
          type: string
          enum:
            - manual
            - auto
          example: auto
          description: >-
            谁把 story 提升为 `ready`（Phase 12c——RN-048）。`manual`：PO
            提议，用户决定。`auto`：创建时自动提升——自 RN-659 起为新项目的默认值；之前创建的项目保留原有的值。
        createdAt:
          type: string
          example: '2026-07-21T11:00:00.000Z'
          format: date-time
        updatedAt:
          type: string
          example: '2026-07-21T11:00:00.000Z'
          format: date-time
      required:
        - id
        - workspaceId
        - name
        - slug
        - workspaceDirName
        - executionMode
        - workspacePath
        - workspaceVerifiedAt
        - mirrorPath
        - language
        - createdBy
        - taskBudgetMicros
        - maxConsecutiveBlocked
        - storyPromotion
        - createdAt
        - updatedAt
    ProjectsBaseResponseDto:
      type: object
      properties:
        projectsBase:
          type: object
          example: /home/voce/brabo
          nullable: true
          description: >-
            运营者电脑上 Brabo 容器唯一能看到的文件夹，按身份（`$X:$X`）挂载到 `api` 和 `engine` 中（ADR
            0141）。`null` 表示未设置 `BRABO_PROJECTS_BASE`：此安装不提供 Mounted
            模式，项目向导不得提供它。绝不是失败——缺省是正常状态。
        brokerConfigurado:
          type: boolean
          example: false
          description: >-
            此安装是否配置了容器 broker（已设置 `BROKER_URL`，ADR 0130）。`container` 和 `mounted`
            项目只通过 broker 启动容器（ADR 0144），没有它，每个 dev agent 都会永远被阻塞（ADR 0143）。为
            `false` 时，项目向导不会预选 Mounted，Container 和 Mounted 保持可见但不可选并以文字说明原因，并预选
            Runner（ADR 0161，RN-573）。它说明的是变量存在，而绝不是 broker 有响应。与 `projectsBase`
            一样是安装配置：每个 workspace 都相同。
      required:
        - projectsBase
        - brokerConfigurado
    ProjectSpendTeamResponseDto:
      type: object
      properties:
        projectId:
          type: string
          format: uuid
        dias:
          type: number
          example: 30
        totalMicros:
          type: number
          example: 1250000
        assinaturaMicros:
          type: number
          example: 0
          description: Seat part of the total (RN-954).
        inputTokens:
          type: number
          example: 120000
        outputTokens:
          type: number
          example: 35000
        chamadas:
          type: number
          example: 42
        porAgente:
          description: By AGENT.
          type: array
          items:
            $ref: '#/components/schemas/SpendLinhaResponseDto'
        porArea:
          description: By catalog AREA of the agent; `sem-area` when none.
          type: array
          items:
            $ref: '#/components/schemas/SpendLinhaResponseDto'
      required:
        - projectId
        - dias
        - totalMicros
        - assinaturaMicros
        - inputTokens
        - outputTokens
        - chamadas
        - porAgente
        - porArea
    ProjectUnreadEventsResponseDto:
      type: object
      properties:
        projectId:
          type: string
          example: 01JC4Z0000PROJETO0000000001
        sessionId:
          type: string
          example: 01JC4Z0000SESSAO00000000001
          description: 项目最近的会话——与 `projects-summary` 在 `latestSessionId` 中报告的相同。
        events:
          description: >-
            按 `seq` 降序——第一项是最新的（RN-100）。每个项目最多 50 条，与 `GET .../events` 在不带
            `limit` 时应用的上限相同；当未读数超过上限时，返回的是最新的那些。有多少被略去由 `latestSeq`
            减去截止点得出，无需再发请求。
          type: array
          items:
            $ref: '#/components/schemas/SessionEventResponseDto'
      required:
        - projectId
        - sessionId
        - events
    PromocaoRecusadaResponseDto:
      type: object
      properties:
        storyId:
          type: string
          example: 01JC2XK9Q7
        reason:
          type: string
          example: 'Story is not ready: missing dod, dor'
          description: 可读的原因。story 可能已失去就绪状态，或者在提议与决定之间它的某个模块已从 `module_map` 中移除。
      required:
        - storyId
        - reason
    PromoteStoriesDto:
      type: object
      properties:
        storyIds:
          example:
            - 01JC2XK9Q7
            - 01JC2XKB4M
          description: 要提升为 `ready` 的 story 的 id。单个即为一批 1 个。每个 story 独立校验和提升——失败的不会阻塞其他的。
          type: array
          items:
            type: string
      required:
        - storyIds
    PromoteStoriesResponseDto:
      type: object
      properties:
        promoted:
          description: 已变为 `ready` 的 id，其任务已可被认领。
          type: array
          items:
            type: string
        failed:
          description: 整批都通过时为空。
          type: array
          items:
            $ref: '#/components/schemas/PromocaoRecusadaResponseDto'
      required:
        - promoted
        - failed
    PromptTemplateReadResponseDto:
      type: object
      properties:
        name:
          type: string
          example: dev-agent-kickoff
        version:
          type: string
          example: '3'
        body:
          type: string
          example: You are the dev agent for the {{modulo}} module...
        hash:
          type: string
          example: sha256:9f2c...
      required:
        - name
        - version
        - body
        - hash
    PromptTemplateResponseDto:
      type: object
      properties:
        name:
          type: string
          example: dev-agent-kickoff
        version:
          type: string
          example: '3'
        body:
          type: string
          example: You are the dev agent for the {{modulo}} module...
        hash:
          type: string
          example: sha256:9f2c...
        active:
          type: boolean
          description: >-
            这是否是模板的当前版本。在创建了新版本的 upsert 之后立即始终为
            `true`；当幂等命中（相同哈希）指向一个已被之后的发布停用的版本时，可能返回 `false`。
      required:
        - name
        - version
        - body
        - hash
        - active
    ProposeActionDto:
      type: object
      properties:
        actionType:
          type: string
          enum:
            - terminal
            - git_commit
            - git_push
            - pr_open
            - spend
            - git_repo_create
            - git_branch_create
            - git_branch_protect
            - write_file
            - open_adr_pr
            - git_merge
            - open_infra_pr
            - instruction_patch
            - parallelize
            - raise_max_parallel
            - propose_execution_plan
            - assess_implementability
            - container_start
            - container_stop
            - container_remove
            - container_recreate
            - container_start_via_runner
            - qa_environment_start
          example: terminal
          description: >-
            操作类型。每种类型都有自己的最低角色：`git_push`、`git_merge` 和 `git_branch_protect` 需要
            `maintainer`，其余需要 `developer`。
        actor:
          description: 提议者。
          allOf:
            - $ref: '#/components/schemas/ActorDto'
        payload:
          type: object
          additionalProperties: true
          example:
            command: pnpm test
          description: 操作参数，形态因 `actionType` 而异。
      required:
        - actionType
        - actor
        - payload
    ProposedActionResponseDto:
      type: object
      properties:
        id:
          type: string
          example: 01JC4Z8QK3M7YV2N5T9B0PXHRC
          description: 操作的 ULID。
        projectId:
          type: string
          example: 01JC4Z0000PROJETO0000000001
        sessionId:
          type: string
          example: 01JC4Z8QK3M7YV2N5T9B0PXHRA
        seq:
          type: number
          example: 7
          description: 操作在会话中的顺序；在列表中用作游标。
        actionType:
          type: string
          enum:
            - terminal
            - git_commit
            - git_push
            - pr_open
            - spend
            - git_repo_create
            - git_branch_create
            - git_branch_protect
            - write_file
            - open_adr_pr
            - git_merge
            - open_infra_pr
            - instruction_patch
            - parallelize
            - raise_max_parallel
            - propose_execution_plan
            - assess_implementability
            - container_start
            - container_stop
            - container_remove
            - container_recreate
            - container_start_via_runner
            - qa_environment_start
          example: terminal
          description: 该操作将要做什么。`git_push` 和 `git_merge` 需要 `maintainer`。
        payload:
          type: object
          additionalProperties: true
          example:
            command: pnpm test
          description: 操作的参数，因 `actionType` 而异。
        status:
          type: string
          enum:
            - pending
            - approved
            - denied
            - auto_approved
            - executed
            - failed
          example: pending
          description: 在流水线中的状态。`auto_approved` 刻意与 `approved` 区分开：日志需要区分人做出的决定和策略放行的内容。
        resolvedPolicy:
          type: string
          enum:
            - auto_approve
            - require_approval
            - deny
          example: require_approval
          description: '`permissions.json` 对此操作的决定。`deny` 始终优先于 `allow`——即使代理的自主权也无法覆盖它。'
        actor:
          description: 提议者。
          allOf:
            - $ref: '#/components/schemas/ActorResponseDto'
        decidedBy:
          type: object
          example: null
          nullable: true
          description: 批准或拒绝它的用户 id。绝不是代理。
        decidedAt:
          type: object
          example: null
          format: date-time
          nullable: true
        rejectionReason:
          type: object
          example: null
          nullable: true
        executionResult:
          type: object
          additionalProperties: true
          nullable: true
          example: null
          description: >-
            执行结果，形态因 `actionType` 而异——终端为输出和退出码，git 类型为 PR 编号和 URL。在操作尚未执行时为
            `null`。
        createdAt:
          type: string
          example: '2026-07-27T14:33:10.900Z'
          format: date-time
        updatedAt:
          type: string
          example: '2026-07-27T14:33:10.900Z'
          format: date-time
      required:
        - id
        - projectId
        - sessionId
        - seq
        - actionType
        - payload
        - status
        - resolvedPolicy
        - actor
        - decidedBy
        - decidedAt
        - rejectionReason
        - executionResult
        - createdAt
        - updatedAt
    ProposeHypothesesInternalDto:
      type: object
      properties:
        projectId:
          type: string
          format: uuid
          example: 01JC4Z0000PROJETO0000000001
        tier:
          type: string
          enum:
            - leve
            - pesada
          example: leve
          description: 两个层级使用的确实是不同的模型；成本也不同。
        triggeredBy:
          type: string
          enum:
            - auto
            - manual
          example: auto
        eventCount:
          type: number
          example: 128
          description: 被分析日志的大小。
        cause:
          type: string
          enum:
            - normal
            - timeout
            - kill
            - crash
            - node_shutdown
            - unknown
          example: timeout
          description: >-
            由 engine 分类的原因。决定是否需要 `terminationAnalysis`
            的是它，而不是终止状态。设为可选，以免在滚动部署期间破坏旧版 engine。
        hypotheses:
          type: array
          items:
            $ref: '#/components/schemas/HypothesisDraftDto'
      required:
        - projectId
        - tier
        - triggeredBy
        - eventCount
        - hypotheses
    ProposeHypothesesResponseDto:
      type: object
      properties:
        analysisId:
          type: string
          example: 01JC4Z0000ANALISE0000000001
          description: 创建的轮次。本会话的上一轮将被取代。
        hypotheses:
          type: array
          items:
            $ref: '#/components/schemas/HypothesisResponseDto'
      required:
        - analysisId
        - hypotheses
    ProposeInstructionPatchInternalDto:
      type: object
      properties:
        projectId:
          type: string
          format: uuid
          example: 01JC4Z0000PROJETO0000000001
        agent:
          type: string
          example: dev-api
        proposedContent:
          type: string
          example: |-
            # dev-api

            Always run the suite before opening the PR.
          description: 文件最终的完整内容，而不是 diff。
        rationale:
          type: string
          example: The task was reopened three times for lack of a criterion.
        hypothesisId:
          type: string
          format: uuid
          example: 01JC4Z0000HIPOTESE000000001
          description: 引发该补丁的假设。正是它闭合了 Anamnesis 循环。
      required:
        - projectId
        - agent
        - proposedContent
        - rationale
    ProposeMaxParallelInternalDto:
      type: object
      properties:
        projectId:
          type: string
          format: uuid
          example: 01JC4Z0000PROJETO0000000001
        area:
          type: string
          example: dev
          description: 上限将要提高的区域。
        proposto:
          type: number
          example: 4
          minimum: 1
          description: 提议的上限。必须高于当前值——提议相同或更低的值会成为用户需要阅读的队列中的噪声，而 Anamnesis 是周期性运行的。
        rationale:
          type: string
          example: >-
            You authorized the same request four times in this window, and none
            was denied.
          description: 以观察到的决定为依据，而不是印象。
      required:
        - projectId
        - area
        - proposto
        - rationale
    ProviderCapabilitiesResponseDto:
      type: object
      properties:
        provider:
          type: string
          enum:
            - ollama
            - anthropic
            - openai
            - openrouter
            - nvidia-nim
            - together
            - deepinfra
            - bitdeer
            - vultr
            - 9router
            - opencode-zen
          example: openrouter
        capabilities:
          $ref: '#/components/schemas/LLMProviderCapabilitiesResponseDto'
      required:
        - provider
        - capabilities
    ProvisionedRepositoryResponseDto:
      type: object
      properties:
        id:
          type: string
          example: 01JC4Z0000REPOSITORIO000001
        projectId:
          type: string
          example: 01JC4Z0000PROJETO0000000001
        provider:
          type: string
          enum:
            - local
            - github
            - gitlab
          example: github
        externalId:
          type: string
          example: '904837261'
          description: 仓库在提供商上的 id。
        url:
          type: string
          example: https://github.com/acme/checkout
        defaultBranch:
          type: string
          example: main
          description: 项目的策略使用 `dev`、`qa` 和 `main` 作为永久分支。
        visibility:
          type: string
          enum:
            - public
            - private
          example: private
        origin:
          type: string
          enum:
            - created
            - adopted
          example: created
          description: '`created` = Brabo 创建了仓库；`adopted` = 它指向了一个已存在的仓库。一旦记录即不可变（RN-046）。'
        provisionedBy:
          type: string
          example: 01JC4Z0000USUARIO0000000001
        createdAt:
          type: string
          example: '2026-07-23T10:00:00.000Z'
          format: date-time
        updatedAt:
          type: string
          example: '2026-07-23T10:00:00.000Z'
          format: date-time
      required:
        - id
        - projectId
        - provider
        - externalId
        - url
        - defaultBranch
        - visibility
        - origin
        - provisionedBy
        - createdAt
        - updatedAt
    ProvisionRepositoryDto:
      type: object
      properties:
        name:
          type: string
          example: checkout
          description: 仓库在提供商上的名称。
        visibility:
          type: string
          enum:
            - public
            - private
          example: private
        namespace:
          type: string
          example: acme
          description: 组织或群组。省略时，仓库创建在登记凭据者的个人账户下。
      required:
        - name
        - visibility
    ProvisionRepositoryResponseDto:
      type: object
      properties:
        repository:
          $ref: '#/components/schemas/ProvisionedRepositoryResponseDto'
        bootstrap:
          description: Gitflow 引导停在了哪里。工作在后台继续——通过 `GET /projects/:id/git/bootstrap` 跟踪。
          allOf:
            - $ref: '#/components/schemas/PassoDeBootstrapResponseDto'
      required:
        - repository
        - bootstrap
    PsychologistAnalysisResponseDto:
      type: object
      properties:
        id:
          type: string
          example: 01JC4Z0000ANALISE0000000001
        projectId:
          type: string
          example: 01JC4Z0000PROJETO0000000001
        sessionId:
          type: string
          example: 01JC4Z8QK3M7YV2N5T9B0PXHRA
        tier:
          type: string
          enum:
            - leve
            - pesada
          example: leve
          description: 分诊层级。两者使用的确实是不同的模型——正是这一点让成本真正产生差异，而不只是标签不同。
        triggeredBy:
          type: string
          enum:
            - auto
            - manual
          example: auto
          description: '`manual` 是由人请求的重新分析。'
        supersedes:
          type: object
          example: null
          nullable: true
          description: 此分析所替代的分析的 id。
        superseded:
          type: boolean
          example: false
        supersededAt:
          type: object
          example: null
          format: date-time
          nullable: true
        eventCountAtAnalysis:
          type: number
          example: 128
          description: 分析时的日志大小——它所看到的快照。
        createdAt:
          type: string
          example: '2026-07-26T18:00:00.000Z'
          format: date-time
        costMicros:
          type: number
          example: 4200
          description: 真实成本，以微美元计，由该轮的 `token_usage` 求和而得。只有已完成的运行才会在这里有记录：失败的运行刻意不记录。
        hypothesisCount:
          type: number
          example: 3
          description: 该轮产出了多少条假设。
      required:
        - id
        - projectId
        - sessionId
        - tier
        - triggeredBy
        - supersedes
        - superseded
        - supersededAt
        - eventCountAtAnalysis
        - createdAt
        - costMicros
        - hypothesisCount
    PsychologistContextBusinessRuleResponseDto:
      type: object
      properties:
        id:
          type: string
          example: RN-014
        title:
          type: string
          example: Cart accepts at most 50 items
      required:
        - id
        - title
    PsychologistContextResponseDto:
      type: object
      properties:
        alreadyAnalyzed:
          type: boolean
          example: false
          description: 本会话已存在一份当前分析。正是这一点让自动路径是幂等的：为 `true` 时 worker 直接短路，不花费任何东西。
        sessionStatus:
          type: string
          enum:
            - created
            - active
            - closing
            - closed
            - closed_abnormally
          example: closed_abnormally
        terminationReason:
          type: object
          example: heartbeat_timeout
          nullable: true
        businessRules:
          type: array
          items:
            $ref: '#/components/schemas/PsychologistContextBusinessRuleResponseDto'
        priorHypotheses:
          description: 之前已经提议过的内容，以免本轮重复。
          type: array
          items:
            $ref: '#/components/schemas/PriorHypothesisResponseDto'
      required:
        - alreadyAnalyzed
        - sessionStatus
        - terminationReason
        - businessRules
        - priorHypotheses
    PsychologistStatusResponseDto:
      type: object
      properties:
        enabled:
          type: boolean
          example: false
          description: 今天是否可以运行新的分析轮次，无论自动还是按需。禁用不会触及已有数据——过去的分析和假设保持完整且可见。
      required:
        - enabled
    QaEnvironmentResponseDto:
      type: object
      properties:
        sucesso:
          type: boolean
          description: '`false` when the broker refused; `motivo` explains.'
        status:
          type: string
          enum:
            - provisioning
            - running
            - failed
            - removed
        chave:
          type: string
          example: a1b2c3d4
          description: Instance key (8 chars of the task id).
        motivo:
          type: string
          nullable: true
      required:
        - sucesso
        - status
        - chave
        - motivo
    RagCoverageResponseDto:
      type: object
      properties:
        docs:
          $ref: '#/components/schemas/RagFileCoverageResponseDto'
        adr:
          $ref: '#/components/schemas/RagFileCoverageResponseDto'
        session:
          $ref: '#/components/schemas/RagSessionCoverageResponseDto'
        local:
          $ref: '#/components/schemas/RagLocalCoverageResponseDto'
        chunksTotal:
          type: number
        chunksWithoutVector:
          type: number
      required:
        - docs
        - adr
        - session
        - local
        - chunksTotal
        - chunksWithoutVector
    RagFeedbackInternalDto:
      type: object
      properties:
        projectId:
          type: string
          format: uuid
        searchId:
          type: string
          format: uuid
        chunkId:
          type: string
          format: uuid
        verdict:
          type: string
          enum:
            - util
            - irrelevante
        agent:
          type: string
          example: qa
          description: 代理 slug——投票者。
      required:
        - projectId
        - searchId
        - chunkId
        - verdict
        - agent
    RagFeedbackInternalResponseDto:
      type: object
      properties:
        searchId:
          type: string
          format: uuid
        chunkId:
          type: string
          format: uuid
        verdict:
          type: string
          enum:
            - util
            - irrelevante
        rank:
          type: number
          description: 被评判的分块在那次搜索中所处的位置（从 1 开始）。
      required:
        - searchId
        - chunkId
        - verdict
        - rank
    RagFeedbackRequestDto:
      type: object
      properties:
        searchId:
          type: string
          format: uuid
          description: 产生此命中的搜索所返回的 `searchId`——绝不由客户端编造。
        chunkId:
          type: string
          format: uuid
          description: 被评判的分块。必须是那次搜索返回的命中之一，否则返回 400。
        verdict:
          type: string
          enum:
            - util
            - irrelevante
          description: 两个值，而不是 1-5 分制：更细的刻度会招致每个投票者标尺不同，之后任何聚合都无法还原。
      required:
        - searchId
        - chunkId
        - verdict
    RagFeedbackResponseDto:
      type: object
      properties:
        searchId:
          type: string
          format: uuid
        chunkId:
          type: string
          format: uuid
        verdict:
          type: string
          enum:
            - util
            - irrelevante
        rank:
          type: number
          description: 被评判的分块在那次搜索中所处的位置（从 1 开始）——正是这个数字区分了“索引不好”和“权重不对”。
      required:
        - searchId
        - chunkId
        - verdict
        - rank
    RagFileCoverageResponseDto:
      type: object
      properties:
        filesInRepo:
          type: number
        filesIndexed:
          type: number
        truncated:
          type: boolean
      required:
        - filesInRepo
        - filesIndexed
        - truncated
    RagLocalCoverageResponseDto:
      type: object
      properties:
        filesIndexed:
          type: number
        folderName:
          type: string
          nullable: true
        lastAttachedAt:
          type: string
          nullable: true
          description: 此范围真实的 `MAX(chunks.created_at)`——绝不是猜测的“N 分钟前”（RN-455）。
      required:
        - filesIndexed
        - folderName
        - lastAttachedAt
    RagSearchInternalDto:
      type: object
      properties:
        projectId:
          type: string
          format: uuid
        query:
          type: string
          example: how the parallelism cap is decided
        topK:
          type: number
          example: 5
        sessionId:
          type: string
          format: uuid
          description: >-
            代理正在运行的会话。缺省表示调用方没有会话——遥测记录仍会写入（RN-479），只是跳过 `rag.search` 时间线叙述。api
            绝不编造：没收到的会话就是不存在的会话。
        agent:
          type: string
          example: dev-lead
          description: '代理 slug，记录为遥测参与者（`actor_kind: agent`）。缺省时回落为 `system` 参与者，而不是编造一个用户。'
      required:
        - projectId
        - query
    RagSearchInternalHitResponseDto:
      type: object
      properties:
        chunkId:
          type: string
          format: uuid
          description: 分块 id——`rag_feedback` 投票所需引用的一半（RN-480）。没有它，代理读到一个命中后将无法说明它是否有用。
        path:
          type: string
          example: docs/adr/0080-chat-rag-pipeline-indexacao.md
          description: 文件路径（`docs`/`adr` 范围）或 `session:<id>`（`session` 范围，没有真实文件路径）。
        chunk:
          type: string
          description: 检索到的分块的完整内容。
        score:
          type: number
          description: 组合得分（向量 + 词法），已按阈值过滤。
        excerpt:
          type: string
          description: 分块的简短预览，用于展示而不会撑爆模型的上下文。
      required:
        - chunkId
        - path
        - chunk
        - score
        - excerpt
    RagSearchInternalResponseDto:
      type: object
      properties:
        searchId:
          type: string
          nullable: true
          format: uuid
          description: >-
            此次搜索留下的 `rag_searches` 记录（RN-479）——投票引用的另一半。遥测记录没有写入时为
            `null`；此时工具会省略这些 id，而不是给模型一个会被拒绝的引用。
        hits:
          type: array
          items:
            $ref: '#/components/schemas/RagSearchInternalHitResponseDto'
        degraded:
          type: boolean
          description: >-
            `true`，当查询的嵌入不可用、搜索回落为仅词法时（与 `vectorAvailable: false`——来自
            `HybridSearchUseCase`——的语义相同）。
      required:
        - searchId
        - hits
        - degraded
    RagSessionCoverageResponseDto:
      type: object
      properties:
        sessionsInProject:
          type: number
        sessionsIndexed:
          type: number
      required:
        - sessionsInProject
        - sessionsIndexed
    ReconhecerFalhaDeProtecaoResponseDto:
      type: object
      properties:
        status:
          type: object
          example: provisioned
          description: 由于 `protect_branches` 是最后一步，确认其失败就会结束引导——项目变得可以从仪表盘进入。
      required:
        - status
    RecordDelegationInternalDto:
      type: object
      properties:
        projectId:
          type: string
          format: uuid
          example: 01JC4Z0000PROJETO0000000001
        area:
          type: string
          example: qa
          description: 进行委派的区域——“qa”（Phase 8b）或 “infra”（Phase 8c）。是文本，不是枚举。
        taskId:
          type: string
          format: uuid
          example: 01JC4Z0000TASK00000000001
          description: >-
            委派背后的 backlog 任务（如存在）。QA（Phase 8b）总会发送它；Infra（Phase
            8c）从不发送——委派针对的是会话，而不是任务。
        leadAgent:
          type: string
          example: qa-lead
        subagent:
          type: string
          example: qa-automacao
        status:
          type: string
          enum:
            - completed
            - failed
            - dispensed
          example: completed
        parecerArtifactId:
          type: string
          example: evt_01jc4z0000parecer000000001
          description: >-
            此子专业裁决对应的 `artifact.qa_verdict` session_event 的 id——当 `status` 为
            “completed” 时必需。
        failureOrigin:
          type: string
          enum:
            - infra
            - modelo
            - codigo
            - politica
          example: infra
          description: 失败的来源（ADR 0020/0038），绝不通过排除法——当 `status` 为 “failed” 时必需。
        failureReason:
          type: string
          example: iteration limit reached without emit_perf_seguranca_verdict
        justification:
          type: string
          example: story with no relevant performance NFR
          description: >-
            lead 为何选择不委派——当 `status` 为 “dispensed”
            时必需。绝不留空：放弃委派是一项被记录的决定，而不是沉默（CLAUDE.md，Phase 8b 第 2 项）。
      required:
        - projectId
        - area
        - leadAgent
        - subagent
        - status
    RecordGateVerdictInternalDto:
      type: object
      properties:
        projectId:
          type: string
          format: uuid
          example: 01JC4Z0000PROJETO0000000001
        taskId:
          type: string
          format: uuid
          example: 01JC4Z0000TAREFA00000000001
        gate:
          type: string
          enum:
            - qa
            - secops
          example: qa
        veredito:
          type: string
          enum:
            - approved
            - changes_requested
            - needs_user_decision
          example: changes_requested
          description: '`needs_user_decision`（仅 QA，RN-860）：周期等待用户对一个未决问题的回答——不计为一次修正。'
        resumo:
          type: string
          example: Missing tests for the insufficient-stock path.
        itens:
          example:
            - No test for a nonexistent SKU
          description: 需要修改什么，逐项列出。
          type: array
          items:
            type: string
        pergunta:
          type: string
          example: Should a nonexistent code answer 404?
          description: 配合 `needs_user_decision`：新的未决问题（最多 500 个字符）。
        sugestao:
          type: string
          example: 404 with a plain message.
          description: 配合 `needs_user_decision`：代理的建议。
        openQuestionId:
          type: string
          format: uuid
          description: 配合 `needs_user_decision`：一个已存在的未决问题，而不是新问题。
        maxCorrections:
          type: number
          example: 3
          description: 任务变为 `blocked` 之前往返次数的上限。省略时使用领域默认值。
      required:
        - projectId
        - taskId
        - gate
        - veredito
        - resumo
        - itens
    RecordInfraGateVerdictInternalDto:
      type: object
      properties:
        projectId:
          type: string
          format: uuid
          example: 01JC4Z0000PROJETO0000000001
        prActionId:
          type: string
          format: uuid
          example: 01JC4Z8QK3M7YV2N5T9B0PXHRD
          description: 打开该 PR 的 `open_infra_pr` `proposed_action`。
        gate:
          type: string
          enum:
            - qa
            - secops
          example: secops
        veredito:
          type: string
          enum:
            - approved
            - changes_requested
          example: approved
        resumo:
          type: string
          example: Non-root image with no secret in ARG.
        itens:
          example: []
          type: array
          items:
            type: string
        maxCorrections:
          type: number
          example: 3
      required:
        - projectId
        - prActionId
        - gate
        - veredito
        - resumo
        - itens
    RecordProficiencyInternalDto:
      type: object
      properties:
        projectId:
          type: string
          format: uuid
          example: 01JC4Z0000PROJETO0000000001
        windowFrom:
          type: string
          example: '2026-07-27T12:00:00.000Z'
          format: date-time
        windowTo:
          type: string
          example: '2026-07-27T12:15:00.000Z'
          format: date-time
        eventCount:
          type: number
          example: 240
        profiles:
          type: array
          items:
            $ref: '#/components/schemas/ProficiencyDraftDto'
      required:
        - projectId
        - windowFrom
        - windowTo
        - eventCount
        - profiles
    RecordProficiencyResponseDto:
      type: object
      properties:
        runId:
          type: string
          example: 01JC4Z0000RODADA00000000001
        profiles:
          type: array
          items:
            type: object
            additionalProperties: true
          description: 本轮记录的画像。
      required:
        - runId
        - profiles
    RecursosDoContainerResponseDto:
      type: object
      properties:
        cpus:
          type: number
          example: 2
          description: CPU 数，允许小数。
        memoryMb:
          type: number
          example: 4096
          description: 内存，以 MiB 计。
        pidsLimit:
          type: number
          example: 512
          description: 进程数上限。正是它在不依赖动词允许列表的情况下遏制 fork 炸弹。
      required:
        - cpus
        - memoryMb
        - pidsLimit
    RedeDoContainerResponseDto:
      type: object
      properties:
        ports:
          type: array
          items:
            $ref: '#/components/schemas/PortaDto'
        dns:
          type: array
          items:
            type: string
        extraHosts:
          type: array
          items:
            $ref: '#/components/schemas/HostExtraDto'
        version:
          type: number
          example: 1
      required:
        - ports
        - dns
        - extraHosts
        - version
    RegisterDto:
      type: object
      properties:
        email:
          type: string
          example: fulano@brabo.dev
          description: 账户的邮箱。
        senha:
          type: string
          example: correct horse battery staple
          minLength: 12
          description: 密码。至少 12 个字符；不要求大写字母、数字或符号。
        nome:
          type: string
          example: Fulano de Tal
      required:
        - email
        - senha
    RegisterGitCredentialDto:
      type: object
      properties:
        provider:
          type: string
          enum:
            - github
            - gitlab
          example: github
        token:
          type: string
          example: ghp_…
          maxLength: 512
          description: >-
            个人访问令牌。在接触数据库之前通过信封加密进行加密，绝不返回，即使是加密后的也不返回。登记时不会测试令牌（ADR
            0050）——如需验证，请使用 `POST /users/me/credentials/{provider}/test`。512
            个字符的上限是防止荒谬载荷的保护，而不是格式校验。
      required:
        - provider
        - token
    RegistroDeContainerResponseDto:
      type: object
      properties:
        status:
          type: string
          enum:
            - provisioning
            - running
            - stopped
            - failed
            - removed
          example: running
          description: 登记的内容（project_containers.status）。
        imageVersion:
          type: number
          example: 1
        imagem:
          type: object
          nullable: true
          example: node:22-bookworm-slim
          description: >-
            在 `imageVersion` 处冻结的镜像，从该确切版本的 `artifact.project_image`
            事件解析而来——绝不是当前版本，它可能已被修订。找不到该版本的事件时为 `null`。
        resources:
          $ref: '#/components/schemas/RecursosDoContainerResponseDto'
        failureReason:
          type: object
          nullable: true
          example: null
        createdAt:
          type: string
          format: date-time
        statusChangedAt:
          type: string
          format: date-time
      required:
        - status
        - imageVersion
        - imagem
        - resources
        - failureReason
        - createdAt
        - statusChangedAt
    ReindexProjectResponseDto:
      type: object
      properties:
        docs:
          $ref: '#/components/schemas/IndexDocsReportResponseDto'
        sessions:
          $ref: '#/components/schemas/ReindexSessionsResponseDto'
        embeddingAvailable:
          type: boolean
        embeddingReason:
          type: string
      required:
        - docs
        - sessions
        - embeddingAvailable
    ReindexSessionsResponseDto:
      type: object
      properties:
        total:
          type: number
        indexed:
          type: number
        chunksCreated:
          type: number
      required:
        - total
        - indexed
        - chunksCreated
    RenameSessionDto:
      type: object
      properties:
        name:
          type: object
          example: Cart checkout
          nullable: true
          maxLength: 80
          description: >-
            新名称。`null`（或空白字符串）会清除名称，会话恢复为只用井号标签标识——这是撤销的路径，因此该字段接受
            null，而不需要单独的删除路由。
      required:
        - name
    RepoBootstrapStatusResponseDto:
      type: object
      properties:
        status:
          type: string
          enum:
            - provisioning
            - provisioned
            - provision_failed
            - awaiting_plan_decision
          example: provisioning
          nullable: true
          description: >-
            项目中从未进行过置备时为 `null`。`awaiting_plan_decision`
            是一个已接入、计划已生成但尚未决定的仓库：在有人批准计划或按原样接入之前，什么都不会运行。
        sessionId:
          type: object
          example: 01JC4Z8QK3M7YV2N5T9B0PXHRA
          nullable: true
          description: 引导的专用会话。进度界面读取其 `bootstrap.step_*` 事件来构建实时清单。
        failedStep:
          type: string
          enum:
            - commit_pr_template
            - commit_branching_policy
            - commit_gitignore
            - create_dev_branch
            - create_qa_branch
            - create_rc_branch
            - protect_branches
          example: null
          nullable: true
        lastError:
          type: object
          example: null
          nullable: true
        attempts:
          type: number
          example: 1
          description: 引导已经尝试了多少次。它是幂等且可恢复的：重复执行不会重复已成功的部分。
      required:
        - status
        - sessionId
        - failedStep
        - lastError
        - attempts
    ReportSessionTerminationDto:
      type: object
      properties:
        projectId:
          type: string
          format: uuid
          example: 01JC4Z0000PROJETO0000000001
        to:
          type: string
          enum:
            - closing
            - closed
            - closed_abnormally
          example: closed_abnormally
          description: >-
            `closing`
            不是终止状态：它表示“我正在释放这个会话”。如果另一个副本接管了它，它会继续存活；如果在超时前没有人接管，排空过程会发送
            `closed_abnormally`。`created` 和 `active` 被排除在外，因为进入它们是 api
            做出的决定，绝不是由 engine 报告终止而来。
        reason:
          type: string
          example: heartbeat_timeout
          description: 终止的来源，而不是通过排除法得出的诊断。
      required:
        - projectId
        - to
    RequestManualHandoffDto:
      type: object
      properties:
        toAgent:
          type: string
          example: staff
          description: 目标代理的 slug。必须是 lead 或没有区域的代理——绝不能是子代理。
      required:
        - toAgent
    RequestModelPullDto:
      type: object
      properties:
        repoId:
          type: string
          example: meta-llama/Llama-3.1-8B-Instruct-GGUF
          description: Hugging Face Hub 上的 `<publisher>/<modelo>`。
        estimatedSizeBytes:
          type: number
          example: 4900000000
          description: 当 Hub 公布了大小时来自搜索结果——绝不在客户端凭猜测估算。
      required:
        - repoId
    RequestPasswordResetDto:
      type: object
      properties:
        email:
          type: string
          example: fulano@brabo.dev
      required:
        - email
    ResetPasswordDto:
      type: object
      properties:
        token:
          type: string
          description: 通过邮件收到的一次性令牌。
        novaSenha:
          type: string
          example: another pretty long passphrase
          minLength: 12
      required:
        - token
        - novaSenha
    ResolvedBindingResponseDto:
      type: object
      properties:
        modelId:
          type: string
          example: 01JC4Z0000MODELO00000000001
        origin:
          type: string
          enum:
            - workspace
            - project
            - area
            - agent
            - session
          example: project
          description: >-
            该值来自哪个范围。级联顺序为 会话 → 代理 → 项目 →
            workspace，暴露来源正是让界面可以说“继承自项目”，而不是显示一个来历不明的值。
        routingPreference:
          type: string
          enum:
            - price
            - throughput
            - latency
          nullable: true
          example: null
          description: >-
            在级联中胜出的那个 binding 的路由偏好（ADR
            0166）。它从不独立级联：被跳过的层级会带走它的偏好，没有任何层级只继承另一层级的偏好。
        skipped:
          description: 级联在到达 `origin` 之前丢弃的更具体的范围。在正常路径上为空。
          type: array
          items:
            $ref: '#/components/schemas/SkippedBindingResponseDto'
        herdadoDoLead:
          type: string
          example: secops
          description: >-
            当所询问的代理没有自己的模型（其区域或项目也没有）并继承了其区域 lead 已解析的 binding 时出现（RN-703）。此时
            `origin` 是 lead binding 的来源。
      required:
        - modelId
        - origin
        - routingPreference
        - skipped
    ResultadoPorProviderResponseDto:
      type: object
      properties:
        provider:
          type: string
          enum:
            - ollama
            - anthropic
            - openai
            - openrouter
            - nvidia-nim
            - together
            - deepinfra
            - bitdeer
            - vultr
            - 9router
            - opencode-zen
          example: openai
        descobertos:
          type: number
          example: 3
          description: 数据库中原本不存在的模型。它们以未激活状态进入——激活是所有者的策展（RN-043）。
        reencontrados:
          type: number
          example: 1
          description: 曾为 `unavailable` 又回到目录中的模型。它们的 `isActive` 保持不变：所有者的选择在缺席期间得以保留。
        indisponibilizados:
          type: number
          example: 0
          description: 从远程目录中消失的模型。它们变为 `unavailable`，且绝不删除——binding 和成本历史都指向它们。
        pulado:
          type: string
          enum:
            - sem_capability
            - sem_credencial
            - falha
          description: 当提供商没有被同步时出现。被跳过的提供商不会把任何东西标记为不可用：“我不知道那里有什么”不等于“那里什么都没有”。
        origemDaFalha:
          type: string
          enum:
            - infra
            - modelo
          description: >-
            仅配合 `pulado: "falha"`。来源词汇来自 ADR 0020——从未连通提供商时为 `infra`，提供商响应但拒绝时为
            `modelo`。
        detalhe:
          type: string
          example: openai responded with status 401
      required:
        - provider
        - descobertos
        - reencontrados
        - indisponibilizados
    ReturnStoryDto:
      type: object
      properties:
        reason:
          type: string
          example: The acceptance criteria don't cover the payment refusal case.
          description: 理由是必需的：它会变成固定在 PO 会话中的消息，没有理由的退回等于交还了没有任何信息的工作。
      required:
        - reason
    RollbackResponseDto:
      type: object
      properties:
        agent:
          type: string
          example: dev-api
        restoredFrom:
          type: number
          example: 2
          description: 被恢复内容的版本。
        toVersion:
          type: number
          example: 5
          description: 回滚创建的新版本。历史不可变：恢复不会删除任何东西，而是新增。
        cacheInvalidated:
          type: boolean
          example: true
          description: engine 的指令缓存是否成功失效。
      required:
        - agent
        - restoredFrom
        - toVersion
        - cacheInvalidated
    RosterFactsResponseDto:
      type: object
      properties:
        executionActivated:
          type: boolean
          example: true
          description: 最近的会话已经记录了 `execution.activated`——正是这一点把按模块划分的 dev agent 带入名单。
        moduleNames:
          example:
            - api
            - web
          description: 来自当前 module_map（`version` 最高）的模块名称。执行激活后，每个模块一个 dev agent。
          type: array
          items:
            type: string
        gatesEverOpened:
          type: boolean
          example: true
          description: >-
            本会话中某个 PR gate（dev 或 infra）至少打开过一次——正是这一点把 QA 和 SecOps
            带入名单。覆盖整个会话，而不是最近 N 个事件的窗口。
        delegatedSubagents:
          example:
            - qa-automacao
          description: 在会话中至少记录过一次委派的子代理，无论结果如何——放弃委派是一项被记录的决定，而不是沉默。
          type: array
          items:
            type: string
        activatedAgents:
          example:
            - arquiteto
            - infra
          description: >-
            在会话中至少有一次 `agent.activated` 的代理，最近激活的在前（按
            `seq`），每个代理一条（RN-630）。覆盖整个会话，而不是 200 个事件的窗口：客户端将其与窗口合并，并且只有当
            `latestSessionId` 是它正在读取的会话时才信任它。
          type: array
          items:
            type: string
        infraActive:
          type: boolean
          example: false
          description: 最近的会话中存在一个 `accepted` handoff，目标为 `infra`。
        uxDesignerActive:
          type: boolean
          example: false
          description: 最近的会话中存在一个 `accepted` handoff，目标为 `ux-designer`（ADR 0087）。
        staffActive:
          type: boolean
          example: false
          description: >-
            最近的会话中存在一个 `accepted` handoff，目标为 `staff`（docs/fluxo.yml，ADR
            0088）——对自动触发处于休眠状态，只反映已被接受的手动激活。
      required:
        - executionActivated
        - moduleNames
        - gatesEverOpened
        - delegatedSubagents
        - activatedAgents
        - infraActive
        - uxDesignerActive
        - staffActive
    RoteamentoDeModuloInternalDto:
      type: object
      properties:
        modulo:
          type: string
          example: checkout-api
          description: 模块名称——必须存在于当前的 module_map 中。
        imagemCandidata:
          type: string
          example: node:22-bookworm-slim
          description: 此模块带有显式 TAG 或 digest 的 OCI 引用。`latest` 会以 400 拒绝——该规则位于领域层。
        porque:
          type: string
          example: >-
            This module is TypeScript on Node 22; the slim variant has the
            runtime and nothing else.
          description: 为什么这个模块用这个镜像。至少 10 个字符。
      required:
        - modulo
        - imagemCandidata
        - porque
    RoteamentoDeModuloResponseDto:
      type: object
      properties:
        modulo:
          type: string
          example: checkout-api
        imagemCandidata:
          type: string
          example: node:22-bookworm-slim
        porque:
          type: string
          example: >-
            This module is TypeScript on Node 22; the slim variant has the
            runtime and nothing else.
      required:
        - modulo
        - imagemCandidata
        - porque
    RoteamentoDeModulosGeradoResponseDto:
      type: object
      properties:
        roteamento:
          type: array
          items:
            $ref: '#/components/schemas/RoteamentoDeModuloResponseDto'
        version:
          type: number
          example: 1
      required:
        - roteamento
        - version
    RouteModulesToInfraInternalDto:
      type: object
      properties:
        projectId:
          type: string
          format: uuid
        roteamento:
          description: >-
            当前 module_map 的每个模块一项。候选镜像是 Architect 的提议，而不是决定——Infra
            在之后的步骤中从候选项中选择。
          type: array
          items:
            $ref: '#/components/schemas/RoteamentoDeModuloInternalDto'
      required:
        - projectId
        - roteamento
    RuleCoverageResponseDto:
      type: object
      properties:
        ruleId:
          type: string
          example: RN-014
        title:
          type: string
          example: Cart accepts at most 50 items
        coveredByStoryIds:
          example:
            - 01JC4Z0000HISTORIA000000001
          type: array
          items:
            type: string
        covered:
          type: boolean
          example: true
      required:
        - ruleId
        - title
        - coveredByStoryIds
        - covered
    RunInstructionsResponseDto:
      type: object
      properties:
        command:
          type: string
          example: cp .env.example .env && docker compose up --build
        urls:
          type: array
          items:
            $ref: '#/components/schemas/UrlDeExecucaoResponseDto'
        envExample:
          type: boolean
          example: true
          description: 该 PR 提交了一个运行前需要复制的 `.env.example`。
        app:
          description: >-
            AT-610 (ADR 0228): the command the container runs to start the app
            and the port it listens on. Absent when the Infra did not declare
            it.
          allOf:
            - $ref: '#/components/schemas/AppDeExecucaoResponseDto'
        qa:
          type: object
          description: >-
            AT-612 (ADR 0229): the infra services of the ephemeral QA instance
            declared by the Infra (`run.qa`). Absent when none.
        prUrl:
          type: string
          example: http://localhost:3000/git/acme/pulls/3
        version:
          type: number
          example: 1
        eventId:
          type: string
          example: 01JC4Z0000EVENTORUNINSTRUC01
        declaredAt:
          type: string
          example: '2026-10-11T12:00:00.000Z'
      required:
        - command
        - urls
        - envExample
        - prUrl
        - version
        - eventId
        - declaredAt
    RunLlmTurnDto:
      type: object
      properties:
        projectId:
          type: string
          format: uuid
          example: 01JC4Z0000PROJETO0000000001
        agentId:
          type: string
          example: dev-api
          description: 在团队面板中把成本归到此代理名下。
        messages:
          type: array
          items:
            type: object
            additionalProperties: true
          example:
            - role: user
              content: Read file X.
          description: 已由 engine 的 ContextManager 组装好的历史。
        tools:
          type: array
          items:
            type: object
            additionalProperties: true
          description: 提供给模型的工具；`parameters` 是 JSON Schema。
        catalogoCompleto:
          type: boolean
          example: true
          description: >-
            请求完整的工具目录：此次调用跳过 Jev 工具路由器（ADR 0179）。当某一步受限的菜单导致模型没有调用工具就作答、engine
            重复这一步时会设置它。
      required:
        - projectId
        - messages
    RunnerDeviceKeyListResponseDto:
      type: object
      properties:
        id:
          type: string
          example: 01JC4Z0000CHAVE000000000001
        name:
          type: string
          example: laptop
        projectId:
          type: object
          example: 01JC4Z0000PROJETO000000001
          nullable: true
          description: 为空 = 机器密钥（ADR 0154）：适用于其所有者的任何项目，而不只是这一个。见 `especie`。
        especie:
          type: string
          enum:
            - projeto
            - maquina
          example: projeto
          description: >-
            `projeto` = 绑定到此路由的项目（ADR 0118）。`maquina` = 描述的是机器（ADR
            0154），会出现在它所服务的每个项目的列表中——撤销它会断开所有这些项目中的本地代理。
        createdAt:
          type: string
          example: '2026-08-27T12:00:00.000Z'
          format: date-time
        revokedAt:
          type: object
          example: null
          nullable: true
          description: 为空 = 有效。已撤销的仍会出现在列表中。
        lastUsedAt:
          type: object
          example: null
          nullable: true
          description: 为空 = 从未使用——孤立密钥的信号（在 runner 自动配置流程中途关闭了标签页）。
      required:
        - id
        - name
        - projectId
        - especie
        - createdAt
        - revokedAt
        - lastUsedAt
    RunnerProjectResponseDto:
      type: object
      properties:
        projectId:
          type: string
          example: 01JC4Z0000PROJETO000000001
        name:
          type: string
          example: Brabo
        workspaceDirName:
          type: string
          example: brabo-01jc4z
          description: 项目文件夹的名称（RN-109）——机器基目录下的相对段，绝不是绝对路径。
        workspaceVerifiedAt:
          type: object
          example: null
          nullable: true
          description: >-
            runner 第一次确认该文件夹的时间（RN-423）。为空 =
            从未确认。它记录的是一次确认，不是心跳（RN-468）：并不说明文件夹此刻可用。
      required:
        - projectId
        - name
        - workspaceDirName
        - workspaceVerifiedAt
    RunnerTicketResponseDto:
      type: object
      properties:
        ticket:
          type: string
          description: >-
            不透明的一次性令牌，base64url 编码。TTL 30 秒：在第一次成功的 `connect/3`（于 `/runner`
            套接字上）之后或过期时失效。
        expiresAt:
          type: string
          example: '2026-08-09T12:00:30.000Z'
          description: 票据的过期时间（ISO 8601）。
        engineWsUrl:
          type: string
          example: ws://localhost:4000/runner
          description: >-
            Phoenix 套接字 `/runner` 的公开 URL，已是 ws(s):// 形式——客户端（runner CLI 或
            Web）可直接在参数中带上 `{ticket}` 打开连接。
      required:
        - ticket
        - expiresAt
        - engineWsUrl
    SemanticDuplicateCheckInternalDto:
      type: object
      properties:
        projectId:
          type: string
          format: uuid
          example: 01JC4Z0000PROJETO0000000001
        kind:
          type: string
          enum:
            - business_rule
          example: business_rule
        title:
          type: string
          example: Greeting with the caller name
          description: 刚刚追加的规则的标题。规范化后标题相同的规则（RN-080 让新的生效）不参与比较。
      required:
        - projectId
        - kind
        - title
    SemanticDuplicateCheckResponseDto:
      type: object
      properties:
        status:
          type: string
          enum:
            - warned
            - clean
            - skipped
            - nothing_to_compare
          description: >-
            `warned` 绝不阻塞：条目已经写入。`skipped` 携带原因——没有嵌入提供商、守护进程宕机、10
            秒上限——也会在事件日志中叙述。
        similarTo:
          $ref: '#/components/schemas/SemanticDuplicateSimilarDto'
        closest:
          nullable: true
          type: object
          allOf:
            - $ref: '#/components/schemas/SemanticDuplicateSimilarDto'
        similarity:
          type: number
          example: 0.83
        threshold:
          type: number
          example: 0.8
          description: 余弦阈值——一个起点，尚未校准（ADR 0198）。
        compared:
          type: number
          example: 12
          description: 比较了多少个已有条目（最近的，最多 100 个）。
        total:
          type: number
          example: 12
          description: 项目中共有多少个。
        reason:
          type: string
          example: provider "ollama" did not answer
        message:
          type: string
          nullable: true
          description: 代理在工具结果中读到的句子；无话可说时为 `null`。
      required:
        - status
        - message
    SemanticDuplicateSimilarDto:
      type: object
      properties:
        id:
          type: string
          example: 01JC4Z0000HISTORIA000000001
        title:
          type: string
          example: Deterministic public greeting endpoint
        similarity:
          type: number
          example: 0.83
      required:
        - id
        - title
    SendAgentMessageDto:
      type: object
      properties:
        text:
          type: string
          example: I want to allow discount coupons at checkout.
          minLength: 1
          maxLength: 10000
          description: 用户发给会话活跃代理的消息。
      required:
        - text
    SendChatMessageDto:
      type: object
      properties:
        text:
          type: string
          example: Summarize what we have already decided about checkout.
          description: 用户的消息。响应以 SSE 流返回。
      required:
        - text
    SessaoResponseDto:
      type: object
      properties:
        accessToken:
          type: string
          description: 短期有效的 EdDSA JWT。
        expiresIn:
          type: number
          example: 900
          description: 访问令牌过期前的秒数。
        locale:
          type: string
          example: pt-BR
          enum:
            - pt-BR
            - en
            - ru
            - zh-Hans
          description: >-
            用户的语言偏好（i18n 基础，Wave 6a）。它放在这里——而不是单独调用——这样 Web
            应用永远不需要额外一次往返只为知道用哪种语言渲染界面。
      required:
        - accessToken
        - expiresIn
        - locale
    SessionAgentTokenUsageResponseDto:
      type: object
      properties:
        actorId:
          type: string
          example: dev-api
          description: 代理的 slug，或用户的 id。
        costMicros:
          type: number
          example: 3400000
          description: 成本，以微美元计。
        inputTokens:
          type: number
          example: 128000
        outputTokens:
          type: number
          example: 14200
        lastCall:
          nullable: true
          description: >-
            The agent's latest call in the session (Jev excluded); null when
            none.
          type: object
          allOf:
            - $ref: '#/components/schemas/LastCallResponseDto'
      required:
        - actorId
        - costMicros
        - inputTokens
        - outputTokens
        - lastCall
    SessionContextSummaryResponseDto:
      type: object
      properties:
        sessoesLidas:
          description: 所请求的会话，加上其已批准的 Dev Lead 计划所激活的执行会话（RN-828）。
          type: array
          items:
            type: string
        regras:
          type: number
          example: 11
          description: 整个会话中的 `artifact.business_rule`。
        decisoes:
          type: number
          example: 6
          description: 整个会话中的 `artifact.decision_record`。
        itensDeRegras:
          description: 会话中最新的业务规则，上限 50 条；总数为 `regras`（RN-844）。
          type: array
          items:
            $ref: '#/components/schemas/ItemDeRegraResponseDto'
        itensDeDecisoes:
          description: 会话中最新的决策记录，上限 50 条；总数为 `decisoes`（RN-844）。
          type: array
          items:
            $ref: '#/components/schemas/ItemDeDecisaoResponseDto'
        artefatosPorAgente:
          type: array
          items:
            $ref: '#/components/schemas/ArtefatosDoAgenteResponseDto'
        arquivosTocados:
          description: >-
            此会话及关联执行会话中打开的 PR 的文件（从 PR diff 读取，RN-844），然后是
            `git_commit`/`git_push` 载荷中的文件。
          allOf:
            - $ref: '#/components/schemas/ArquivosTocadosResponseDto'
      required:
        - sessoesLidas
        - regras
        - decisoes
        - itensDeRegras
        - itensDeDecisoes
        - artefatosPorAgente
        - arquivosTocados
    SessionEventResponseDto:
      type: object
      properties:
        id:
          type: string
          example: 01JC4Z8QK3M7YV2N5T9B0PXHRB
          description: 事件的 ULID。
        sessionId:
          type: string
          example: 01JC4Z8QK3M7YV2N5T9B0PXHRA
        seq:
          type: number
          example: 41
          description: 在会话中的顺序。单调递增且无间隙。
        type:
          type: string
          example: chat.message
          description: 事件类型。完整清单见 `docs/reference/events.md`。
        actor:
          $ref: '#/components/schemas/ActorResponseDto'
        payload:
          type: object
          additionalProperties: true
          example:
            role: assistant
            content: I'll open the epic first.
          description: 自由形态，因 `type` 而异。见事件参考。
        createdAt:
          type: string
          example: '2026-07-27T14:30:58.512Z'
          format: date-time
      required:
        - id
        - sessionId
        - seq
        - type
        - actor
        - payload
        - createdAt
    SessionListItemResponseDto:
      type: object
      properties:
        id:
          type: string
          example: 01JC4Z8QK3M7YV2N5T9B0PXHRA
          description: 会话的 ULID。
        projectId:
          type: string
          example: 01JC4Z0000PROJETO0000000001
        createdBy:
          type: string
          example: 01JC4Z0000USUARIO0000000001
          description: 打开会话的人。
        status:
          type: string
          enum:
            - created
            - active
            - closing
            - closed
            - closed_abnormally
          example: active
          description: >-
            显式状态机：created → active → closing → closed |
            closed_abnormally。无效的转换返回 409。
        kind:
          type: string
          enum:
            - consultiva
            - criativa
          example: criativa
          description: >-
            打开会话时的意图，在创建时选择且不可变。`consultiva`（咨询式）只是对话；`criativa`（创作式）产出内容，并且是唯一会进入执行的。不要与执行状态混淆，执行状态仍是日志中的
            `execution.activated` 事件。
        name:
          type: object
          example: Cart checkout
          nullable: true
          description: 友好名称，或 `null`。界面会将其与 id 的井号标签组合；它从不取代井号标签。
        nextSeq:
          type: number
          example: 42
          description: 事件日志的下一个 `seq`。用作游标：`?afterSeq=41` 获取 41 之后的所有内容。
        createdAt:
          type: string
          example: '2026-07-27T14:03:22.187Z'
          format: date-time
        updatedAt:
          type: string
          example: '2026-07-27T14:31:09.004Z'
          format: date-time
        closedAt:
          type: object
          example: null
          format: date-time
          nullable: true
          description: 仅在终止状态下填写。
        terminationReason:
          type: object
          example: null
          nullable: true
          description: >-
            engine
            终止时报告的原因（heartbeat_timeout、conversation_idle_timeout、killed、exception……）。人工关闭或会话仍存活时为
            `null`。
        traceParent:
          type: object
          example: 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01
          nullable: true
          description: 根 span 的 W3C `traceparent`。正是靠它在 Tempo 中找回整个会话。
        technical:
          type: boolean
          example: false
          description: >-
            这是否是由仓库置备打开的技术会话（从 `repo_bootstraps`
            关联的那个），而不是工作会话。依据的是关联，绝不是名称：重命名会话不会改变它。挑选“最近会话”的界面会跳过它，除非它是唯一的会话。
      required:
        - id
        - projectId
        - createdBy
        - status
        - kind
        - name
        - nextSeq
        - createdAt
        - updatedAt
        - closedAt
        - terminationReason
        - traceParent
        - technical
    SessionPendingWorkResponseDto:
      type: object
      properties:
        pending:
          type: boolean
          example: true
          description: >-
            存在因标签页不活跃而阻止关闭的工作：一个 `offered` handoff、一个 `pending`
            操作、一个处于回合中的代理、一个正在工作或被阻塞的 dev agent，或一个正在等待用户的对话式代理（RN-064，RN-581）。
        motivo:
          type: object
          example: handoff po → arquiteto aguardando aceite
          nullable: true
          description: 悬而未决的是什么。没有时为 `null`。
        aguardandoUsuarioDesde:
          type: string
          example: '2026-09-18T12:00:00.000Z'
          nullable: true
          description: >-
            仅当唯一的待办是正在等待用户的对话式代理时设置（RN-581）：其回合结束的时刻。这是唯一带有上限的待办信号，由 engine
            应用（`SESSION_CONVERSATION_IDLE_TIMEOUT_MS`，默认 8 小时）——超过后会话以
            `conversation_idle_timeout` 关闭。其他情况下为 `null`。
      required:
        - pending
        - motivo
        - aguardandoUsuarioDesde
    SessionResponseDto:
      type: object
      properties:
        id:
          type: string
          example: 01JC4Z8QK3M7YV2N5T9B0PXHRA
          description: 会话的 ULID。
        projectId:
          type: string
          example: 01JC4Z0000PROJETO0000000001
        createdBy:
          type: string
          example: 01JC4Z0000USUARIO0000000001
          description: 打开会话的人。
        status:
          type: string
          enum:
            - created
            - active
            - closing
            - closed
            - closed_abnormally
          example: active
          description: >-
            显式状态机：created → active → closing → closed |
            closed_abnormally。无效的转换返回 409。
        kind:
          type: string
          enum:
            - consultiva
            - criativa
          example: criativa
          description: >-
            打开会话时的意图，在创建时选择且不可变。`consultiva`（咨询式）只是对话；`criativa`（创作式）产出内容，并且是唯一会进入执行的。不要与执行状态混淆，执行状态仍是日志中的
            `execution.activated` 事件。
        name:
          type: object
          example: Cart checkout
          nullable: true
          description: 友好名称，或 `null`。界面会将其与 id 的井号标签组合；它从不取代井号标签。
        nextSeq:
          type: number
          example: 42
          description: 事件日志的下一个 `seq`。用作游标：`?afterSeq=41` 获取 41 之后的所有内容。
        createdAt:
          type: string
          example: '2026-07-27T14:03:22.187Z'
          format: date-time
        updatedAt:
          type: string
          example: '2026-07-27T14:31:09.004Z'
          format: date-time
        closedAt:
          type: object
          example: null
          format: date-time
          nullable: true
          description: 仅在终止状态下填写。
        terminationReason:
          type: object
          example: null
          nullable: true
          description: >-
            engine
            终止时报告的原因（heartbeat_timeout、conversation_idle_timeout、killed、exception……）。人工关闭或会话仍存活时为
            `null`。
        traceParent:
          type: object
          example: 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01
          nullable: true
          description: 根 span 的 W3C `traceparent`。正是靠它在 Tempo 中找回整个会话。
      required:
        - id
        - projectId
        - createdBy
        - status
        - kind
        - name
        - nextSeq
        - createdAt
        - updatedAt
        - closedAt
        - terminationReason
        - traceParent
    SessionResponseLanguageResponseDto:
      type: object
      properties:
        language:
          type: string
          example: pt-BR
          description: 有效语言。
        origin:
          type: string
          enum:
            - sessao
            - conta
            - detectado
            - interface
          example: interface
          description: >-
            `language` 的来源：`sessao`（在此会话中固定）> `conta`（账户选择）>
            `detectado`（检测到且已确认）> `interface`（界面语言）。
        sessionOverride:
          type: string
          nullable: true
          example: null
        account:
          type: string
          example: automatico
          description: '`automatico` 或在账户中选择的代码。'
        detected:
          type: string
          nullable: true
          example: null
        interfaceLocale:
          type: string
          enum:
            - pt-BR
            - en
            - ru
            - zh-Hans
          example: pt-BR
        detectionQuestion:
          type: string
          nullable: true
          example: es
          description: >-
            你最近的消息所指向的语言，当界面应当询问是否用它来回答时（RN-624）——`null` 表示没有问题。检测本身从不改变偏好：只有回答
            `confirm`（在 `POST /users/me/preferences/detected-language`
            上）才会。当有效语言来自明确选择（此会话或账户）、已经是检测到的语言，或你之前拒绝过这种语言时，绝不询问。尽力而为：检测失败为
            `null`，绝不是此路由的错误。
      required:
        - language
        - origin
        - sessionOverride
        - account
        - detected
        - interfaceLocale
        - detectionQuestion
    SetAgentAutonomyDto:
      type: object
      properties:
        agentId:
          type: string
          example: dev-api
          description: 代理的 slug。
        actionType:
          type: string
          enum:
            - terminal
            - git_commit
            - git_push
            - pr_open
            - spend
            - git_repo_create
            - git_branch_create
            - git_branch_protect
            - write_file
            - open_adr_pr
            - git_merge
            - open_infra_pr
            - instruction_patch
            - parallelize
            - raise_max_parallel
            - propose_execution_plan
            - assess_implementability
            - container_start
            - container_stop
            - container_remove
            - container_recreate
            - container_start_via_runner
            - qa_environment_start
            - '*'
          example: terminal
          description: >-
            操作类型，或 `"*"` 表示该代理的所有操作类型——即 ApprovalCard
            的“自动模式”（RN-153）。特定规则始终优先于通配符：记录 `terminal: deny` 时，即使已开启 `"*":
            auto_approve`，仍会拒绝终端操作。
        mode:
          type: string
          enum:
            - auto_approve
            - require_approval
            - deny
          example: auto_approve
          description: 该代理对此类型的自主权。不会覆盖 `permissions.json`：`deny` 中的模式无论代理有多少自主权都保持阻止。
      required:
        - agentId
        - actionType
        - mode
    SetAreaBudgetDto:
      type: object
      properties:
        limitUsd:
          type: object
          example: 20
          minimum: 0
          nullable: true
          description: >-
            区域的上限，以美元计——与项目/会话预算（`BudgetsController`）相同的美元输入、微美元输出约定。`null`
            清除上限：区域恢复为无限制，这也是从未设置过上限的区域的默认值。
      required:
        - limitUsd
    SetMaxParallelDto:
      type: object
      properties:
        maxParallel:
          type: number
          example: 4
          minimum: 1
          description: >-
            区域 lead 在会话中无需请求你授权即可拥有的代理数量。最小为
            1——零不表示“无限制”，而是无效配置，把它当作无限制会让一个笔误变成不受限制的花费。
      required:
        - maxParallel
    SetMirrorPathDto:
      type: object
      properties:
        mirrorPath:
          type: string
          nullable: true
          example: /home/you/mirrors/store
          description: >-
            用户机器上的绝对路径，本地代理会把项目工作复制到这里（RN-515，ADR 0147）。发送 `null`——该键是必需的，省略返回
            400——以清除目标并关闭镜像；`null` 是项目的正常状态，不是错误。只有 `mounted`/`runner` 项目可以有：在
            `container` 中，来源是本地代理看不到的服务端托管卷，请求会以 400 拒绝并说明这一原因。API
            只校验词法形态（绝对路径，不含 `..`/`.`，绝不是根目录、系统文件夹或与 Brabo
            自身检出目录重叠）以及来源↔目标循环的两个方向（目标不能位于 `workspacePath`
            内部，也不能包含它）——它从不触碰磁盘，因为它看不到目标所在的机器，与 `runner`
            模式完全一样（RN-423）。解析符号链接是本地代理负责的那一半防护，而不是这里。
      required:
        - mirrorPath
    SetModelBindingDto:
      type: object
      properties:
        modelId:
          type: string
          format: uuid
          example: 9b1c2d3e-4f50-4a61-8b72-0c3d4e5f6a7b
          description: 要固定到此范围的模型。必须存在于 `GET /models` 中。
        routingPreference:
          type: string
          enum:
            - price
            - throughput
            - latency
          nullable: true
          example: throughput
          description: >-
            中转平台如何选择服务该模型的上游（ADR 0166，RN-583）。缺省时，若新模型的提供商接受则保留已存储的值，否则清除；`null`
            清除它；为提供商未声明 `routingPreference` 能力（`GET
            /llm/provider-capabilities`）的模型设置值会以 422 拒绝。它随此 binding 一起传递：从不独立级联。
      required:
        - modelId
    SetModelsActiveDto:
      type: object
      properties:
        modelIds:
          example:
            - 9b1c2d3e-4f50-4a61-8b72-0c3d4e5f6a7b
          description: 要开启或关闭的模型。整批全有或全无。
          type: array
          items:
            type: string
            format: uuid
        isActive:
          type: boolean
          example: true
          description: '`true` 让模型出现在选择器中并能接收新的 binding。不改动 `availability`，那是同步在提供商处观测到的。'
      required:
        - modelIds
        - isActive
    SetModelUsesDto:
      type: object
      properties:
        modelIds:
          example:
            - 9b1c2d3e-4f50-4a61-8b72-0c3d4e5f6a7b
          description: 要标记的模型。整批全有或全无。
          type: array
          items:
            type: string
            format: uuid
        uses:
          type: array
          example:
            - codigo
            - analise
          description: >-
            完整的用途列表——替换之前的列表，而不是追加。空列表会取消全部标记。封闭词汇：词汇之外的用途会让调用失败，因为自由文本会在同一个过滤器中出现
            `code`、`coding` 和 `código`。
          items:
            type: string
            enum:
              - codigo
              - documentacao
              - analise
              - imagem
              - conversa
      required:
        - modelIds
        - uses
    SetProjectPermissionsDto:
      type: object
      properties:
        allow:
          example:
            - Terminal(pnpm test:*)
            - Terminal(pnpm lint)
          description: 自动批准的模式。
          type: array
          items:
            type: string
        deny:
          example:
            - Terminal(rm -rf *)
            - Terminal(git push --force*)
          description: 禁止的模式。`deny` 优先于 `allow`，也优先于代理的自主权。
          type: array
          items:
            type: string
        ask:
          example:
            - Terminal(pnpm add *)
          description: 始终需要人工决定的模式。
          type: array
          items:
            type: string
      required:
        - allow
        - deny
        - ask
    SetSessionResponseLanguageDto:
      type: object
      properties:
        language:
          type: string
          nullable: true
          example: en
          maxLength: 35
          description: >-
            代理仅在此会话中回答你所使用的语言（RN-618）——其他参与者绝不受影响。服务器能识别的任何 BCP-47 代码，以规范形式存储。发送
            `null`（或 `automatico`）——该键是必需的——以恢复为从你的账户继承。
      required:
        - language
    SetToolRouterDto:
      type: object
      properties:
        enabled:
          type: boolean
          example: false
          description: 是否由 Jev 为代理的每一步选择工具。默认开启；仅当回合模型来自 OpenRouter 时才起作用。
      required:
        - enabled
    SkippedBindingResponseDto:
      type: object
      properties:
        scope:
          type: string
          enum:
            - workspace
            - project
            - area
            - agent
            - session
          example: agent
        modelId:
          type: string
          example: 01JC4Z0000MODELO00000000001
        reason:
          type: string
          enum:
            - unavailable
            - sem_tool_calling
          example: unavailable
          description: >-
            `unavailable`：模型已从提供商目录中消失。`sem_tool_calling`：请求来自代理而模型只能聊天——级联会在每个层级重新校验该能力，以免悄悄违反
            RN-040。
      required:
        - scope
        - modelId
        - reason
    SocketTicketResponseDto:
      type: object
      properties:
        ticket:
          type: string
          description: 不透明的一次性令牌，base64url 编码。TTL 30 秒，一次性使用：在第一次成功 `connect/3` 之后或过期时失效。
        expiresAt:
          type: string
          example: '2026-08-09T12:00:30.000Z'
          description: 票据的过期时间（ISO 8601）。
      required:
        - ticket
        - expiresAt
    SpendLinhaResponseDto:
      type: object
      properties:
        chave:
          type: string
          example: anthropic/claude-sonnet-4
          description: 分组键：模型名称、提供商名称、项目 id、参与者 id 或会话 id，取决于该行出现在哪个列表中。
        rotulo:
          type: object
          example: Loja
          nullable: true
          description: 可读名称，当存在带名称的表时（项目）。当键本身就是标签时为 `null`。
        actorKind:
          type: object
          example: agent
          nullable: true
          description: 只有按参与者的列表会填写（`porAtor`、`porOwner`、`porAgente`）；其他列表中为 `null`。
        costMicros:
          type: number
          example: 1250000
        assinaturaMicros:
          type: number
          example: 0
          description: >-
            Part of the cost from models curated as `subscription` (RN-954): a
            seat, shown apart from the US$ paid (`cost - subscription`).
        inputTokens:
          type: number
          example: 120000
        outputTokens:
          type: number
          example: 35000
        chamadas:
          type: number
          example: 42
      required:
        - chave
        - rotulo
        - actorKind
        - costMicros
        - assinaturaMicros
        - inputTokens
        - outputTokens
        - chamadas
    SpendPorDiaResponseDto:
      type: object
      properties:
        dia:
          type: string
          example: '2026-08-09'
          description: UTC 日期。序列是稠密的：没有花费的日子以零出现。
        costMicros:
          type: number
          example: 1250000
        chamadas:
          type: number
          example: 42
      required:
        - dia
        - costMicros
        - chamadas
    StoryComTarefasResponseDto:
      type: object
      properties:
        id:
          type: string
          example: 01JC4Z0000HISTORIA000000001
        epicId:
          type: string
          example: 01JC4Z0000EPICO000000000001
        projectId:
          type: string
          example: 01JC4Z0000PROJETO0000000001
        sessionId:
          type: string
          example: 01JC4Z8QK3M7YV2N5T9B0PXHRA
        title:
          type: string
          example: Add item to cart
        description:
          type: string
          example: As a buyer, I want to gather items before paying.
        rf:
          example:
            - The cart accepts up to 50 items
          description: 功能需求。
          type: array
          items:
            type: string
        rnf:
          example:
            - The response stays under 200 ms at p95
          description: 非功能需求。
          type: array
          items:
            type: string
        businessRuleIds:
          example:
            - RN-014
          description: 所覆盖的业务规则。规则→story 的覆盖情况就是根据这里在 `GET /projects/:id/coverage` 中计算的。
          type: array
          items:
            type: string
        dod:
          example:
            - Unit tests green
          description: 完成的定义（DoD）。
          type: array
          items:
            type: string
        dor:
          example:
            - Module defined
          description: 就绪的定义（DoR）。
          type: array
          items:
            type: string
        moduleIds:
          example:
            - api
          description: story 涉及的来自 `module_map` 的模块。没有模块或模块不存在的 story 会变成一项架构待办。
          type: array
          items:
            type: string
        status:
          type: string
          enum:
            - draft
            - ready
            - in_progress
            - done
          example: ready
        proposedReady:
          type: boolean
          example: true
          description: >-
            PO 已完成该 story，正在等待用户的决定（Phase 12c——RN-048）。与 `status: "draft"`
            共存：它是一个提议，不是一个状态。始终为 `false`，如果项目处于 `auto` 模式。
        returnedReason:
          type: object
          example: DoD too generic — spell out the acceptance criteria.
          nullable: true
          description: 用户为何把 story 退回给 PO。从未被退回时为 `null`。
        returnedAt:
          type: object
          example: '2026-08-02T14:00:00.000Z'
          format: date-time
          nullable: true
        archivedAt:
          type: string
          example: null
          format: date-time
          description: >-
            story 被归档的时间（RN-727）。已归档的 story 离开 backlog、覆盖、计划和认领；`null` 表示仍在进行中的
            story。
          nullable: true
        archivedReason:
          type: string
          example: null
          description: 归档的原因，如果有人说明的话（RN-727）。
          nullable: true
        createdAt:
          type: string
          example: '2026-07-25T09:00:00.000Z'
          format: date-time
        updatedAt:
          type: string
          example: '2026-07-25T09:40:00.000Z'
          format: date-time
        tasks:
          type: array
          items:
            $ref: '#/components/schemas/TaskResponseDto'
      required:
        - id
        - epicId
        - projectId
        - sessionId
        - title
        - description
        - rf
        - rnf
        - businessRuleIds
        - dod
        - dor
        - moduleIds
        - status
        - proposedReady
        - returnedReason
        - returnedAt
        - archivedAt
        - archivedReason
        - createdAt
        - updatedAt
        - tasks
    StoryResponseDto:
      type: object
      properties:
        id:
          type: string
          example: 01JC4Z0000HISTORIA000000001
        epicId:
          type: string
          example: 01JC4Z0000EPICO000000000001
        projectId:
          type: string
          example: 01JC4Z0000PROJETO0000000001
        sessionId:
          type: string
          example: 01JC4Z8QK3M7YV2N5T9B0PXHRA
        title:
          type: string
          example: Add item to cart
        description:
          type: string
          example: As a buyer, I want to gather items before paying.
        rf:
          example:
            - The cart accepts up to 50 items
          description: 功能需求。
          type: array
          items:
            type: string
        rnf:
          example:
            - The response stays under 200 ms at p95
          description: 非功能需求。
          type: array
          items:
            type: string
        businessRuleIds:
          example:
            - RN-014
          description: 所覆盖的业务规则。规则→story 的覆盖情况就是根据这里在 `GET /projects/:id/coverage` 中计算的。
          type: array
          items:
            type: string
        dod:
          example:
            - Unit tests green
          description: 完成的定义（DoD）。
          type: array
          items:
            type: string
        dor:
          example:
            - Module defined
          description: 就绪的定义（DoR）。
          type: array
          items:
            type: string
        moduleIds:
          example:
            - api
          description: story 涉及的来自 `module_map` 的模块。没有模块或模块不存在的 story 会变成一项架构待办。
          type: array
          items:
            type: string
        status:
          type: string
          enum:
            - draft
            - ready
            - in_progress
            - done
          example: ready
        proposedReady:
          type: boolean
          example: true
          description: >-
            PO 已完成该 story，正在等待用户的决定（Phase 12c——RN-048）。与 `status: "draft"`
            共存：它是一个提议，不是一个状态。始终为 `false`，如果项目处于 `auto` 模式。
        returnedReason:
          type: object
          example: DoD too generic — spell out the acceptance criteria.
          nullable: true
          description: 用户为何把 story 退回给 PO。从未被退回时为 `null`。
        returnedAt:
          type: object
          example: '2026-08-02T14:00:00.000Z'
          format: date-time
          nullable: true
        archivedAt:
          type: string
          example: null
          format: date-time
          description: >-
            story 被归档的时间（RN-727）。已归档的 story 离开 backlog、覆盖、计划和认领；`null` 表示仍在进行中的
            story。
          nullable: true
        archivedReason:
          type: string
          example: null
          description: 归档的原因，如果有人说明的话（RN-727）。
          nullable: true
        createdAt:
          type: string
          example: '2026-07-25T09:00:00.000Z'
          format: date-time
        updatedAt:
          type: string
          example: '2026-07-25T09:40:00.000Z'
          format: date-time
      required:
        - id
        - epicId
        - projectId
        - sessionId
        - title
        - description
        - rf
        - rnf
        - businessRuleIds
        - dod
        - dor
        - moduleIds
        - status
        - proposedReady
        - returnedReason
        - returnedAt
        - archivedAt
        - archivedReason
        - createdAt
        - updatedAt
    StreamLlmTurnDto:
      type: object
      properties:
        projectId:
          type: string
          format: uuid
          example: 01JC4Z0000PROJETO0000000001
        agentId:
          type: string
          example: criativo
        messages:
          type: array
          items:
            type: object
            additionalProperties: true
          example:
            - role: user
              content: Summarize what we decided.
        tools:
          type: array
          items:
            type: object
            additionalProperties: true
        catalogoCompleto:
          type: boolean
          example: true
          description: >-
            请求完整的工具目录：此次调用跳过 Jev 工具路由器（ADR 0179）。当某一步受限的菜单导致模型没有调用工具就作答、engine
            重复这一步时会设置它。
      required:
        - projectId
        - messages
    SugestaoDoCatalogoResponseDto:
      type: object
      properties:
        cobranca:
          type: string
          enum:
            - api
            - subscription
            - free
          nullable: true
          description: 'Suggested billing band (9Router alias). Null: no suggestion.'
        preco:
          nullable: true
          description: Manufacturer list price; null when the table has no row.
          type: object
          allOf:
            - $ref: '#/components/schemas/PrecoSugeridoResponseDto'
      required:
        - cobranca
        - preco
    SweepQaEnvironmentsInternalDto:
      type: object
      properties:
        taskIdsEmVoo:
          description: >-
            Task ids that still have a gate in flight; every other active
            instance is removed.
          type: array
          items:
            type: string
        projectId:
          type: string
          format: uuid
          description: Limit the sweep to one project.
      required:
        - taskIdsEmVoo
    SweepQaEnvironmentsResponseDto:
      type: object
      properties:
        removidas:
          type: array
          items:
            type: string
        falhas:
          type: array
          items:
            type: object
            properties:
              taskId:
                type: string
              motivo:
                type: string
      required:
        - removidas
        - falhas
    SyncModelCatalogResponseDto:
      type: object
      properties:
        porProvider:
          type: array
          items:
            $ref: '#/components/schemas/ResultadoPorProviderResponseDto'
      required:
        - porProvider
    TarefaEmCursoDeDevResponseDto:
      type: object
      properties:
        projectId:
          type: string
          format: uuid
        taskId:
          type: string
          format: uuid
        assignedTo:
          type: string
          example: dev-api
          description: 持有它的 dev agent。
      required:
        - projectId
        - taskId
        - assignedTo
    TaskResponseDto:
      type: object
      properties:
        id:
          type: string
          example: 01JC4Z0000TAREFA00000000001
        storyId:
          type: string
          example: 01JC4Z0000HISTORIA000000001
        title:
          type: string
          example: POST /cart/items endpoint
        description:
          type: string
          example: Accepts SKU and quantity; validates stock.
        status:
          type: string
          enum:
            - todo
            - in_progress
            - in_review
            - done
          example: in_progress
        assignedTo:
          type: object
          example: dev-api
          nullable: true
          description: 认领该任务的 dev agent 的 slug。
        blocked:
          type: boolean
          example: false
          description: 任务被阻塞，等待决定或依赖。
        blockedReason:
          type: object
          example: null
          nullable: true
        blockedOrigin:
          type: string
          enum:
            - infra
            - modelo
            - codigo
            - politica
          example: null
          nullable: true
          description: >-
            阻塞的来源（ADR 0020/0038），如已知。Phase 4a 的所有阻塞均为 `null`——只有 QA Lead（Phase
            8b）会填写。
        gateStatus:
          type: string
          enum:
            - awaiting_qa
            - awaiting_secops
            - awaiting_user
          example: null
          nullable: true
          description: 任务的 PR 在 gate 流水线中的位置：QA，然后 SecOps，然后用户。没有打开 PR 时为 `null`。
        gateCorrectionCount:
          type: number
          example: 0
          description: gate 已经要求了多少轮修正。
        module:
          type: object
          example: api
          nullable: true
          description: >-
            任务所属的模块，由 Dev Lead 在已批准的执行计划中分配（RN-678）。只有该模块的 dev agent
            会认领它。在没有计划分配之前为 `null`——这样的任务只有在其 story 恰好只有一个模块时才可被认领。
        createdAt:
          type: string
          example: '2026-07-25T10:00:00.000Z'
          format: date-time
        updatedAt:
          type: string
          example: '2026-07-25T12:30:00.000Z'
          format: date-time
      required:
        - id
        - storyId
        - title
        - description
        - status
        - assignedTo
        - blocked
        - blockedReason
        - blockedOrigin
        - gateStatus
        - gateCorrectionCount
        - module
        - createdAt
        - updatedAt
    TextDiffResponseDto:
      type: object
      properties:
        lines:
          type: array
          items:
            $ref: '#/components/schemas/DiffLineResponseDto'
        additions:
          type: number
          example: 3
        deletions:
          type: number
          example: 1
      required:
        - lines
        - additions
        - deletions
    ToolCallResponseDto:
      type: object
      properties:
        id:
          type: string
          example: call_01
        name:
          type: string
          example: read_file
        arguments:
          type: object
          additionalProperties: true
          example:
            path: src/index.ts
          description: 已经反序列化；engine 按 `name` 分派到对应工具。
      required:
        - id
        - name
        - arguments
    ToolRoutingResponseDto:
      type: object
      properties:
        modelo:
          type: string
          example: typesafe/jev-1.13
        ofertadas:
          type: number
          example: 9
          description: 代理在这一步拥有多少个工具。
        menuAntes:
          description: 路由器之前的工具名称。
          type: array
          items:
            type: string
        menuDepois:
          description: 提供给聊天模型的工具名称（任何回落时为整个目录）。
          type: array
          items:
            type: string
        escolha:
          type: object
          nullable: true
          example: create_story
        confianca:
          type: object
          nullable: true
          example: 0.91
        segunda:
          type: object
          nullable: true
          additionalProperties: true
          example:
            opcao: create_task
            probabilidade: 0.06
        anterior:
          type: object
          nullable: true
          example: read_file
          description: 同一次运行中的上一个工具。
        aplicado:
          type: boolean
          description: Jev 作出了回答，且菜单比目录更小。
        motivoDaQueda:
          type: string
          nullable: true
          enum:
            - timeout
            - erro_http
            - erro_de_rede
            - resposta_invalida
            - escolha_fora_das_opcoes
            - estado_grande
            - colisao_de_nome
        origemDaQueda:
          type: string
          nullable: true
          enum:
            - infra
            - modelo
            - codigo
        detalheDaQueda:
          type: object
          nullable: true
        latenciaMs:
          type: number
          example: 212
        custoMicros:
          type: number
          example: 52
          description: Jev 回答的真实成本（`usage.cost`），以微美元计。
        gastoNaoRegistrado:
          type: boolean
          description: '`true` 表示 Jev 已计费，但 `token_usage` 记录无法写入。'
      required:
        - modelo
        - ofertadas
        - menuAntes
        - menuDepois
        - escolha
        - confianca
        - segunda
        - anterior
        - aplicado
        - motivoDaQueda
        - origemDaQueda
        - detalheDaQueda
        - latenciaMs
        - custoMicros
        - gastoNaoRegistrado
    TransferOwnershipDto:
      type: object
      properties:
        userId:
          type: string
          format: uuid
          example: 3f1b2c8e-5a4d-4b7e-9c10-2d6f8a1b4c33
          description: 将成为记录所有者的用户 id。必须已经是此 workspace 的 `owner`。
      required:
        - userId
    TransitionSessionDto:
      type: object
      properties:
        status:
          type: string
          enum:
            - created
            - active
            - closing
            - closed
            - closed_abnormally
          example: closing
          description: 目标状态。状态机会以 409 拒绝无效的跳转——从 `created` 只能到 `active`，并且从终止状态无法返回。
      required:
        - status
    UnblockTaskInternalDto:
      type: object
      properties:
        projectId:
          type: string
          format: uuid
          example: 01JC4Z0000PROJETO0000000001
        agentId:
          type: string
          example: dev-api
          description: 因短暂失败而安排了重试的 dev agent。
      required:
        - projectId
        - agentId
    UnreadCursorDto:
      type: object
      properties:
        projectId:
          type: string
          format: uuid
          example: 01JC4Z0000PROJETO0000000001
        afterSeq:
          type: number
          example: 41
          minimum: 0
          description: 此浏览器在该项目中已看过的最后一个 `seq`。返回 `seq` 严格更大的事件。`0` 表示“什么都没看过”，会带来最近会话的开头。
      required:
        - projectId
        - afterSeq
    UnreadEventsDto:
      type: object
      properties:
        cursors:
          maxItems: 200
          description: >-
            每个要查询的项目一个游标。空列表返回空列表——“我什么都没要”不等于“全都给我”。来自其他 workspace
            的项目会被静默忽略，因为游标来自调用方的本地存储，可能带有旧 workspace 的残留。
          type: array
          items:
            $ref: '#/components/schemas/UnreadCursorDto'
      required:
        - cursors
    UpdateModelBillingDto:
      type: object
      properties:
        billing:
          type: string
          enum:
            - api
            - subscription
            - free
          example: subscription
      required:
        - billing
    UpdateModelPricingDto:
      type: object
      properties:
        inputPricePerMillionMicros:
          type: number
          example: 2500000
          description: 每百万输入 token 的价格，以微美元计。
        outputPricePerMillionMicros:
          type: number
          example: 10000000
          description: 每百万输出 token 的价格，以微美元计。
      required:
        - inputPricePerMillionMicros
        - outputPricePerMillionMicros
    UpdateProjectDto:
      type: object
      properties:
        name:
          type: string
          example: Checkout
          minLength: 2
        slug:
          type: string
          example: checkout
          pattern: ^[a-z0-9]+(-[a-z0-9]+)*$
          description: kebab-case。在 workspace 内唯一。
        maxConsecutiveBlocked:
          type: number
          example: 3
          description: >-
            熔断器（Phase 12b——RN-047）：连续多少个任务以阻塞结束会让该模块的 dev agent 停在
            idle_tripped。省略时使用领域默认值（DEFAULT_MAX_CONSECUTIVE_BLOCKED）。从下一次激活执行起生效——不影响已在运行的代理。
        storyPromotion:
          type: string
          enum:
            - manual
            - auto
          example: auto
          description: >-
            谁把 story 从 `draft` 提升为 `ready`（Phase 12c——RN-048）。`auto`（自 RN-659
            起为新项目的默认值）：完整的 story 一出生就是 `ready`，没有人工步骤。`manual`：PO 提议，由你在 Backlog
            标签页中决定。更改默认值并没有改写任何已有项目。领域校验（DoD/DoR/RF/规则/模块）在两种模式下完全相同；模式只改变由谁触发。
        language:
          type: string
          example: pt-BR
          maxLength: 35
          description: >-
            项目的语言（RN-619）——共享制品和没有人类作者的回合使用它。服务器能识别的任何 BCP-47 代码，以规范形式存储；无法识别的返回
            400。省略时，项目以其创建者的有效回复语言开始（RN-618：账户选择 > 已确认的检测 > 界面语言）。
    UpdateStoryInternalDto:
      type: object
      properties:
        projectId:
          type: string
          format: uuid
          example: 01JC4Z0000PROJETO0000000001
        title:
          type: string
          example: Register a user
        description:
          type: string
          example: As a visitor, I want to sign up.
        questionId:
          type: string
          format: uuid
          description: RN-859（ADR 0214）：一个用户已经回答过的未决问题——只有这样，`ready` story（没有执行中的任务）才能被修正。
      required:
        - projectId
    UpdateStoryTitleDto:
      type: object
      properties:
        title:
          type: string
          example: Register a user
          description: 已去除首尾空白；1 到 200 个字符。
      required:
        - title
    UpdateUserPreferencesDto:
      type: object
      properties:
        locale:
          type: string
          enum:
            - pt-BR
            - en
            - ru
            - zh-Hans
          example: en
          description: 界面语言。限定在列表中——不是自由形式的 BCP-47。
        responseLanguage:
          type: string
          example: es
          maxLength: 35
          description: >-
            代理回答所用的语言（RN-618）：`automatico` 或服务器能识别其语言的任何 BCP-47 代码——开放列表，与
            `locale` 不同。以规范形式存储（`pt-br` 变为 `pt-BR`）。无法识别的代码返回 400 并指明规则。绝不改变
            `locale`。
    UpdateWorkspaceDto:
      type: object
      properties:
        name:
          type: string
          example: Acme Corp
          minLength: 2
        slug:
          type: string
          example: acme-corp
          pattern: ^[a-z0-9]+(-[a-z0-9]+)*$
          description: kebab-case。在系统中唯一，并在 URL 中可见。
    UpsertBudgetDto:
      type: object
      properties:
        limitUsd:
          type: number
          example: 50
          minimum: 0
          description: 上限，以美元计——契约中唯一以美元表述的地方，因为那是人会输入的单位。api 将其转换为微美元，响应以微美元返回。
        policy:
          type: string
          enum:
            - block
            - allow
          example: block
          description: '`block` 会在调用将超出上限时拒绝它；`allow` 会放行并只发出警告。'
      required:
        - limitUsd
        - policy
    UpsertCredentialDto:
      type: object
      properties:
        provider:
          type: string
          enum:
            - anthropic
            - openai
            - openrouter
            - nvidia-nim
            - together
            - deepinfra
            - bitdeer
            - vultr
            - 9router
            - opencode-zen
          example: anthropic
        apiKey:
          type: string
          example: sk-ant-api03-…
          maxLength: 512
          description: >-
            明文密钥。它在接触数据库之前通过信封加密进行加密，绝不会出现在任何响应中——即使是加密后的也不会。512
            个字符的上限是防止荒谬载荷的保护，而不是格式校验：密钥是否有效由提供商在 `POST
            /users/me/credentials/{provider}/test` 回答。
      required:
        - provider
        - apiKey
    UpsertPromptTemplateInternalDto:
      type: object
      properties:
        name:
          type: string
          example: dev-agent-kickoff
        version:
          type: string
          example: '3'
        body:
          type: string
          example: You are the dev agent for the {{modulo}} module...
        hash:
          type: string
          example: sha256:9f2c...
      required:
        - name
        - version
        - body
        - hash
    UrlDeExecucaoResponseDto:
      type: object
      properties:
        label:
          type: string
          example: Swagger
          maxLength: 60
        url:
          type: string
          example: http://localhost:8080/docs
        kind:
          type: string
          enum:
            - api_docs
            - web
          example: api_docs
      required:
        - label
        - url
        - kind
    UserCredentialResponseDto:
      type: object
      properties:
        id:
          type: string
          example: 01JC4Z0000CREDENCIAL00000001
        provider:
          type: object
          example: anthropic
          description: 涵盖 LLM 密钥和 git 令牌（`github`、`gitlab`）。
        createdAt:
          type: string
          example: '2026-07-10T10:00:00.000Z'
          format: date-time
        updatedAt:
          type: string
          example: '2026-07-10T10:00:00.000Z'
          format: date-time
      required:
        - id
        - provider
        - createdAt
        - updatedAt
    UserPreferencesResponseDto:
      type: object
      properties:
        locale:
          type: string
          enum:
            - pt-BR
            - en
            - ru
            - zh-Hans
          example: pt-BR
        responseLanguage:
          type: string
          example: automatico
          description: '`automatico`（每个账户的初始值）或在账户页面中明确选择的 BCP-47 代码。'
        detectedLanguage:
          type: string
          nullable: true
          example: null
          description: 从你自己的消息中检测到且经你确认的语言——绝不是未确认的检测结果。在存在确认之前为 `null`。
        detectedLanguageConfirmedAt:
          type: string
          format: date-time
          nullable: true
        effectiveResponseLanguage:
          description: 今天在任何会话之外适用的语言及其来源：账户选择 > 已确认的检测 > 界面语言。
          allOf:
            - $ref: '#/components/schemas/IdiomaEfetivoResponseDto'
      required:
        - locale
        - responseLanguage
        - detectedLanguage
        - detectedLanguageConfirmedAt
        - effectiveResponseLanguage
    VerifyEmailDto:
      type: object
      properties:
        token:
          type: string
          description: 通过邮件收到的一次性令牌。
      required:
        - token
    WorkspaceComPapelResponseDto:
      type: object
      properties:
        workspace:
          $ref: '#/components/schemas/WorkspaceResponseDto'
        role:
          type: string
          enum:
            - viewer
            - developer
            - maintainer
            - owner
          example: maintainer
          description: 调用方在此 workspace 中的角色。
      required:
        - workspace
        - role
    WorkspaceMemberComUsuarioResponseDto:
      type: object
      properties:
        userId:
          type: string
          example: 01JC4Z0000USUARIO0000000002
        role:
          type: string
          enum:
            - viewer
            - developer
            - maintainer
            - owner
          example: maintainer
          description: workspace 角色——除非项目记录覆盖它，workspace 的每个项目都会继承（RN-471）。
        name:
          type: string
          example: Senior Dev
          nullable: true
        email:
          type: string
          example: dev@brabo.dev
      required:
        - userId
        - role
        - name
        - email
    WorkspaceMemberResponseDto:
      type: object
      properties:
        workspaceId:
          type: string
          example: 01JC4Z0000WORKSPACE00000001
        userId:
          type: string
          example: 01JC4Z0000USUARIO0000000002
        role:
          type: string
          enum:
            - viewer
            - developer
            - maintainer
            - owner
          example: maintainer
          description: 线性层级：owner > maintainer > developer > viewer。每个角色都包含其后角色的权限。
        createdAt:
          type: string
          example: '2026-07-22T08:00:00.000Z'
          format: date-time
      required:
        - workspaceId
        - userId
        - role
        - createdAt
    WorkspaceResponseDto:
      type: object
      properties:
        id:
          type: string
          example: 01JC4Z0000WORKSPACE00000001
        name:
          type: string
          example: Acme Corp
        slug:
          type: string
          example: acme-corp
          description: 在系统中唯一；它会出现在 URL 中。
        createdBy:
          type: string
          example: 01JC4Z0000USUARIO0000000001
        toolRouterEnabled:
          type: boolean
          example: true
          description: >-
            是否为此 workspace 运行由 Jev 进行的工具路由步骤（ADR 0179）。默认开启；仅当回合模型来自 OpenRouter
            时才起作用，因此在没有 OpenRouter 的 workspace 上为 `true` 不会产生任何效果。
        createdAt:
          type: string
          example: '2026-07-20T09:12:00.000Z'
          format: date-time
        updatedAt:
          type: string
          example: '2026-07-20T09:12:00.000Z'
          format: date-time
      required:
        - id
        - name
        - slug
        - createdBy
        - toolRouterEnabled
        - createdAt
        - updatedAt
    WorkspaceSpendReportResponseDto:
      type: object
      properties:
        workspaceId:
          type: string
          example: 9b1c2d3e-4f50-4a61-8b72-0c3d4e5f6a7b
          format: uuid
        ownerId:
          type: string
          example: 9b1c2d3e-4f50-4a61-8b72-0c3d4e5f6a7b
          format: uuid
          description: 为这笔花费出资的密钥的所有者（RN-058）。
        dias:
          type: number
          example: 30
          description: 滑动窗口，以天计。
        projectId:
          type: object
          format: uuid
          nullable: true
          description: >-
            The `?projectId=` slice asked for (RN-952); `null` = whole
            workspace.
        totalMicros:
          type: number
          example: 1250000
        assinaturaMicros:
          type: number
          example: 0
          description: >-
            Part of the cost from models curated as `subscription` (RN-954): a
            seat, shown apart from the US$ paid (`cost - subscription`).
        inputTokens:
          type: number
          example: 120000
        outputTokens:
          type: number
          example: 35000
        chamadas:
          type: number
          example: 42
        porModelo:
          description: 按模型。两个提供商服务同一名称时会落入同一行——把它们分开会以另一个名义重新引入凭据维度。
          type: array
          items:
            $ref: '#/components/schemas/SpendLinhaResponseDto'
        porProvider:
          description: >-
            按提供商（ADR 0076，RN-186）。这个维度关乎凭据，因此只存在于这里，在要求 `owner`
            的路由上（RN-060）——成员的响应中没有它，而且这不是一个把它隐藏起来的过滤器：是仓储类型在参与者范围下拒绝它（RN-187）。
          type: array
          items:
            $ref: '#/components/schemas/SpendLinhaResponseDto'
        porProjeto:
          description: 按 workspace 内的项目。
          type: array
          items:
            $ref: '#/components/schemas/SpendLinhaResponseDto'
        porSessao:
          description: By SESSION (RN-952).
          type: array
          items:
            $ref: '#/components/schemas/SpendLinhaResponseDto'
        porAtor:
          description: 按参与者——代理和人在同一列表中，以 `actorKind` 区分。
          type: array
          items:
            $ref: '#/components/schemas/SpendLinhaResponseDto'
        porOwner:
          description: >-
            人的行（`actor_kind = "user"`）——handoff 称之为“按所有者”的块，因为依据
            RN-058，他们花费的都是所有者的密钥。所有者是谁见 `ownerId`。是 `porAtor`
            的一个分区，无需额外查询（RN-188）。
          type: array
          items:
            $ref: '#/components/schemas/SpendLinhaResponseDto'
        porAgente:
          description: 代理的行（`actor_kind = "agent"`）。`porAtor` 的另一半。
          type: array
          items:
            $ref: '#/components/schemas/SpendLinhaResponseDto'
        porDia:
          type: array
          items:
            $ref: '#/components/schemas/SpendPorDiaResponseDto'
      required:
        - workspaceId
        - ownerId
        - dias
        - projectId
        - totalMicros
        - assinaturaMicros
        - inputTokens
        - outputTokens
        - chamadas
        - porModelo
        - porProvider
        - porProjeto
        - porSessao
        - porAtor
        - porOwner
        - porAgente
        - porDia
    WorkspaceSummaryResponseDto:
      type: object
      properties:
        activeProjects:
          type: number
          example: 4
          description: workspace 中的项目数。领域中没有“活跃”标志——每个项目都计入。
        agentCount:
          type: number
          example: 6
          description: >-
            本月花费过 token 的不同代理（actorKind=agent），在 workspace
            所有项目中求和——包括区域子专业（Phase 8）。
        spentMicros:
          type: number
          example: 12500000
          description: 本月花费，以微美元计，按 token_usage.createdAt 求和。
      required:
        - activeProjects
        - agentCount
        - spentMicros
