From 13000c553c25645361022f04741614567ff52368 Mon Sep 17 00:00:00 2001 From: yanghl Date: Mon, 20 Jul 2026 13:08:07 +0800 Subject: [PATCH] fix(coding): runBranchSetup 复用已存在分支时必须先合默认分支 --- workflows/coding.mjs | 37 ++++++++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/workflows/coding.mjs b/workflows/coding.mjs index b23ab9d..e5b4ed6 100644 --- a/workflows/coding.mjs +++ b/workflows/coding.mjs @@ -1517,6 +1517,34 @@ function createBranchFromPromptM(fromBranch, newBranch, c) { ].join('\n') } +// syncBranchWithDefaultPromptM:把默认分支合进当前功能分支(幂等)。 +// +// 为什么必须有这一步:runBranchSetup 对**新建**分支以默认分支为起点(createBranchFromPromptM), +// 但对**已存在**分支只做 checkout(主根)/ worktree add(lane 分支 2),二者都不带任何同步动作。 +// 于是「上一轮跑完留下的功能分支」会停在它当时的位置,其后提交到默认分支的一切——新 REQ 卡片、 +// 新 migration、docs 增量——对本轮**全部不可见**。子代理据残缺事实作业不会报错,只会自行发明 +// 需求(实测:spec 阶段报「全仓 grep 零命中」后照自己的理解另实现一套),到 milestone +// merge 才以内容冲突暴露,此时整轮返工。故此步须在功能分支上作业**之前**执行。 +// +// 冲突处置:不 abort、不 stash——保留冲突现场返回失败,由 runAction 仲裁后 halt 交人工。 +// 陈旧分支与默认分支真冲突意味着两边都动了同一处,只有人能判断取舍;自动 abort 会把这个 +// 信号抹掉,让下一轮再次撞上同一个坑。 +function syncBranchWithDefaultPromptM(branch, defaultBranch, c) { + return [ + `# 把默认分支 \`${defaultBranch}\` 合进功能分支 \`${branch}\`(幂等)`, + microStepContract(c.root), + '', + `前置:确认 \`git -C ${c.root} rev-parse --abbrev-ref HEAD\` 等于 \`${branch}\`;不等则直接返回失败(error 写明实际分支),**绝不**自行切分支。`, + `跑 \`git -C ${c.root} merge --no-edit ${defaultBranch}\`。`, + '按结果分三种:', + `1. 输出含 \`Already up to date\` → 分支本就包含默认分支全部提交,返回 \`{ "success": true, "detail": "up-to-date" }\`。`, + `2. 合并成功(fast-forward 或产生合并提交)→ 返回 \`{ "success": true, "detail": "merged: <被合入的提交数,取 git -C ${c.root} rev-list --count ${branch}@{1}..${branch}>" }\`。`, + `3. 冲突或其它失败 → **保留冲突现场**(绝不跑 \`merge --abort\` / \`reset\` / \`stash\`),返回 \`{ "success": false, "error": "merge conflict", "detail": "" }\`。`, + '## 输出(ACTION_RESULT_SCHEMA)', + '- 见上三种分支;`detail` 必填,供上层日志与人工诊断。', + ].join('\n') +} + // ── lane 物理隔离基础设施(Phase C:worktree 生命周期 + migration 版本段 + lane 测试库)── // 启用时机:Phase D 波次执行器在 ≥2 宽波次的前置串行段调用(读 maxV / 读 schema 基底 / 探测支持 / // 建 lane),本阶段只提供确定性原语;串行 / 主根路径(c.lane == null)不触达其中任何一个,行为不变。 @@ -2184,7 +2212,14 @@ async function runBranchSetup(module, c) { } if (head.branch !== branch) throw new Error(`HALT branchSetup-branch-mismatch ${branch}: ${ADJUDICATE_MAX} 轮后 HEAD 仍在 ${head.branch}`) - log(`branch-setup: ${id} → ${branch}`) + // 默认分支 → 功能分支同步(幂等)。新建分支走到这里恒为 up-to-date(起点就是默认分支); + // 真正吃这一步的是**复用已存在分支**的两条路径——主根 checkout 与 lane worktree add—— + // 它们此前不带任何同步,会让上一轮遗留的功能分支看不到其后进入默认分支的 REQ 卡片 / + // migration / docs 增量,子代理据残缺事实自行发明实现,直到 milestone merge 才以冲突暴露。 + // 冲突时保留现场 halt(见 prompt 说明),由人工判断取舍。 + const sync = await runAction(g => syncBranchWithDefaultPromptM(branch, def.branch, c) + g, + {site:`branchSetup-sync-default:${branch}`, grp:'Milestone', label: lbl('sync'), dec: c.decisions, root: c.root}) + log(`branch-setup: ${id} → ${branch}(同步默认分支 ${def.branch}:${sync.detail || 'ok'})`) } // ---- runMilestone:原 milestonePrompt 的 6 步散文流程 → JS 编排 ---- -- libgit2 0.22.2