# 03-C-控制与接口 接口分三层:项目层接口(第 13 节新增,对应控制台的项目创建/人工交互)、Org Graph 层接口(对两个后端一视同仁,架构文档 7.2 节)、Work Graph 层接口(MVP 阶段以 Claude Code 原生动态工作流为准,架构文档 10.1 节方案 A)。所有接口默认 `auth: system_internal`(MVP 单用户,无外部开放 API)。 ## 3.-1 `c_pick_folder`(架构文档 13.1 节决策 5) - `method + path`:`POST /api/system/pick-folder` - 语义:控制台前端是本地 Web 应用,无法直接调用 OS 原生文件夹选择对话框;此接口由本机常驻的后端进程代为拉起系统对话框(macOS `osascript`、Windows/Linux 对应平台方案),返回用户选中的真实绝对路径 - `input_contract`:`{}`(无需参数,对话框本身是模态的,由后端进程所在操作系统渲染) - `output_contract`:成功 `{ selected_path: string }`;用户取消对话框 `{ selected_path: null }`(不算错误) - `idempotent`:否(每次调用弹出新的对话框交互) - `errors`: - `DIALOG_UNAVAILABLE`(不可重试):后端所在环境不支持拉起原生对话框(如无图形界面的纯服务器环境),控制台应提示改用手动路径输入作为兜底 - `owner_team`:control-plane ## 3.0 `c_project_create` - `method + path`:`POST /api/projects` - `input_contract`:`{ target_folder_path: string(required), name: string(optional), init_git_if_missing: boolean(required) }` - `output_contract`:成功 `{ project_id, has_git_repo, graphlemon_subdir_initialized }` - `idempotent`:是——同一 `target_folder_path` 重复调用直接返回已有 `project` 记录(见 01-M 1.0 幂等约束) - `side_effects`:若 `has_git_repo=false` 且 `init_git_if_missing=true`,执行 `git init`;创建 `/.graphlemon/` 子目录 - `errors`: - `PATH_NOT_FOUND`(不可重试):文件夹不存在 - `PATH_NOT_WRITABLE`(不可重试):无写权限 - `GIT_INIT_DECLINED`(不可重试):非 Git 仓库且用户未确认初始化 - `owner_team`:control-plane ## 3.0.1 `c_project_get` / `c_project_list` - `GET /api/projects/:project_id`、`GET /api/projects` - `idempotent`:是 - 说明:MVP 控制台 UI 不强制暴露完整项目列表交互(架构文档 13.1 节决策 2),但接口按多项目设计,供未来扩展 ## 3.1 `c_task_create` - `method + path`:`POST /api/tasks` - `input_contract`:`{ project_id: string(required), goal_text: string(required), task_type: enum('goal'), created_via: enum('api','human_console', default='api') }` - `output_contract`:成功 `{ task_id, status: 'queued' }`;失败见 errors - `idempotent`:否(每次调用创建新任务) - `transaction`:单表插入 + 一条 `execution_event(event_type=task_status_changed)`,需同一事务 - `side_effects`:写入 `task` 表;若 `org_concurrency_config.max_concurrent_goals` 已达上限,任务落 `queued` 但不触发调度;写入 `execution_event` 供看板实时推送 - `errors`: - `INVALID_PARAM`(不可重试):`goal_text` 为空或 `project_id` 不存在 - `audit_trace`:`trace_id`、`actor=system|human`、`business_id=task_id` - `owner_team`:graph-engine ## 3.1.1 `c_bug_report_create` - `method + path`:`POST /api/bug-reports` - 语义:控制台人工反馈 bug 的入口,区别于 Validator 自动生成的 `c_bug_record_create`(3.10 节),复用同一张 `bug_record` 表但 `source=human` - `input_contract`:`{ project_id: string(required), description: string(required), repro_steps: string(optional), related_task_id: string(optional) }`(`project_id` 是 2026-07-31 订正新增——原文遗漏了它,但 `execution_event.project_id` 是 NOT NULL(01-M 1.9),`bug_record` 表自己没有 `project_id` 列,`related_task_id` 留空时("视为新发现的独立问题")没有任何字段能推导出 `project_id`,写 `execution_event` 会失败;U-023 实现时发现,按 `c_task_create` 同款处理方式把它变成必填) - `output_contract`:成功 `{ bug_id, status: 'open' }` - `idempotent`:否 - `side_effects`:写入 `bug_record(source=human, status=open)`;写入 `execution_event(event_type=bug_recorded)`;异步触发 Validator 认领该 bug 进行 triage(复用 04-P `p_validator_root_cause_trace`,见该节新增的人工接入分支) - `errors`: - `INVALID_PARAM`(不可重试):`description` 为空;`project_id`/`related_task_id` 不存在 ## 3.2 `c_task_list` / `c_task_get` - `GET /api/tasks?status=&org_node=&keyword=`、`GET /api/tasks/:task_id` - `output_contract`:`c_task_list` 返回分页数组;`c_task_get` 返回单条 `task` + 关联 `artifact` 列表 + 关联 `bug_record` 列表 - `idempotent`:是 - `errors`:`NOT_FOUND`(不可重试,仅 `c_task_get`) ## 3.3 `c_org_node_trigger` - `POST /api/org-nodes/:org_node/trigger` - `input_contract`:`{ task_id: string(required) }` - 语义:Org Graph 调度器将 `task.assigned_org_node` 指向的 Org Agent 唤起,Org Agent 内部先调用 `c_work_graph_plan` 再调用 `c_work_graph_execute` - **实现契约(架构文档 7.2 节决策,2026-07-29)**:底层是子进程调用 `claude --agent -p "..."`,让整个会话以目标项目 `.claude/agents/.md` 定义的 subagent 身份运行。三个 Org Agent 的身份(对应 SOUL.md)、memory、skills、tools、model 全部由这个 subagent 定义文件承载,`c_org_node_trigger` 本身只负责拼装任务上下文 prompt 和解析返回结果,不需要额外的角色隔离逻辑 - `idempotent`:否(每次触发可能生成新的 `work_graph_run`) - `errors`: - `TASK_NOT_READY`(可重试):上游依赖 Artifact 未就绪 - `CONCURRENCY_LIMIT_REACHED`(可重试):超过 `max_concurrent_goals` - `USAGE_LIMIT_REACHED`(新增,2026-07-29):语义同 3.6 节,Org Agent 级别的调用同样可能撞到 plan 额度,处理方式一致(等待重试,不转人工介入) - `owner_team`:graph-engine ## 3.4 `c_work_graph_plan` - `POST /api/work-graph/plan` - 语义:对应 Org Agent 的 Planning Skill 调用——输入任务上下文,输出结构化 `graph_manifest`(节点、边、fan-out/fan-in 分组、loop 规则),不执行,只规划 - `input_contract`:`{ task_id, org_node }` - `output_contract`:`{ run_id, graph_manifest }` - `idempotent`:否 - `errors`:`PLANNING_FAILED`(可重试,重试上限由调用方控制) - 约束(继承 10 节课程映射):`graph_manifest` 中每条边必须标注是否为"零 token 边"(纯代码聚合,如 flatten/dedupe)还是"需要 Agent 判断的边",规划阶段就要做这个区分,不能等执行时才发现 ## 3.5 `c_work_graph_execute` - `POST /api/work-graph/:run_id/execute` - 语义:直接调用 Claude Code 原生动态工作流能力执行 `graph_manifest` 对应的编排脚本(`agent()`/`parallel()`/`pipeline()`),编排本身不消耗模型 token(MVP 阶段唯一执行后端,见架构文档 7.2 节 2026-07-29 决策——不再考虑 Hermes CLI 后端,此前的后端不对等声明已作废) - `input_contract`:`{ run_id }` - `output_contract`:`{ status, result_artifact_refs }` - `idempotent`:否 - `errors`: - `NODE_CONTRACT_VIOLATION`(可重试,触发对应 `work_node_execution` 重试):某节点输出未通过 `output_schema_ref` 校验 - `BUDGET_EXCEEDED`(不可重试,需人工介入):执行中触发 `c_budget_check` 拦截 - `CLASSIFIER_BLOCKED`(不可重试,需人工介入):见下方权限模式说明——auto mode 分类器连续拦截 3 次或累计 20 次,`-p` 下无人可确认,进程中止 **Claude Code ACP 后端的实现契约(A-1 已通过 spike 实测确认,见 `spikes/a1-dynamic-workflows/`,2026-07-29)**: 调用是 **prompt-mediated** 的,不是对 Claude Code 的直接函数调用。`c_work_graph_execute` 的具体实现步骤: 1. 将 `graph_manifest` 对应的编排脚本写入/更新目标项目仓库的 `.claude/workflows/.js`(随目标项目自身 git 历史版本化,而非 graphlemon 平台仓库) 2. 以子进程方式调用: ``` claude -p 'Run / with args {...}' \ --output-format json \ --json-schema \ --permission-mode auto ``` **不加 `--bare`**:实测 `--bare` 会跳过凭据/登录态发现(报 `Not logged in`,与是否有 workflow 无关,是 `--bare` 本身的限制),必须用不带 `--bare` 的完整冷启动 **权限模式选定为 `auto`,而非 `bypassPermissions`/`--dangerously-skip-permissions`(决策,2026-07-29)**:官方文档明确要求 `bypassPermissions` 只应在隔离容器/VM+无网络访问环境下使用,且"对 prompt injection 和意外操作零防护";graphlemon 当前的节点隔离手段是 git worktree(同机目录级隔离,非容器/VM 级),达不到这个前提。`auto` mode 用一个分类器对每个动作实时把关,默认拦截一批高危操作(`curl|bash`、密钥外泄、生产部署、`force push`、批量删除、IAM 变更等),同样能在 `-p` 下零 prompt 跑完,且和架构已有的预算护栏/人工升级理念一致。**约束**:`auto` mode 要求会话模型是 Claude Sonnet 5 / Opus 4.7+ / Fable 5(Anthropic API 上);架构文档 10 节"按节点分层选模型"给便宜/有界重复节点选模型时,需要在这个集合内选,不能选更早/更小的模型 3. 解析返回 JSON 的 `structured_output` 字段作为 `output_contract` 的 `result_artifact_refs` 来源 4. 用返回 JSON 的 `total_cost_usd` / `usage` 字段回填 `budget_guardrail_config.current_usage`(对应 `c_budget_check`) 5. 子进程需以无 stdin 方式启动(`< /dev/null` 或等效),避免任何交互式提示导致挂起;实测确认无 stdin 时不会挂起、不会插入确认提示 6. 若返回结果表明本轮运行被分类器连续拦截 3 次或累计 20 次(`-p` 下无人可确认,官方文档称此时 auto mode 会中止会话),映射为 `CLASSIFIER_BLOCKED` 错误,对应 04-P 状态机路由到 `blocked_pending_human`,`blocked_reason='classifier_blocked'`,并把分类器拦截的动作摘要写入 `v_task_detail`,供人工判断是补 `permissions.allow`/白名单信任规则还是这确实是个危险操作 ## 3.6 `c_work_node_run` - 内部接口(不对外暴露 HTTP,供 `c_work_graph_execute` 调用),语义对应课程"给每个节点一个契约" - `input_contract`:`{ node_exec_id, input_payload, input_schema_ref }` —— 校验失败直接拒绝,不进入执行 - `output_contract`:`{ output_payload }` —— 必须通过 `output_schema_ref` 校验才算成功 - `idempotent`:否 - `errors`: - `SCHEMA_VALIDATION_FAILED`(可重试,重试即重新调用 Subagent,非幂等回放) - `USAGE_LIMIT_REACHED`(新增,2026-07-29,可重试但不是立即重试,见下方等待重试语义) - 约束:并行写代码的节点必须先申请 `git_worktree_ref`(见 01-M `work_node_execution.git_worktree_ref`),执行完成后清理 worktree 并合并回主分支 - **self-target 特例(新增,2026-07-30,见 05 节 §5.8)**:`task.project_id` 对应的 `project.target_folder_path == graphlemon 平台自身安装路径` 时,无论并行还是串行,一律强制申请 `git_worktree_ref`,不适用"仅并行时才强制"的一般规则;Coder/Validator 的自测/复核全部在 worktree 副本里跑,不碰主 checkout - **新增约束(架构文档 13.1 节决策 4)**:节点开始执行时必须先创建对应的 `execution_log_file` 记录(`status=active`),执行期间将 Claude Code ACP 的会话输出流式追加写入该文件,每次追加同时写一条 `execution_event(event_type=log_appended)`;节点执行结束(成功或失败)后将 `execution_log_file.status` 置为 `closed` **Claude Code plan 额度耗尽的等待重试语义(架构文档 7.5 节决策,2026-07-29)**: `claude -p` 的账号/plan 用量是一个会自然恢复的资源(5 小时滚动窗口 / 7 天窗口 / Opus 专属限额),耗尽时官方 CLI 直接终止请求并在文本里带上人类可读的恢复时间,例如 `You've hit your session limit · resets 3:45pm`、`You've hit your weekly limit · resets Mon 12:00am`。这类错误**不应该**路由到 `blocked_pending_human`(那是给需要人工判断的情况用的),而是自动等待+重试: 1. 检测:`claude -p` 返回 `is_error=true` 且 `result` 文本匹配 `/hit your (session|weekly|Opus) limit/i` 时,判定为 `USAGE_LIMIT_REACHED`,从匹配的关键词提取 `quota_limit_type`(`session`\|`weekly`\|`opus`),写入 `work_node_execution.quota_limit_type` 2. 状态转移:`work_node_execution.status → waiting_for_quota`,记录 `quota_wait_started_at`;不冒泡到 `task.status`,调度器继续跑队列里其他不依赖这个节点的工作(同 04-P `budget_exceeded` 的"跳过而非全局阻塞"模式) 3. 退避轮询:**不解析"resets 3:45pm"这种脆弱的人类可读文本**(没有日期/时区锚点,跨天跨周边界容易解析错),改用容量更简单可靠的退避重试——等待间隔从 5 分钟起步,每次翻倍,上限 30 分钟(5/10/20/30/30/... 分钟),每次到点直接重新发起原本要跑的那次 `claude -p` 调用本身作为探测,成功即转入正常执行 - 例外:`quota_limit_type=opus` 时不用等待退避——按官方建议直接把该节点这次调用降级到非 Opus 模型重试(需符合 03-C 3.5 节 auto mode 的模型约束:Sonnet 5 / Opus 4.7+ / Fable 5) 4. 超时兜底(防止真正的账号/计费问题被无限期静默等待掩盖):`quota_limit_type=session` 累计等待超过 8 小时、或 `quota_limit_type=weekly` 累计等待超过 9 天仍未恢复,转 `blocked_pending_human`,`blocked_reason='usage_limit_wait_timeout'` 5. 可见性:进入 `waiting_for_quota` 时写一条 `execution_event(event_type=quota_wait_started)`,`v_dashboard`/`v_graph_visualizer` 用信息性样式(非告警)展示,见 02-V 2.2/2.7 节——这是自愈型等待,不需要人工介入,但应该在控制台可见 ## 3.6.1 `c_execution_log_get` - `GET /api/work-nodes/:node_exec_id/log` - 语义:获取指定 Work Node 的原始执行日志(静态模式,用于已结束节点或首次加载) - `input_contract`:`{ path: { node_exec_id }, query: { offset: int(optional), limit: int(optional), download: boolean(optional) } }` —— 支持分页避免大文件一次性传输 - `output_contract`:`{ content: string, format, total_size_bytes, status }`;`download=true` 时返回文件流 - `idempotent`:是 - `errors`: - `LOG_NOT_FOUND`(不可重试):`node_exec_id` 不存在对应日志记录 - `LOG_PURGED`(不可重试):日志已被定时清理任务删除,`v_log_viewer` 据此展示对应异常态 ## 3.6.2 `c_ws_log_tail`(实时日志订阅) - `protocol`:WebSocket,`WS /api/ws/work-nodes/:node_exec_id/log` - 语义:订阅指定 Work Node 的实时日志追加事件,用于运行中节点的滚动查看 - `input_contract`:`{ path: { node_exec_id } }` - `output_contract`(推送消息体):`{ node_exec_id, appended_content: string, created_at }` - `idempotent`:不适用(长连接) - `errors`:`NODE_EXEC_NOT_FOUND`(不可重试):连接建立时校验 - 说明:节点执行结束后(`work_node_execution.status` 转为终态)服务端主动关闭该连接,前端应据此切换到 `c_execution_log_get` 的静态模式 ## 3.7 `c_artifact_write` - `POST /api/artifacts` - `input_contract`:`{ artifact_type, task_id, produced_by_run_id, file_path, content, depends_on_artifact_ids }` - `output_contract`:`{ artifact_id, git_commit_sha }` - `idempotent`:否(`artifact` 不可变更,见 01-M 迁移约束) - `side_effects`:写文件 + Git commit(commit message 包含 `task_id` 与 `artifact_type`) - `errors`:`GIT_COMMIT_FAILED`(可重试) ## 3.8 `c_skill_update` - `POST /api/skills/:skill_id/update` - 语义:Skill 自主迭代的落地接口(架构文档 8 节决策 1)——允许 Validator 触发的自动改写立即生效,但必须产生独立 Git 提交 - `input_contract`:`{ skill_id, triggered_by_bug_id(required), new_content, change_summary(required) }` - `output_contract`:`{ iteration_id, after_git_commit_sha }` - `idempotent`:否 - `transaction`:文件写入 + Git commit 需原子完成(commit 失败则整体回滚,不留半成品 Skill 文件) - `errors`:`MISSING_TRIGGER_REF`(不可重试):`triggered_by_bug_id` 缺失时直接拒绝——不允许无来源的 Skill 变更 ## 3.9 `c_skill_rollback` - `POST /api/skills/:skill_id/rollback` - `input_contract`:`{ iteration_id(required) }` —— 回滚到该次迭代之前的版本 - `output_contract`:`{ new_iteration_id }`(回滚本身也生成一条新的 `skill_iteration` 记录) - `idempotent`:否 - `errors`:`ITERATION_NOT_FOUND`(不可重试) ## 3.10 `c_bug_record_create` - `POST /api/bug-records` - `input_contract`:`{ task_id, description, repro_steps, root_cause_category(required, enum), root_cause_detail(required), related_artifact_ids, suggested_route(required) }` - `output_contract`:`{ bug_id }` - `idempotent`:否 - `errors`:`INVALID_ROOT_CAUSE_CATEGORY`(不可重试):未落在 `planning`\|`coder`\|`skill_defect`\|`core_engine`(新增,2026-07-30,见 01-M 1.6/05 节 §5.8)四类之一 - 约束:`root_cause_category=skill_defect` 时必须同时创建 `skill_iteration_suggestion`,否则视为字段缺失(见 08 反模式门禁:不允许"空目标"式的归因);`root_cause_category=core_engine` 时不要求人工评审即可提交(用户明确否决"比照 `org_graph_definition` 强制人工 PR 评审"的原提案),但 `c_org_loop_route` 会将其路由到蓝绿部署+监控流程而非常规 `fast_lane`(见 3.11 节) ## 3.11 `c_org_loop_route` - `POST /api/tasks/:task_id/route` - 语义:Org-level Loop 的路由决策落地——Validator 判定结果驱动任务回到 Planner(`full_replan`)或直达 Coder(`fast_lane`),决策本身由 Agent 判断(Validator 分类),路由本身是确定性代码(架构文档 10 节课程映射:判断用 Agent,路由用代码) - `input_contract`:`{ task_id, decided_route: enum('fast_lane','full_replan','manual_resume') }` - `output_contract`:`{ task_id, new_status, new_assigned_org_node }` - `idempotent`:否 - `errors`: - `LOOP_LIMIT_REACHED`(不可重试):`task.loop_round` 已达上限(3 轮),强制转 `blocked_pending_human`,不接受常规路由请求,只接受 `manual_resume` - **`core_engine` 路由分支(新增,2026-07-30,见 05 节 §5.8)**:关联 `bug_record.root_cause_category=core_engine` 的任务,无论 Validator 给出的 `decided_route` 是 `fast_lane` 还是 `full_replan`,`c_org_loop_route` 都不直接按常规路由处理,而是在 Coder 完成修复、Validator PASS、worktree 分支 merge 回 graphlemon 自身仓库主分支之后,转交 `p_deploy_supervisor_blue_green` 流程(04-P)——即触发 Deploy Supervisor 的蓝绿部署与自动回滚机制,不是"合并即生效" ## 3.12 `c_budget_check` - 内部接口,`c_work_graph_execute` 执行前/中调用 - `input_contract`:`{ scope, estimated_usage }` - `output_contract`:`{ allowed: boolean, remaining_quota }` - `idempotent`:是 - `errors`:`BUDGET_EXCEEDED`(不可重试):触发后对应任务转 `blocked_pending_human`,并记录 `blocked_reason='budget_exceeded'` ## 3.13 `c_concurrency_config_update` / `c_budget_config_update` - `PUT /api/settings/concurrency`、`PUT /api/settings/budget` - `input_contract`:对应 01-M `org_concurrency_config` / `budget_guardrail_config` 的可写字段 - `output_contract`:更新后的完整配置对象 - `idempotent`:是 - `errors`:`INVALID_PARAM`(不可重试):数值超出校验范围 ## 3.13.1 `c_log_retention_config_update` - `PUT /api/settings/log-retention` - `input_contract`:`{ retention_days: int(required, >0), max_total_size_mb: int(optional) }` - `output_contract`:更新后的完整配置对象 - `idempotent`:是 - `errors`:`INVALID_PARAM`(不可重试):`retention_days <= 0` ## 3.14 `c_ws_subscribe`(实时事件订阅,架构文档 13.1 节决策 3) - `protocol`:WebSocket,`WS /api/ws/projects/:project_id` - 语义:控制台建立连接后订阅指定 `project_id` 下的全部 `execution_event`;服务端在 `execution_event` 写入的同一事务提交后立即推送给已订阅的连接 - `input_contract`(连接建立时的订阅参数,query string):`{ project_id: string(required), task_id: string(optional) }` —— 传 `task_id` 时可选做服务端过滤,减少推送量 - `output_contract`(服务端推送的消息体):`{ event_id, event_type, task_id, run_id, node_exec_id, payload, created_at }`,字段与 `execution_event` 实体一一对应 - `idempotent`:不适用(长连接) - `errors`: - `PROJECT_NOT_FOUND`(不可重试):连接建立时校验 `project_id` 不存在,直接拒绝连接 - 断线重连约束:客户端重连后需带上 `last_event_id` 查询自该事件之后的遗漏事件(通过 `c_task_get` 系接口的时间线字段补齐,而非 WebSocket 层做消息重放缓冲,MVP 阶段简化处理) - `owner_team`:control-plane ## 3.15 Deploy Supervisor 控制操作(PH5 新增,2026-07-30,见 `docs/supervisor-auto-rollback-design.md`) 以下五个操作均由 Deploy Supervisor 进程调用,不是控制台前端直接调用的 HTTP API(Deploy Supervisor 是独立于 Graph Engine 之外的确定性 Python 进程,见 01-M 1.15 节),标记为内部接口。所有决策逻辑均为确定性代码,不接入任何 LLM 判断。触发前提:`task` 关联的 `project.target_folder_path == graphlemon 平台自身安装路径` 且 Validator 判定 PASS 后,worktree 分支已 merge 回主分支(见 3.11 节 `core_engine` 路由分支)。 ### `c_deploy_supervisor_stage` - 内部接口,语义:拉取合并后的主分支到全新目录(非原地 `git pull`),生成 `deployment` 记录 - `input_contract`:`{ git_commit_hash }` - `output_contract`:`{ deployment_id, status: 'staged' }` - `idempotent`:否 - `side_effects`:复制 `PROD_DB_PATH` 到一次性 `candidate_db_path`;用 `supervisor/isolation/isolated_workspace.py` 同款隔离原则拉起候选进程(`subprocess.Popen`,`shell=False`),监听临时 `candidate_port` - `errors`:`CHECKOUT_FAILED`(不可重试,转 `blocked_pending_human`):全新目录 checkout 失败(如磁盘空间不足) ### `c_deploy_supervisor_health_check` - 内部接口,语义:对候选实例执行 `process_alive`/`http_health`/`canary_task` 三项 pre-cutover 检查(见 `supervisor-auto-rollback-design.md` §6) - `input_contract`:`{ deployment_id }` - `output_contract`:`{ status: 'health_check_passed' | 'health_check_failed', signals: deploy_health_signal[] }` - `idempotent`:是(可重复查询当前检查状态,但不重新触发候选进程重启) - `errors`:`HEALTH_CHECK_TIMEOUT`(不可重试,直接判定该项 fail,进入回滚路径 A,不重试——重试属于"给坏版本第二次机会",不符合 fail-closed 原则) - 约束:任一检查项 fail 立即整体判定 `health_check_failed`,不等待其余检查项跑完 ### `c_deploy_supervisor_cutover` - 内部接口,语义:蓝绿切换本体——仅在 `c_deploy_supervisor_health_check` 返回 `health_check_passed` 后才允许调用 - `input_contract`:`{ deployment_id }` - `output_contract`:`{ status: 'live', cutover_at }` - `idempotent`:否 - `side_effects`:Live 停止接受新的 `c_org_node_trigger` 调用(在途 `work_node_execution` 允许走到可安全中断的检查点,依赖既有持久化恢复机制,不强求优雅排空)→ Live 进程退出 → 候选进程改连 `PROD_DB_PATH`、改绑 `LIVE_PORT` → 候选成为新 Live;原 Live checkout 目录保留为 `previous_instance_dir`,不删除 - `errors`:`CUTOVER_FAILED`(不可重试,转 `blocked_pending_human`):切换过程中候选进程未能正确绑定生产端口/生产库 ### `c_deploy_supervisor_monitor` - 内部接口,语义:post-cutover 监控窗口内周期性采样四类信号(见 `supervisor-auto-rollback-design.md` §6),窗口时长/任务数上限见 05 节 §5.9 - `input_contract`:`{ deployment_id }`(由 Deploy Supervisor 自身的调度循环周期性调用,不是一次性调用) - `output_contract`:`{ status: 'monitoring' | 'anomaly_detected' | 'monitoring_window_clean', latest_signal: deploy_health_signal }` - `idempotent`:是 - 约束:`validator_approval_rate` 信号从 `execution_event`/`work_node_execution` 按滚动窗口统计 Validator 节点 `approved` 占比,与该 `deployment` 之前的历史基线做纯算术比较,偏离 `VALIDATOR_RATE_DEVIATION_THRESHOLD`(05 节 §5.9)即判定 `anomaly_detected`——这是"改坏了的 validator"场景的核心检测点,判断全程不需要另一个 LLM 参与 ### `c_deploy_supervisor_rollback` - 内部接口,语义:回滚路径 A(pre-cutover,健康检查未过)与路径 B(post-cutover,监控期 `anomaly_detected`)的统一入口,具体步骤见 `supervisor-auto-rollback-design.md` §7 - `input_contract`:`{ deployment_id, rollback_reason }` - `output_contract`:`{ status: 'rolled_back' | 'health_check_failed' | 'rollback_failed' }` - `idempotent`:否 - `side_effects`:路径 A——终止候选进程、删除一次性 DB 副本、`git revert` 本次合并 commit、创建 `bug_record(root_cause_category=core_engine)`;路径 B——用 `previous_instance_dir` 重新拉起旧版本、绑定 `LIVE_PORT`+`PROD_DB_PATH`、对复活的旧版本重跑一遍 pre-cutover 健康检查确认无误、`git revert`、创建 `bug_record` - `errors`: - `ROLLBACK_FAILED`(不可重试,转 `blocked_pending_human`,`blocked_reason='rollback_failed'`):连 `previous_instance_dir` 也起不来,或 `git revert` 冲突(见 `supervisor-auto-rollback-design.md` §10 开放问题 3)——这是本机制唯一保留的人工介入点,触发条件严格限定为"自动恢复机制本身失效",不是"代码判断谁对谁错" - `owner_team`:deploy-supervisor(独立于 `graph-engine`,进程边界见 01-M 1.15 节)