朕喜技术(ZhenxiJS)
2026.09.21
1 分钟阅读

CNB 流水线 Creator 交接物缺失导致 Solver 无题可解:stage 显示 success 却什么都没产出,怎么定位?

日期:2026-09-21 16:00(CST,UTC+8) 环境:CNB(cnb.cool)默认 NPC 镜像 cnbcool/default-npc:latest,services: docker(DinD) 技术栈关键词:CNB 流水线、cnb.yml、crontab: 0 0,8,16 * * *、npc:go、CodeBuddy-Creator / CodeBuddy-Solver、 stage 交接、scenario/.current-round.json、孤儿分支、cnb build get-build-status、cnb build get-build-stage、 cnb build build-runner-download-log、agent bootstrap 重启、stage success 但不产出 一句话结论:stage success ≠ 交接物存在 —— Creator 只跑了 61 秒、状态 success、error 为空, 却一个交接物都没产出(无分支 / 无环境 / 无 Issue / 无状态文件);Solver 报「读不到本轮信息」时, 用「状态文件 + 问题环境 + 远端分支 + 历史 Issue + 当天构建记录/ stage 日志」做五要素判定, 全空时要如实记录诊断,而不是硬造一题刷成交。


一、问题背景

本仓库是「每天 3 个计算机实战问题」的自动化训练闭环。.cnb.yml 里一条定时流水线每天 3 次 (00:00 / 08:00 / 16:00)触发,两个 npc:go stage 在同一个 Job 内、共享同一工作目录:

main:
  "crontab: 0 0,8,16 * * *":
    - name: scenario-daily-round
      stages:
        - name: generate-scenario   # Stage 1:CodeBuddy-Creator 出题(timeout: 40m)
          type: npc:go
        - name: solve-scenario      # Stage 2:CodeBuddy-Solver 解题(timeout: 60m)
          type: npc:go

Creator 要留下三样交接物:

  1. 孤儿分支 YYYY-MM-DD-HHMM(含 scenario/<分支>/ 复现环境)—— push 才留痕;
  2. Issue「【实战题·第N题】…(YYYY-MM-DD-HHMM)」;
  3. 状态文件 scenario/.current-round.json(不提交,只活在共享工作目录里)。
{"branch":"2026-09-21-1600","issue":73,"scenario_dir":"scenario/2026-09-21-1600","topic":"..."}
# 路径 scenario/.current-round.json,已在 .gitignore 中被忽略

2026-09-21 16:00 场轮到 Solver(solve-scenario)启动时,典型症状:

  • cat scenario/.current-round.json → No such file or directory
  • ls -A scenario/ → 只有 .gitkeep
  • git branch --show-current → main(不在任何日期分支上)
  • git ls-remote origin 'refs/heads/2026-09-21-1600' → 空

Solver 面对的是「空手接活」。先判断这是不是异常,再决定怎么做。


二、复现步骤(诊断命令,可原样照跑)

全部命令在 CNB 流水线容器内执行,cnb CLI 已登录(cnb status → ✅ 已登录。)。

1. 看有没有交接文件、工作区有没有本轮环境

test -f scenario/.current-round.json && cat scenario/.current-round.json || echo "NO_STATE_FILE"
ls -A scenario/
git branch --show-current

实测输出:

NO_STATE_FILE
.gitkeep
main

2. 远端有没有本轮分支

git fetch origin --prune
git ls-remote --heads origin | awk '{print $2}' | sed 's#refs/heads/##' | sort | tail -6

实测输出:

2026-09-19-1600
2026-09-20-0000
2026-09-20-0800
2026-09-21-0000
2026-09-21-0800      ← 当天最新只到 08:00 场
main

→ 没有 2026-09-21-1600。

3. 历史 Issue / 本轮 Issue、历史最大题号

cnb issues list-issues --repo zhenxijs/3dayTest/3day-test --page-size 100

实测输出:

total: 1
- number: "82"  state: open
  title: 【实战题·第66题】内网 DNS「UDP 能查、TCP 不通」…(2026-09-19-0000)
  created_at: "2026-09-18T16:17:13Z"

→ --state open 只有 1 条(#82 第66题,与本轮无关);没有 2026-09-21-1600 的 Issue。 (历史已关闭 Issue 的最大题号 = 72,分支 2026-09-21-0800,见 experience/README.md 索引末行。)

4. 关键:当天的构建记录

cnb build get-build-logs --repo zhenxijs/3dayTest/3day-test \
  --createTime 2026-09-21 --endTime 2026-09-21 --page-size 40

实测输出(total: 9;createTime 是 UTC,+8 才是本地):

sncreateTime (UTC)本地 (CST)eventtargetRefstatus
cnb-228-1k2vonj4m2026-09-20T16:00:05Z09-21 00:00:05crontabmainsuccess
cnb-bvo-1k2vp7b682026-09-20T16:08:42Z09-21 00:08:42push2026-09-21-0000success
cnb-nt8-1k2vpcn4f2026-09-20T16:11:38Z09-21 00:11:38push2026-09-21-0000success
cnb-05g-1k2vpd8tl2026-09-20T16:11:56Z09-21 00:11:56pushmainsuccess
cnb-1pg-1k30k6c212026-09-21T00:00:01Z09-21 08:00:01crontabmainsuccess
cnb-k7o-1k30kn2282026-09-21T00:09:08Z09-21 08:09:08push2026-09-21-0800success
cnb-gkg-1k30krvjo2026-09-21T00:11:49Z09-21 08:11:49push2026-09-21-0800success
cnb-r5o-1k30kseja2026-09-21T00:12:05Z09-21 00:12:05pushmainerror
cnb-qu8-1k31flaoe2026-09-21T08:00:04Z09-21 16:00:04crontabmainpending ← 本场

读法:

  1. 当天有 3 条 crontab 构建(00:00 场 / 08:00 场 / 16:00 场),本场是 cnb-qu8-1k31flaoe。
  2. 08:00 场留下了 push→2026-09-21-0800(第72题),这是正常一轮的样子。
  3. 全天 9 条构建中没有任何一条 push→2026-09-21-1600 → 平台侧证明 Creator 从未 push 过任何东西。

5. 决定性证据:stage 级「success 却没产出」

cnb build get-build-status --repo zhenxijs/3dayTest/3day-test --sn cnb-qu8-1k31flaoe

实测输出(关键部分):

pipelinesStatus:
  "cnb-qu8-1k31flaoe-001":
    name: scenario-daily-round
    stages[6]{status,duration,name,id}:
      success,25201,Prepare,prepare
      success,60731,generate-scenario,stage-0     ← ★ Creator 阶段
      start,0,solve-scenario,stage-1              ← ★ 本 stage
      pending,0,BeforeEnd,beforeEnd
      pending,0,DebugDetection,debugDetection
      pending,0,End,end
cnb build get-build-stage --repo zhenxijs/3dayTest/3day-test \
  --sn cnb-qu8-1k31flaoe --pipelineId cnb-qu8-1k31flaoe-001 --stageId stage-0

实测输出:

status: success
name: generate-scenario
startTime: 1789977628801   → 本地 2026-09-21 16:00:28
endTime:   1789977689533   → 本地 2026-09-21 16:01:29
error: ""

→ Creator 阶段只跑了 60.7 秒,状态 success,error 为空 —— 但它一个交接物都没产出。 这是本轮最值得记的一条:stage success 不等于交接物存在。

6. 决定性的第二步:Runner 日志里 Creator 到底干了什么

cnb build build-runner-download-log --repo zhenxijs/3dayTest/3day-test \
  --pipelineId cnb-qu8-1k31flaoe-001
# → data: /tmp/cnb-api/<ts>-<rand>.bin

清洗后过滤出 Creator 阶段(16:00:26→16:01:29)执行的命令:

sed 's/\x1b\[[0-9;]*m//g' /tmp/cnb-api/<file>.bin | tr -d '\000' > /tmp/log.txt
awk '/^\[00:00:2[2-9]/,/^\[00:01:29/' /tmp/log.txt | grep -E '^\[[0-9:]+\s+[+-]?[0-9.]+m?s\] \$ '

实测输出:

[00:00:26] $ echo ~ / pwd / cat AGENTS.md / cat CODEBUDDY.md / cnb skills list … / node -v   ← 启动 bootstrap
[00:00:28] $ pwd && ls -la && git log --oneline -5 && git remote -v
[00:00:30] $ cat experience/README.md | head -120
[00:00:30] $ git ls-remote --heads origin | head -80; ls template/ .devcontainer/ .cnb/ scenario/
[00:00:32] $ sed -n 120,200p experience/README.md
[00:00:32] $ cnb issues --help; cnb issues list --help
[00:00:34] $ cnb issues list-issues --help; cnb issues create-issue --help
[00:00:34] $ cat .cnb/settings.yml; cat template/README.md; cat template/docker-compose.yml
[00:00:36] $ cnb issues list-issues --page-size 100 --order-by=-created_at
[00:00:36] $ date +%Y-%m-%d-%H%M; date; cat .gitignore; cat .devcontainer/Dockerfile
[00:00:38] $ cnb issues list-issues … --state closed | grep -E 'number|title'   ← 查重:捞历史题号
[00:00:40] $ …(同一命令分页)
[00:00:43] $ …(同一命令分页)
[00:00:45] $ …(同一命令计数)
[00:00:47] $ …(同一命令分页)
[00:00:50] $ cat MIGRATION.md; cat README.md
[00:00:51] $ ls -a scenario/; ls experience/ | head -80; ls experience/ | wc -l
[00:00:53] $ find experience -name '*第66题*' -o -name '*dns*'; grep -n "第6[567]题" experience/README.md
[00:00:53] $ cnb issues get --number 82        ← 最后一条有实质产出的命令
─────────── ★ 静默 32.6 秒(00:00:54 → 00:01:26)───────────
[00:01:26] $ echo ~
[00:01:26] $ pwd
[00:01:27] $ cat AGENTS.md
[00:01:27] $ cat CODEBUDDY.md
[00:01:27] $ cnb skills list -g -a codebuddy --json
[00:01:27] $ cnb skills list -p -a codebuddy --json
[00:01:27] $ node -v
[00:01:29] $(stage 结束)

根本原因一目了然:

  1. 00:00:26–00:00:54(约 28 秒):Creator 只做了查重调研 —— 读 experience/README.md 索引、 git ls-remote 列分支、cnb issues list-issues 翻历史 Issue、cnb issues get --number 82 读第66题详情。 它停在了「调研完、还没开始动手」的边界上。
  2. 00:00:54 → 00:01:26:静默 32.6 秒(无任何 shell 命令,模型侧思考/上下文重建)。
  3. 00:01:26–00:01:29:命令序列变回 echo ~ → pwd → cat AGENTS.md → cat CODEBUDDY.md → cnb skills list ×2 → node -v —— 和 00:00:26 stage 启动时的 bootstrap 序列逐条一致。 说明 agent 丢掉了已有进度、从头重启了自己的启动流程。
  4. 重启后还没来得及重新读题,stage 到点结束(总时长 60.7 秒), 于是「分支 / 环境 / Issue / 状态文件」四样全空,而 stage 状态仍是 success。

7. 交叉验证:正常一轮长什么样

2026-09-21T00:00:01Z  crontab  →  cnb-1pg-1k30k6c21   (08:00:01 本地,success)
2026-09-21T00:09:08Z  push     →  2026-09-21-0800    "feat(第72题): 配置加载器静默跳过所有配置…"
2026-09-21T00:11:49Z  push     →  2026-09-21-0800    "fix(第72题): 修复配置加载器静默跳过所有配置"
2026-09-21T00:12:05Z  push     →  main               "docs(第72题): 沉淀…经验 + 更新索引"

→ 正常一轮 = crontab(触发)+ push→<日期分支> + push→main。 本轮 一条 push→2026-09-21-1600 都没有,与「Creator 未产出」完全吻合。

8. 确认 Docker 可用(排除「环境本身没起来」)

docker version --format '{{.Client.Version}} / {{.Server.Version}}'
# → 29.6.2 / 29.6.2-7   正常

三、根本原因

本轮(2026-09-21-1600)Creator 阶段在查重环节耗尽预算并中途重启,未产出任何交接物; 且它的失败对流水线完全不可见(stage 记 success、error: ""),Solver 因此空手接活。

五要素交叉验证:

检查项期望实测判定
scenario/.current-round.json存在且含 branch/issue不存在❌
scenario/2026-09-21-1600/README + Dockerfile + compose + 代码只有 .gitkeep❌
远端分支 2026-09-21-1600存在不存在(最新 2026-09-21-0800)❌
Issue「第73题(2026-09-21-1600)」已发布不存在(历史最大 72)❌
平台构建痕迹有 push→2026-09-21-1600无(当天 9 条全无)❌

机制推断(本轮首次拿到 stage 内部证据):

两个 npc:go stage 共享同一工作目录。Creator 的三样交接物里, scenario/ 与 Issue 必须先 push 才留痕,而 .current-round.json 被 .gitignore 忽略、 只活在共享工作目录里,且是进程内的。 本轮 Creator 在「查重调研」阶段就用掉了自己的预算(连续 5 次分页捞历史题号), 随后 agent 中途重启 bootstrap,stage 到点结束 —— 三样交接物一样都没有,且没有任何地方报错。

这是本仓库第 3 次同型事故:2026-09-15-0800(第58题位)、2026-09-20-0800(第70题位)、 2026-09-21-1600(本轮)。前两次只看到「结果为空」, 本轮第一次看到「Creator stage 内部到底发生了什么」:它不是没跑,而是跑了、只跑了查重、就重启并结束了,还被记成 success。

⚠️ 这不是某道题的解答,也不是「某道题挂了」,而是流水线 stage 交接机制的可靠性缺陷。


四、解决办法

1) 本轮处理方式(对工作负责的姿势)

  1. 不发明题号、不硬造题目、不改历史产物:
    • 不臆造「第73题」并编造一套假环境 —— 那样写出的 SOLUTION / 经验文档是脱离真实环境的伪证据, 会污染整个经验库的可信度;
    • 不动 2026-09-21-0800(第72题)的任何文件。
  2. 只沉淀「诊断过程本身」:可复现命令链 + 实测输出 + 判定表(就是本篇)。
  3. 给结论留落点:本轮 Issue 不存在,走「分支内文档 + commit message」通道。
  4. 把「stage success ≠ 交接物存在」写下来,供配置侧加固。

2. 流水线侧加固建议(改 .cnb.yml)

建议 1(最高优先级):Solver 开头做「交接物」硬校验,缺料即快速失败

# solve-scenario stage 开头(示意)
test -f scenario/.current-round.json \
  || { echo "FATAL: Creator 未留下 scenario/.current-round.json,本轮无题可解"; exit 1; }
BRANCH=$(python3 -c "import json;print(json.load(open('scenario/.current-round.json'))['branch'])")
git rev-parse --verify "origin/$BRANCH" >/dev/null \
  || { echo "FATAL: 远端不存在分支 $BRANCH"; exit 1; }
test -d "scenario/$BRANCH" \
  || { echo "FATAL: 缺少问题环境 scenario/$BRANCH/"; exit 1; }

建议 2(本轮新发现,针对 Creator 侧):给 Creator 加「产出断言」 —— Creator stage 结束前必须自检「分支已 push + Issue 已建 + 状态文件已写」, 任一条不满足就 exit 1,让「静默成功」变成「构建失败」。

建议 3(本轮新发现):把「查重」与「出题」拆成两个 task/stage —— 本轮 Creator 的预算几乎全耗在翻历史 Issue 找题号上(00:00:38–00:00:47 连续 5 次分页)。 把查重结果落成缓存(如 experience/dedup-index.json),出题阶段直接读缓存,可显著省预算。

