Skip to content

feat(goal): 增加可恢复的 headless 单目标 CLI#549

Open
Ghost-LZW wants to merge 1 commit into
deepcoldy:masterfrom
Ghost-LZW:feat/headless-goal-host-api
Open

feat(goal): 增加可恢复的 headless 单目标 CLI#549
Ghost-LZW wants to merge 1 commit into
deepcoldy:masterfrom
Ghost-LZW:feat/headless-goal-host-api

Conversation

@Ghost-LZW

@Ghost-LZW Ghost-LZW commented Jul 22, 2026

Copy link
Copy Markdown

为什么值得进入 botmux

botmux 已有 v3 的真实 ephemeral worker、沙箱/enforcement、append-only journal、drive lease、worker fence 与 manifest validator,但程序化运行一个单独 goal 仍只能手写 DAG,再解析面向人的 CLI 输出。

本 PR 把这些现有能力收束成一个当天可用的本机产品入口:

botmux goal run "检查仓库并生成发布说明" \
  --bot my-codex \
  --working-dir ~/projects/app \
  --run-id release-notes-2026-07-22 \
  --timeout 900 \
  --json

它可直接服务本机脚本、CI、cron 和人工触发自动化,不依赖、也不感知任何外部项目或特定 controller。

本 PR 做什么

  • 新增 botmux goal run:位置参数、--goal-file--stdin 三种 goal 输入。
  • 走现有 v3 单节点 DAG + 真实 createEphemeralPool,复用既有沙箱/enforcement;没有第二套 planner、scheduler 或 mutable ledger。
  • --json stdout 只写一份 botmux.goal-run-result/v1 终态 JSON,日志走 stderr;失败也有结构化 error.code/message
  • 稳定退出码:0 succeeded、10 failed、11 blocked、12 cancelled、13 runId / active-driver conflict、14 usage / startup error。
  • 可选 --run-id 是幂等键:
    • 已终态:从 durable journal + validated manifest 原样重放,零 spawn,且不依赖当前 bots.json
    • live drive lock:按 PID + process-start/boot identity 判活,返回独立冲突码,零 spawn;
    • dead lock + 非终态 journal:复用 v3 recovery/worker fence re-drive,新建 attempt,不覆盖旧 attempt。
  • 将 canonical CLI 请求作为 goal.request.json 精确字节 artifact 固定进既有 run.json envelope;DAG 与 secret-free bot snapshot 继续固定解析后的 bot/cwd/model/sandbox。不同 goal、bot selector、cwd 或 timeout 不能复用同一 runId。
  • workingDir 在首次授权时展开 ~ 并冻结为绝对路径;从另一 cwd 重驱不会静默跑进另一个仓库。
  • SIGINT、SIGTERM、--timeout 都通过既有 cancelSignal → durable runCancelRequested → worker drain → runCancelled 路径,终态落盘后才向 stdout ack。
  • artifacts 只来自既有 manifest validator;runDirectory 明示 informative-only,不把私有目录布局变成 API。
  • usage/cost 采不到时不输出字段,不用 0 冒充完整数据。

明确不做什么

这不是 daemon / 远程 Host API,也没有 UDS/HTTP server、事件流、controller lease 字段、跨机器 fencing 或 discovery isolation profile。上述能力若需要,将分别走 RFC / 安全评审 / Linux E2E,不在本 PR 预埋不可运行的脚手架。

因此本 PR 也不再包含旧草案中的:

  • agent-sidecar 与第二套 durable ledger;
  • taskId/nodeId/leaseEpoch/leaseExpiresAt 等外部 controller 专用 wire 字段;
  • REAL_RUNS_DISABLED 测试门;
  • 未接线的 discovery profile / 临时 CI;
  • 任何 Motivation import、命名或叙事。

本机操作者运行 goal run 的信任模型与直接运行 botmux 相同,并且不会绕过现有 v3 沙箱/enforcement 路径。

可靠性合同

  • terminal-before-ack:journal 已 durable terminal 后才同步写最终 JSON。
  • create-or-attach(无 daemon):terminal replay / live conflict / dead-driver re-drive 三分明确。
  • crash recovery:Linux 集成测试真实 SIGKILL 首个 driver,再以同一 runId 恢复为 attempts/002
  • cancellation:SIGINT、SIGTERM、timeout 都验证最后一个 journal event 是 runCancelled
  • concurrency:同 runId 双并发调用断言恰好一次 runNode
  • request integrity:goal.request.json exact bytes 与 SHA-256 进入 immutable run.json,篡改 fail-closed。
  • machine output:subprocess wire 测试断言 stdout 恰好等于一个 JSON 文档。

验证

基于最新 origin/master@f4cc4f56

  • pnpm build:通过
  • pnpm exec tsc --noEmit:通过
  • feature + v3 回归:80 passed,Linux-only kill -9 1 skipped on macOS
    • test/v3-goal-cli.test.ts
    • test/v3-run-envelope.test.ts
    • test/cli-root-help.test.ts
    • test/v3-runtime.test.ts
    • test/v3-cancel-runtime.test.ts
  • 全量 pnpm test:622 files passed / 9811 tests passed / 23 skipped
  • git diff --check origin/master...HEAD:通过

Review

Bob 已按 B1–B6 逐项复核,并对最新 dc40e275 的 absolute workingDir 增量复审通过;当前结论 approved,可以转 Ready。

非阻塞 follow-up:

  1. goal run --help 目前走结构化 USAGE / exit 14,后续可补人类 help / exit 0。
  2. RUN_ACTIVE 当前通过 file-lock timeout 文案分类,后续宜给 withFileLock 增加 typed error。
  3. 单节点 v1 的 frozen snapshot resolver 有首项回退;若未来多节点化,应改为 selector 严格匹配。

后续(不阻塞本 PR)

若未来另提 daemon API RFC,应继承已经验证过的三条机制:过期 dead predecessor 的 cancel-fold 接管、per-task admission 串行化、以及受前任/自身双租约边界钳制的退避。跨机器 discovery isolation 另提 PR,并以 Linux 真实正/负 E2E 为合入门。

@Ghost-LZW
Ghost-LZW requested a review from deepcoldy as a code owner July 22, 2026 00:58
@Ghost-LZW
Ghost-LZW marked this pull request as draft July 22, 2026 01:28
@Ghost-LZW
Ghost-LZW force-pushed the feat/headless-goal-host-api branch from 4f75759 to 1f2763c Compare July 22, 2026 01:56
@Ghost-LZW Ghost-LZW changed the title feat(goal-runtime): 增加通用 headless goal Host API feat(goal): 增加可恢复的 headless 单目标 CLI Jul 22, 2026
@Ghost-LZW
Ghost-LZW force-pushed the feat/headless-goal-host-api branch from 1f2763c to dc40e27 Compare July 22, 2026 01:59
@Ghost-LZW
Ghost-LZW marked this pull request as ready for review July 22, 2026 02:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant