CNB 流水线 Solver 阶段「交接文件缺失」怎么排查:scenario/.current-round.json 不存在、scenario/ 只有 .gitkeep、上轮构建仍 pending
日期:2026-09-15 08: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 交接、.current-round.json、孤儿分支、构建 pending、cnb build get-build-logs、cnb issues list-issues一句话结论:Solver 报「读取不到本轮信息」时,先别急着解题 —— 要先用「状态文件 + 远端分支 + 历史 Issue + 当天构建记录」四要素判定本轮 Creator 是否真的产出过题;四要素全空时要如实记录诊断,而不是硬造一题刷成交。
一、问题背景
本仓库是一个「每天 3 个计算机实战问题」的自动化训练闭环,.cnb.yml 里定义了一条定时流水线:
main:
"crontab: 0 0,8,16 * * *":
- name: scenario-daily-round
stages:
- name: generate-scenario # Stage 1:CodeBuddy-Creator 出题
type: npc:go
- name: solve-scenario # Stage 2:CodeBuddy-Solver 解题
type: npc:go
两个 stage 共享同一个工作目录,Creator 通过写入一个「不提交」的状态文件把本轮信息交接给 Solver:
{"branch":"2026-09-15-0800","issue":58,"scenario_dir":"scenario/2026-09-15-0800","topic":"..."}
# 路径 scenario/.current-round.json,已被 .gitignore 忽略
08:00 场次轮到 Solver(solve-scenario stage,构建 cnb-mmg-1k2h5q29j)启动时,出现了典型症状:
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-15-0800'→ 空(本轮分支不存在)
也就是说,Solver 面对的是一个「空手接活」的局面。先判断这是不是一次异常,再决定怎么做。
二、复现步骤(诊断命令,可原样照跑)
全部命令在 CNB 流水线容器内执行,
cnbCLI 已登录(cnb status)。
1. 看有没有交接文件、工作区有没有本轮环境
cd $CNB_BUILD_WORKSPACE
test -f scenario/.current-round.json && cat scenario/.current-round.json || echo "状态文件不存在"
ls -A scenario/ # 期望看到 scenario/2026-09-15-0800/,实际只有 .gitkeep
实测输出:
状态文件不存在
.gitkeep
2. 看是否在本轮分支上、远端有没有本轮分支
git fetch origin --prune
git branch --show-current
git ls-remote origin 'refs/heads/2026-09-15-0800'
实测输出:
main
# 第二条命令无任何输出 → 远端不存在 2026-09-15-0800 分支
3. 看历史 Issue,拿「历史最大题号」并确认本轮 Issue 是否发布
cnb issues list-issues --repo zhenxijs/3dayTest/3day-test \
--page-size 10 --order-by=-created_at --state closed --verbose \
| grep -E '"number"|"title"|"created_at"'
实测输出(截取):
"number": "72", "title": "【实战题·第57题】…(2026-09-15-0000)"
"number": "71", "title": "【实战题·第56题】…(2026-09-14-1600)"
"number": "70", "title": "【实战题·第55题】…(2026-09-14-0800)"
→ 历史最大题号 = 57(日期 2026-09-15-0000)。没有 2026-09-15-0800 的 Issue,即 08:00 场的题从未发布。
(仓库 open_issue_count = 0,说明也没有遗留的 open Issue 等着接。)
4. 关键一步:看「当天的构建记录」,判断上轮是否真的结束
cnb build get-build-logs --repo zhenxijs/3dayTest/3day-test \
--createTime 2026-09-15 --endTime 2026-09-16 --page-size 20
实测输出(关键字段):
- sn: cnb-mmg-1k2h5q29j
createTime: "2026-09-15T00:00:02.000Z" # = 2026-09-15 08:00:02 CST
status: pending # ★ 仍是 pending,没有结束
event: "crontab: 0 0,8,16 * * *"
duration: 93000
pipelineTotalCount: 1
pipelineSuccessCount: 0
pipelineFailCount: 0
对照上一场的成功构建:
cnb build get-build-logs --repo zhenxijs/3dayTest/3day-test --sn cnb-07g-1k2gabhlm
# createTime: "2026-09-14T16:00:14.000Z" = 2026-09-15 00:00:14 CST
# status: success
5. 交叉验证:本轮 stage 详情查不到
cnb build get-build-stage --repo zhenxijs/3dayTest/3day-test \
--sn cnb-mmg-1k2h5q29j --pipelineId cnb-mmg-1k2h5q29j-001 --stageId job-0
# → code 204,"没有查询到该stage信息。"
6. 顺带确认 Docker 可用(避免误判成「环境没起来」)
docker version # Client 29.6.2 / Server 29.6.2-7,正常
docker run --rm hello-world # 正常拉取运行
结论:Docker 与网络都没问题,问题不在环境,而在「没有本轮环境」。
三、根本原因
按四要素交叉验证,根因可定位为:本轮(2026-09-15-0800)的 Creator 阶段没有产出任何交接物。
证据链:
| 检查项 | 期望 | 实测 | 判定 |
|---|---|---|---|
scenario/.current-round.json | 存在且含 branch/issue | 不存在 | ❌ |
scenario/2026-09-15-0800/ | README + Dockerfile + compose + 代码 | 仅 .gitkeep | ❌ |
远端分支 2026-09-15-0800 | 存在 | 不存在 | ❌ |
| Issue「第58题(2026-09-15-0800)」 | 已发布 | 不存在(最大 #72 = 第57题/0000) | ❌ |
| 当天构建记录 | 上一场 success | cnb-mmg-1k2h5q29j 仍 pending | ⚠️ |
补充判据 —— 时间线自相矛盾,说明存在两个 00:00 场的实例:
构建 cnb-mmg-1k2h5q29j:createTime = 2026-09-15T00:00:02Z = 08:00:02 CST,status = pending ← 当前运行的就是这条
Issue #72(第57题 / 2026-09-15-0000):created_at = 2026-09-14T16:25:36Z = 00:25:36 CST,closed_at = 00:53:40 CST
远端分支 2026-09-15-0000:最后提交 2026-09-15 00:52:41 +0800
反观 2026-09-15-0000(第57题):Issue 创建在本地 00:25、关闭在本地 00:53,说明 00:00 场次是从 00:00 一路运行到 00:53 才结束的(这是一条真实存在的、运行了 53 分钟的 00:00 场次构建,但它在当天的构建列表里已经查不到——大概率被平台按条数/时间清理,或与当前这条 pending 记录互相覆盖)。
对照上一场次已结束的构建 cnb-07g-1k2gabhlm(起于 2026-09-14T16:00:14Z = 本地 00:00:14),可见正常情况下每一轮都应有自己一条独立的构建记录;而当前 08:00 的 run 里,「08:00 场次」和「另一个 00:00 场次实例」之间已无法在构建列表中清晰区分(2026-09-15 只返回 1 条记录且 pipelineTotalCount: 1)。
两个 00:00 场次实例「撞车」在同一个工作目录里,最可能的机制是:
前一场 00:00 的构建长时间未结束(90+ 分钟尚未 finalize), 新一场(本场)在同一个共享工作目录上启动, 且
git checkout main把工作区重置回了 main 的干净状态 ——「Creator 写下的未提交文件」是唯一活在这块目录里的交接载体, 一旦工作区被重置,本轮分支、scenario/2026-09-15-0800/、.current-round.json全部归零, 而 Creator 若在本场次内尚未执行到 push/发 Issue,平台侧也就查不到任何本轮痕迹。
一句话:Solver 读到「空手」=「本轮 Creator 未产出 + 共享工作区被重置」的组合结果。
⚠️ 这不是第 58 题的解答,也不是「某道题挂了」。它是流水线 stage 交接机制的可靠性缺陷。
四、解决办法 / 处理方式
本轮采取的处理(对工作负责的姿势)
- 不发明题号、不硬造一道题、不改动任何历史产物:
- 不创建
2026-09-15-0800分支(那属于 Creator 职责,且会导致「分支有、Issue 无」的错配); - 不动
2026-09-15-0000(第57题)的任何文件; - 不臆造「第58题」主题 —— 题目一旦虚构,写出的 SOLUTION/经验文档就是脱离真实环境的伪证据。
- 不创建
- 只沉淀「诊断过程本身」:把上述可复现命令链、实测输出、判定表写成文档(就是本篇),供后续排障直接抄。
- 把缺陷位置指出来,方便流水线配置侧加固(见下节)。
流水线侧加固建议(改 .cnb.yml,供后续维护)
建议 1:Solver 开头做「交接物四要素」硬校验,缺失即快速、可读地失败
让 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:给交接文件加「新鲜度」校验,防跨场次串读
.current-round.json 里除了 branch/issue/scenario_dir/topic,再加 created_at 与 run_id,Solver 校验「本轮 run_id 是否等于自己的工作目录轮次」,避免读到上一场遗留的文件。
建议 3:构建并发 / 超时治理,消除「两个 00:00 场次撞车」
- 给流水线加互斥锁,保证同一时刻只有一个「出题 → 解题」轮次占用工作目录:
lock:
key: scenario-daily-round
wait: false # 或 wait: true + cancel-in-wait: true 只保留最新
- 给
generate-scenario的timeout(当前40m)与整体构建时长做巡检;一个 00:00 场次跑到 00:53 属偏长,配合上面的 pending 现象,是撞车的温床。
建议 4:交接文件双保险
scenario/.current-round.json 是唯一交接物且不提交,脆弱。可同时把轮次信息写入 Issue 正文(Creator 发 Issue 时已含分支名)或 commit message,Solver 侧在状态文件缺失时可回退到「按当天日期查最近 Issue + 远端分支」兜底(本轮的诊断流程正是这个兜底思路)。
五、验证
判定本轮「无题可解」的四要素校验(全部命中)
# 1) 无交接文件
test -f scenario/.current-round.json || echo "NO_STATE_FILE"
# 2) 无本轮环境
ls -A scenario/ # → .gitkeep
# 3) 无本轮分支
git ls-remote origin 'refs/heads/2026-09-15-0800' # → 空
# 4) 无本轮 Issue;历史最大题号 57(#72, 2026-09-15-0000)
cnb issues list-issues --repo zhenxijs/3dayTest/3day-test --page-size 10 --order-by=-created_at --state closed \
| grep -E '"number"|"title"'
# 5) 当天仅有 1 条构建记录且 pending
cnb build get-build-logs --repo zhenxijs/3dayTest/3day-test --createTime 2026-09-15 --endTime 2026-09-16
环境可用性反向验证(排除「环境没起来」)
docker version # Client/Server 29.6.2-7 → 可用
docker run --rm hello-world # → "Hello from Docker!" 正常
结论:不是 Docker/网络问题,也不存在「已就绪但未被读取」的题;本轮确实无题可解,已按实情记录而非伪造产出。
六、经验与避坑
- Solver 报「读不到本轮信息」时,先做四要素判定,再决定动作:状态文件 → 远端分支 → 历史 Issue → 当天构建记录。四者全空 = 本轮无题,不要硬造。
scenario/.current-round.json是「不提交」的进程内交接物,生命周期只有「同一工作目录内的相邻 stage」。任何git checkout/git reset/ 工作区重刷都可能让它蒸发;它不是可靠持久化载体。- 别把「构建是新创建」当成「构建属于本场次」:
createTime是 UTC,本地是 UTC+8。2026-09-15T00:00:02Z其实是 08:00:02 CST。时区看错一步,整个判断就反了。 status: pending+stageId查不到(code 204) 组合出现时,说明该构建还在早期、stage 尚未登记,别拿它当「成功构建」的证据。- 时间线自相矛盾就是信号:当「上一场的产物时间」晚于「本场次的触发时间」时,基本可以断定存在多实例撞车 / 前一场未结束。
- 共享工作目录的 NPC 流水线一定要做并发互斥(
lock: { key: ... })。否则「未提交的进程内状态」会被并发实例互相覆盖,产生「偶发莫名其妙的空手接活」。 - 文档要写「实情」而不是「战果」:本轮没有可解题的环境,就记录诊断链路与加固建议。伪造一道题再写「修复前后 PASS 对比」,是最坏的做法——它会污染整个经验库的可信度。
- 交接物建议冗余:创建者除了写状态文件,还要把轮次信息落到 Issue 正文 / commit message,让解题者有兜底可查。
附:本轮事实快照
| 项 | 值 |
|---|---|
| 运行时间 | 2026-09-15 08:02 CST |
| 触发事件 | crontab: 0 0,8,16 * * *(08:00 场) |
| 当前 stage | solve-scenario |
| 构建号 | cnb-mmg-1k2h5q29j(2026-09-15T00:00:02Z,status=pending) |
| 构建地址 | https://cnb.cool/zhenxijs/3dayTest/3day-test/-/build/logs/cnb-mmg-1k2h5q29j |
| 当前分支 | main |
| 历史最大题号 | 57(Issue #72,分支 2026-09-15-0000,状态 closed/completed) |
| 本轮状态 | 无题可解(Creator 交接物缺失),仅产出诊断文档 |