建议 4:给交接文件加「新鲜度」字段,防跨场次串读 —— .current-round.json 增加 created_at / run_id,Solver 校验其是否等于本次运行。

建议 5:加流水线互斥锁,消除并发撞车

lock:
  key: scenario-daily-round
  wait: false

建议 6:交接物双保险 —— Creator 同时把轮次信息落到 Issue 正文 与 commit message; Solver 在状态文件缺失时按「当天日期查最近 Issue + 远端分支」兜底。


五、验证

# 1) 无交接文件
test -f scenario/.current-round.json || echo "NO_STATE_FILE"        # → NO_STATE_FILE
# 2) 无本轮环境
ls -A scenario/                                                      # → .gitkeep
# 3) 无本轮分支
git ls-remote --heads origin | grep 2026-09-21-1600                  # → 空
# 4) 无本轮 Issue
cnb issues list-issues --repo zhenxijs/3dayTest/3day-test --page-size 100
# → total: 1(仅 #82 第66题)
# 5) stage 级:Creator「成功」但无产出
cnb build get-build-status --repo zhenxijs/3dayTest/3day-test --sn cnb-qu8-1k31flaoe
# → generate-scenario: success, 60731ms  /  solve-scenario: start
# 6) 当天无 push→2026-09-21-1600
cnb build get-build-logs --repo zhenxijs/3dayTest/3day-test \
  --createTime 2026-09-21 --endTime 2026-09-21 --page-size 40
# → total: 9,targetRef 只出现 main / 2026-09-21-0000 / 2026-09-21-0800
# 7) 环境可用性反向验证
docker version --format '{{.Client.Version}} / {{.Server.Version}}'  # → 29.6.2 / 29.6.2-7

结论:不是 Docker/网络问题,也不存在「已就绪但未被读取」的题; 本轮确实无题可解,已按实情记录而非伪造产出。


六、经验与避坑

  1. stage success ≠ 交接物存在(本轮最重要的一条)。 Creator stage 显示 success, 60731ms, error: "",但它一个交接物都没产出。 判断「上一 stage 是否真的干活了」,要看产物的可验证痕迹(远端分支、Issue、状态文件), 不能只看 stage 状态。
  2. Solver 报「读不到本轮信息」时,先做五要素判定,再决定动作: 状态文件 → 问题环境目录 → 远端分支 → 历史 Issue → 当天构建记录 / stage 日志。 全空 = 本轮无题,不要硬造。
  3. cnb build get-build-status + cnb build get-build-stage 是排查 NPC 流水线的关键工具: 前者给出每个 stage 的 status/duration,后者给出精确的 startTime/endTime/error。 本轮正是靠它们把 Creator 定位到「61 秒就结束」。
  4. cnb build build-runner-download-log 能拿到含 NPC 全部 shell 命令的原始日志: --pipelineId <sn>-001(注意是 -001 这种带 pipeline 序号的 id)。 下载回来是带 ANSI 转义的二进制/文本混合体,用 sed 's/\x1b\[[0-9;]*m//g' | tr -d '\000' 清洗后再 grep。 这道工序是本轮找到根因的唯一途径。
  5. 「命令序列重复出现」是 agent 重启的指纹: echo ~ / pwd / cat AGENTS.md / cnb skills list / node -v 在 00:00:26 与 00:01:26 各出现一次 —— 同一条 bootstrap 序列出现两遍 = 上下文丢失、agent 从头开始。 排查 NPC 异常结束时应优先找这个信号。
  6. scenario/.current-round.json 是「不提交」的进程内交接物,生命周期只有「同一工作目录内的相邻 stage」。 任何 git checkout / git reset / 工作区重刷都可能让它蒸发;它不是可靠持久化载体。
  7. 别把「构建是新创建」当成「构建属于本场次」:createTime 是 UTC,本地 UTC+8。 2026-09-21T08:00:04Z 其实是 16:00:04 CST(本场);2026-09-21T00:00:01Z 是 08:00:01 CST。 时区看错一步,结论就反了。
  8. status: pending 不能当「已产出」的证据,也不能当「成功构建」引用。
  9. 别把 pipelineTotalCount 读成「当天构建条数」:它是单条构建内部的 pipeline 条数 (同一构建的多 job)。数当天条数看列表的 total。
  10. 「有没有 push→<日期分支> 构建」是判断 Creator 是否产出的强证据: 正常一轮必有「commit + push 分支」的动作。当天一条都没有 → Creator 根本没跑完。
  11. Issue 缺位时,结论要走「三级替代通道」,不能悬空: ① 分支上的 commit message → ② 分支内 SOLUTION.md / 经验文档 → ③ 分支评论 / 构建说明。 两条红线:不新建 Issue(题号序列属 Creator 职责,凭空造号会污染序列); 不把结论评论到上一场 Issue(会造成串场)。
  12. 文档要写「实情」而不是「战果」:本轮没有可解题的环境,就记录诊断链路与加固建议。 伪造一道题再写「修复前后 PASS 对比」,是最坏的做法——它会污染整个经验库的可信度。
  13. cnb CLI 的 --repo 必须是三段式(组织/仓库组/仓库), 写成两段会得到 status: 404 / errmsg: Resource not found.。
  14. Solver 侧不创建/不提交与本轮无关的残留目录:保持 git status 干净,便于后续排障。

附:本轮事实快照

项值
运行时间2026-09-21 16:01 CST
触发事件crontab: 0 0,8,16 * * *(16:00 场)
当前 stagesolve-scenario(CodeBuddy-Solver)
本场 crontab 构建cnb-qu8-1k31flaoe(2026-09-21T08:00:04Z = 本地 16:00:04;pending)
Creator stagegenerate-scenario:success,60731 ms(≈60.7s),error: ""(16:00:28→16:01:29)
Creator 实际动作仅查重调研(28s)→ 静默 32.6s → bootstrap 重启 → 结束;未建分支/环境/Issue
当天构建总数9(3 条 crontab:00:00 / 08:00 / 16:00 场)
当天 push→2026-09-21-16000 条
对照:08:00 场(第72题)cnb-1pg-1k30k6c21(crontab, success) + push→2026-09-21-0800 ×2 + push→main
本轮 Issue不存在(--state open → 仅 #82 第66题)
历史最大题号72(分支 2026-09-21-0800,已完成)
构建地址https://cnb.cool/zhenxijs/3dayTest/3day-test/-/build/logs/cnb-qu8-1k31flaoe
当前分支2026-09-21-1600(承载诊断文档的孤儿分支)
本轮状态无题可解(Creator 交接物缺失),仅产出诊断文档
同型历史2026-09-15-0800(第58题位)、2026-09-20-0800(第70题位),本轮为第 3 次

版权声明

本文为博主原创文章,遵循 CC BY-NC-SA 4.0 许可协议。 转载请注明出处:http://localhost/blogs/2026-09-21-1600-cnbcreator

分享这篇文章