2026-06-12-parallel-orchestration-addendum.md
48.8 KB
Coding 并行化实施附录(设计综合裁决)
本附录优先级高于计划正文《2026-06-12-parallel-orchestration.md》,冲突以附录为准。
来源:预置修正点 D1–D5 + 三个镜头评审的全部 findings,逐条裁决后综合(裁决标准:正确性 > 简单性 > 性能;坚持 fail-open to serial——任何调度/隔离环节失败一律降级串行,绝不因并行机制新增 halt 点)。关键事实均已对照 workflows/coding.mjs 与 skills/plan/skeleton-gen/templates/ 实测核验(含
node --check基线恒红的复现)。裁决总览:接受 28 条 findings(含跨镜头重复条目合并)+ D1–D5 全部确认(其中 D2/D3/D4/D5 带修正);拒绝 1 条(lane 测试库 moduleId 命名,见 § 十)。
一、总开关与入口(修正 D4)
补 1:coding-start 只开模块级并行,features 留闸
-
改什么:
ARGS.parallel缺省 → 全串行(D4 前半,确认不变)。coding-start 步骤 4 只传parallel: { modules: true, maxWidth: 3 },不传features/featureMaxWidth(即维持计划行 12 的features: false缺省)。features: true的开启动作写成 Task 14 的验收 checkbox——评估通过后才改 coding-start 的 SKILL.md(一行改动)。 -
为什么:D4 原文让入口从第一天就传
features: true,Task 12-13 一旦合入该 flag 立即生效,绕过计划自设的两道闸(Phase F 题注「A–E 稳定 ≥1 轮」、Task 14「收益 < 20% 冻结」);届时排查问题的人不会想到入口早已埋了开关。 - 落点:Task 10(coding-start args 与横幅)、Task 14(新增"开启 features 开关"验收 checkbox)。
二、Phase F 范围(确认 D1)
补 2:feature 级并行仅限后端 REQ,移除 ERP_PORT_OFFSET
- 改什么:D1 照案通过——Phase F 收窄为后端 REQ;Task 13 中 ERP_PORT_OFFSET 相关内容(含模板支持)整体移除;FE 的 feature 级并行收益由 Phase E spec 预生成承担。
- 为什么:评审核验成立——后端 REQ 链的 tdd/verify 不起栈、不占固定端口(Spring 测试随机端口);FE feature 的 e2e 要打穿 vite/playwright/后端三层端口配置,复杂度/收益比不成立。
- 落点:Phase F 题注、Task 13。
三、波次前置串行段(修正 D2:补 lane 占用感知)
补 3:主根复位 + prune 不够,必须加 lane 占用感知
-
改什么:D2 的「主根 HEAD 回默认分支 + 树净 +
git worktree prune」确认保留,但补三点修正:- (a) lane 占用感知微步骤(prune 之外另加):
git -C <ROOT> worktree list --porcelain枚举<ROOT>-lanes/下持有module-*分支的残留 lane。本次要跑的模块若其分支被某残留 lane 占用:lane 树脏 → 先对该 lane 跑 recoverDirtyWorktreePromptM(branch=module-<id>)(in-scope 残留 commit 进模块分支,工作不丢);恢复成功或本就净 →git worktree remove该 lane;恢复失败(越界残留)→ 该模块按现行branchSetup-dirty-worktree同类语义结构化 halt(等价前移,非新增 halt 类——串行路径走到 runBranchSetup 也会在同一种脏树上 halt),波次其余模块照常调度。 - (b) 占用感知前置跑在每个波次(含宽 1)开跑前——宽 1 回退路径正是被残留 lane 卡死的高发场景:halt → lane 保留取证 → resume 后剩余模块独占波次(宽 1)→ 主根 legacy 路径
git checkout module-<id>被残留 lane 拒绝(git 实测 fatal: already used by worktree)→ 仲裁三轮全败 → 硬 halt。即"降级回串行"这条兜底路径本身会被残留 lane 毒化。 - (c) 主根脏树恢复的目标分支传当前 HEAD 分支而非默认分支:HEAD 停在
module-<id>且脏 → recoverDirtyWorktree(branch=module-<id>) 就地 commit-in-scope 后再 checkout 默认分支(传默认分支会被 coding.mjs:1144 的分支护栏按 dirty-on-wrong-branch 拒绝,造成本可避免的降级/halt);HEAD 已在默认分支且脏 → 不在前置段处理(绝不为并行机制新增"对默认分支自动 commit"),本波次降级 maxWidth=1 交给 legacy 路径现有恢复/halt 语义。 -
git worktree prune仅保留用于清理"目录已删"的管理残留——它对目录完好的 lane(恰是 Task 6 Step 4 刻意保留的取证态)毫无作用,不要依赖它。
- (a) lane 占用感知微步骤(prune 之外另加):
-
为什么:git 实测双向阻塞——分支被主根 checkout 时
worktree add报 fatal(D2 动机,成立);反向残留 lane 持分支时主根 checkout 同样 fatal(D2 未覆盖)。 - 落点:Task 9 Step 3(前置串行段微步骤,扩展为含宽 1 波次)、Task 6 Step 4(halt 保留 lane 的注释补一句"resume 时由波次前置占用感知清理")。
四、migration 版本段(Task 7)
补 4:maxV 改全局并集,laneIdx 保留
-
改什么:maxMigrationVersionPromptM 的输入从"主根
ls sql/migrations/V*.sql"改为全局并集:主根文件 ∪git for-each-ref refs/heads/module-*的每个分支跑git ls-tree -r --name-only <ref> -- sql/migrations,取全体最大版本号(纯 git 只读、确定性、零时间/随机源)。vBase(maxV, laneIdx)的 laneIdx 保留不改,并在注释写明:并集口径下"halt-resume 后 laneIdx 漂移"无害——每个新波次的段基址恒高于一切已知版本(含未合并分支已占用的段),同段绝不二次发放,只产生无害的段空洞。 -
为什么:halt 波次收敛 + 保留 lane 意味着 halted 模块的 V 文件已 commit 在未合并的
module-<id>分支上,对主根 ls 不可见;下一波或下次 resume 会把同段重复发放给别的模块,两分支各写 V(文件名不同,git 合并静默通过),Flyway 在"同版本号多文件"上直接报错——正是计划行 23 点名的事故,且发生在两次运行之间,波次内防线救不了。评审另案的"docs02Idx 替代 laneIdx""段账本文件"两个子方案不采纳:并集口径已消除全部撞号路径,再加稳定键/账本是多余复杂度(正确性等同,简单性更差)。 - 落点:Task 7 Step 1(prompt 改并集)、Step 2(注释写明漂移无害的依据)。
补 5:Flyway out-of-order 约定
-
改什么:docs-04 骨架模板锁定段加一条后端约定「Flyway 配置
out-of-order: true」;Task 7 Step 3 注释与 README 阶段 B 同步写明:并行版本段下版本号只保唯一、不保连续、不保合并序;独立模块 migration 互不引用正是同波次准入条件,乱序 apply 安全。 - 为什么:计划行 202「段间空洞无影响」只对每次重建的库成立。同波次两模块按完成序 merge,高版本段可能先落地默认分支;任何 inter-merge 窗口被 migrate 过、之后不重建的持久库(docs-04 § 3.5 鼓励的人工验收/演示库正落在该窗口),下一次 migrate 会因低版本号 pending 在 validate 上硬失败,报错形似模块 bug。
- 落点:Task 7 Step 3、Task 8(docs-04 模板同批改动)、Task 10(README)。
五、测试 DB lane 化(修正 D5:三件套 + fail-closed 兜底,Task 8)
补 6:env 必须打穿到 Spring 进程,探测两点联查
-
改什么(D5 修正后确认,三件套):
- (a) 新骨架约定:docs-04 skeleton 模板锁定段 + tddPrompt 写明——后端
application*.yml(含测试 profile)JDBC URL 的 schema 段必须写成 Spring 占位${ERP_TEST_DB_SCHEMA:<db.schema 字面量>}(env 缺省回退原 schema,串行行为一字不变;gradle test fork 的 JVM 与 bootRun 默认继承 env,env 即天然贯通,无需 SPRING_DATASOURCE_URL 拼装)。 - (b) 探测两点联查(替换 Task 8 Step 3 的单 grep):grep 目标项目
scripts/含ERP_TEST_DB_SCHEMA且 grepbackend/src/**/application*.{yml,properties}含${ERP_TEST_DB_SCHEMA占位;任一不满足 → 本波次降级 maxWidth=1(log 写明原因 + 建议重跑 skeleton 升级,不 halt)。 - (c) lane 注入维持"成品字符串"纪律(Task 8 Step 2 原案):
ERP_TEST_DB_SCHEMA=<c.dbSchema>前缀由波次执行器读 config-vars.yaml 拼好渲染进 contract,绝不让子代理自己拼;明示含派发给嵌套子会话的命令。
- (a) 新骨架约定:docs-04 skeleton 模板锁定段 + tddPrompt 写明——后端
- 为什么:实测模板现状——schema 引用只在 setup-test-db / seed-demo-data 两个 Node 脚本;gradle 测试与 bootRun 的 datasource 由 coding 期生成的 application*.yml 写死(全仓无任何 application 模板或 spring.datasource 占位约定),env 传进去无人消费。按计划只改三个模板,Task 8 Step 3 的探测必然假阳性放行:setup-test-db 建了 lane 库、两条 lane 的 gradle 测试仍同连共享库互相清库——fail-open 的降级判据必须可信,否则降级形同虚设。评审中"test.mjs run() 注入 SPRING_DATASOURCE_URL + 探测 grep 注入代码特征"的子方案不采纳:tdd 内循环由嵌套子会话直跑 gradle、不经 test.mjs,该注入覆盖不到主要路径;占位约定让 env 在所有 fork 路径天然贯通,更简单且覆盖完整。存量项目(无占位)按 (b) 老老实实降级串行,不另设兜底注入。
- 落点:Task 8 Step 1/2/3、tddPrompt(与 Task 7 Step 3 同文件顺带)、docs-04 模板。
补 7:lane schema 的 fail-closed 兜底 marker
-
改什么:波次执行器建好 lane worktree 后,在 lane 根写确定性标记文件
.tmp/erp-lane-db(一行 lane schema 名;.tmp/已在 gitignore 模板忽略,绝不会被 recoverDirtyWorktree 判 in-scope 自动 commit)。三个脚本模板取值顺序改为:process.env.ERP_TEST_DB_SCHEMA || <SCRIPT_DIR/../.tmp/erp-lane-db 文件内容> || db.schema——脚本按自身所在仓库根定位 marker,lane 副本天然 lane 作用域;主根无此文件,串行行为一字不变。lib 模板测试补 marker 优先级 case。明令禁止用"改 lane 副本 config-vars.yaml 的 database.schema"兜底(该文件随 git 提交,lane 树一脏会被自动 commit 并随 milestone 合回默认分支,把真 schema 名搞坏)。 - 为什么:lane 注入是纯 prompt-only,tdd 主会话还会把命令转述给二级子会话(提示词链两跳),任何一跳丢前缀,setup-test-db 就对共享 schema DROP+CREATE——症状是兄弟 lane 随机红测,烧 testGate attempt 与仲裁预算甚至假 halt。最具毁灭性的操作(DROP)需要确定性兜底,不能只靠提示词。
- 落点:Task 8 Step 1(三模板 + lib 测试)、Task 9 Step 3(建 lane 后写 marker 的微步骤)。
补 8:lane 库命名统一序号制(修正 D3)
-
改什么:模块级 lane 库维持
<schema>_lane<N>(Task 8 Step 2 原案不变);Phase F 的 feature 级测试库由 D3 的<schema>_<moduleId>_f<i>改为<schema>_lane<N>_f<i>(serial 主根跑 feature 并行时 N 取 0)。D3 的"每条 feature 链独立测试库"结论本身确认。 -
为什么:moduleId 进 schema 名引入两个不必要问题——64 字符上限需要专门截断规则;截断后长前缀相同的 moduleId 撞名会重新引入互相清库,且只在特定 id 组合下出现、极难排查。另 module id 允许
-(/[A-Za-z0-9_-]+$/),落进 MySQL 标识符/JDBC URL 还要引号/转义处理。序号制后缀固定 ≤ 10 字符、唯一性由「波次内 lane 序号唯一 × 模块内批序号唯一」确定性保证,与模块级口径统一。 - 落点:D3 修正、Task 12-13(Phase F)。
六、起栈类 stage 全局互斥(新增;Task 8/9)
补 9:stackLock——Seed 与 testGate 整段互斥
-
改什么:新增与 withMainRootLock 同构的全局 stackLock(promise 链实现,确定性、无时间/随机源)。runModule 内把 Seed stage 整段与 testGate 整段(test.mjs 第 5 步 e2e 的 Playwright globalSetup 同样按固定端口冷起后端,e2e 段不可单拆则整个 testGate 包锁)各自
await withStackLock(...)包进锁内——同一时刻全仓只有一条链在起栈/占端口。seedGenPrompt 与 gatePrompt 的 lane 注入同步写明:「端口探测/残留 pid 回收只允许在持有起栈互斥的本 stage 内执行」。Task 9 Step 3 注释写明取舍:「冷起栈资源(固定端口 + 进程树)全局唯一,相关 stage 串行化而非 lane 化」。 - 为什么:Seed 冷起 gradle bootRun 用 config-vars.yaml 固定端口,且 seedGenPrompt:645 明确指示"起栈前按既知端口回收上一次残留 pid"——≥2 宽波次两条链并发进 Seed 时,后到者会把兄弟 lane 正在验证的后端当残留 kill 掉(主动互杀,每个 ≥2 宽波次必然踩中的确定性故障,不是小概率 race)。不打穿端口配置(与 D1 的复杂度论证一致),改互斥:Seed/testGate 占模块总时长比例小,串行化损失远低于打穿三层端口配置;并行收益主体(tdd/review/fix,不起栈)完整保留。tdd 内循环(Spring 测试随机端口、不起栈)不进锁。
- 落点:Task 9 Step 1(锁定义并列处)、Task 9 Step 3(注释)、Task 8 Step 2(seedGenPrompt/gatePrompt 注入文案)。
补 10:seedStageContract 同步 lane 注入(Seed 全链用 lane schema)
-
改什么:Task 8 Step 2 的 lane 注入范围从"仅 featureStageContract"改为「所有会触达 DB 的 contract」:featureStageContract 与 seedStageContract 追加同一条成品硬约束(
c.dbSchema渲染)——Seed 全链(setup-test-db / gradle bootRun / seed-demo-data / mysql COUNT 对账)在 lane 模式一律带ERP_TEST_DB_SCHEMA=<lane schema>前缀(bootRun 经补 6 的占位约定吃到 lane schema;COUNT 对账连 lane schema 查询)。Task 8 验证清单加一条 grep 审计:凡 prompt 文本含setup-test-db.mjs/seed-demo-data.mjs字样的 builder,必须含 lane 注入分支、或确属主根专属(行为门只在 frontend-phase 终波宽 1 主根跑,天然豁免)。 - 为什么:Seed 是计划自己定义的"第三类 stage"(coding.mjs:580-590 注释明示不套 featureStageContract),按 Task 8 Step 2 字面注入覆盖不到它,而它恰是 DROP+CREATE/起栈/灌种子的大户——并行 lane 中按计划字面会直接 DROP 共享库。统一 lane schema 也与补 7 的 marker 兜底自洽(marker 把 lane 内一切脚本确定性指向 lane 库;若 Seed 按"锁内默认库"跑会与 marker 互相打架)——评审中"锁内段保持默认 schema、不注入 lane env"的替代子方案因此不采纳。
- 落点:Task 8 Step 2(含验证清单新增 grep 审计)。
七、执行器与穿线细节(Task 1/2/9)
补 11:microStepContract 显式 root 入参,仲裁在 lane 上取证
-
改什么:microStepContract 改为
microStepContract(root)显式入参;主根专属 prompts(detectDefaultBranchPromptM、milestone 微步骤等)传 ROOT;runStage/runAction 的 opts 增加root(缺省 ROOT),透传 adjudicate → adjudicatePromptM,使 lane stage 失败触发的仲裁子代理在c.root上取证。Task 1 Step 2 的两份名单相应改写,消除"microStepContract 列入穿线名单(追加 c、调用方必传),而其主根专属调用方又列入保持名单(无 c 可传)"的自相冲突。 -
为什么:lane 内 runStage/runAction 失败的仲裁按原计划钉死主根(
git -C ROOT)——主根工作树在默认分支(或被兄弟 milestone 切换中),看不到 lane 分支上刚 commit 的 spec/plan/源码,retry/halt 裁决与 guidance 会基于错误树面作出;且名单按字面执行直接报错。 - 落点:Task 1 Step 2/3。
补 12:行为门 recordDecisions 直调收口 + 顶层 decisions 口径
-
改什么:Task 1 Step 3 给 runBehaviorGate/runBehaviorGateOnce 穿线 c 的同时,coding.mjs:1927/:1935/:1953/:1973/:1988 共 5 处 recordDecisions 直调改为
recordDecisions(site, list, c.decisions)。顶层 return 的decisions改为「results 各 r.decisions 拼接 ∪ 全局 sink 残量」;loop-end 的汇总口径写明 =「flush 失败模块的 r.decisions ∪ 全局残量」(同一条决策绝不既丢失又重复)。 - 为什么:Task 2 删除 flushedCount/decStart 水位线后,这 5 处直调仍落全局数组——frontend-phase 模块的 per-module RESUME flush 改读 r.decisions 后会漏掉全部行为门决策;顶层 return 也只剩残量,记账悬空。
- 落点:Task 1 Step 3、Task 2 Step 1-2。
补 13:锁非重入纪律(withMainRootLock 与 stackLock 同适用)
-
改什么:两把锁的定义注释钉死:「promise 链非重入:只允许 runModule 顶层逐段获取(
await withMainRootLock(() => runMilestone(m))、await withMainRootLock(() => recordResume(...))),被包函数及其调用链内绝不再调同一把锁;两把锁之间也绝不嵌套获取」。loop-end 的两处 recordResume(:2235/:2246,波次循环已结束、本无竞态)统一也走锁——保持单一纪律避免遗忘。 - 为什么:promise 链锁嵌套获取即互等死锁,且静默无诊断(Workflow 直接挂死);计划文字「runMilestone + recordResume 包进锁」没有说明获取层级,实现者若 runModule 包一层 + recordResume 函数体自包一层即触发。
- 落点:Task 9 Step 1。
补 14:Task 9 Step 2 伪代码修正
-
改什么:remaining 过滤改为按波次成员集:
const waveIds = new Set(wave.map(m => m.id)); remaining = remaining.filter(m => !waveIds.has(m.id))(绝不以 rs 为键反查——runtime-null 项没有 r.module,按 r.module 匹配会把故障模块留在 remaining,下轮 deps 不满足触发"图卡死降级全序"误重跑)。parallel() 调用处加注释:「结果数组与 thunk 数组按位次对齐(runtime 保证);null 仅表示 runtime 级故障(runModule 永不 throw)」。 - 落点:Task 9 Step 2。
补 15:lane 模式 milestone 前先收口 lane 树
-
改什么:runModule 在调 runMilestone 之前(仍在 lane 上下文)先对
c.root跑 worktreeClean + recoverDirtyWorktree(branch=module-<id>):in-scope 残留 commit 进模块分支、out-of-scope → halt(两个 prompt 已在 Task 1 穿线清单内,只补一个调用点)。runMilestone 自身(主根专属)的 step 1 wt-clean 保留——只管 merge 的机械安全。两个检查各管一棵树。 - 为什么:串行模式 milestone 的 wt-clean 同时守住「merge 机械安全」与「被测树 == 被合并树」两件事;lane 模式下它只看主根——lane 里 tdd/fix 留下的未提交文件参与了 testGate(test.mjs 跑在工作树上)却不进 merge,默认分支拿到的是"没测过的子集",正确性缺口无人兜底(残留只会让 removeLaneWorktree 的 bestEffort 删除失败留 log)。
- 落点:Task 2 Step 2(runModule 链内调用点)、Task 9 Step 1(与互斥段的相对位置:收口在 lane 上下文,先于取锁)。
八、调度与种子语义(Task 4/7)
补 16:调度器新增"共享表"边规则
-
改什么:schedulerPrompt 增加一条边规则:「两个模块的演示种子会触达同一张表(按 docs/03 的表归属 / docs/08 各模块
路径:字段判定共同表)→ 必须加 fk 或 seed 边(宁串勿并),evidence 写共同表名」。 - 为什么:演示种子 1000–9999 主键区间是按表隔离的;两个无依赖边的并行模块若都向同一共享表插种子,各自 lane 互看不见对方文件,PK 都从 1000 起取——合并后 seed-demo-data 逐文件 apply 撞主键(data-error),拖到行为门/e2e 才爆、归因困难。共享表本身就是不可并行信号,符合"宁串勿并"偏置。
- 落点:Task 4 Step 2。
补 17:seed NN 约定显式化(允许并行同号)
-
改什么:seedGenPrompt 在
c.vBase > 0(并行 lane)时条件渲染一条附注:「并行 lane 间允许同 NN:NN 仅表观排序,(NN, module_id) 才是唯一键;同波次模块无 seed 依赖边、互不引用,注入按完整文件名字典序应用(seed-demo-data 现状已满足)」。不做 NN 偏移预分配(良性撞号,最小代价方案)。 -
为什么:两个并行模块从同一基点各算 NN=max+1 必产同号文件(文件名含 module id,git 无冲突、
_demo_seed_history按文件名记账仍确定性工作),但静默违反 prompt 自己声明的"NN=max+1 唯一"字面约定——豁免理由必须落进 prompt/注释,否则实现者各自发挥。 - 落点:Task 7(seedGenPrompt 同文件顺带改动)。
九、验证清单(全计划)
补 18:语法门替换(基线 node --check 恒红)
-
改什么:实测基线
node --check workflows/coding.mjs恒红(node v26:export const meta判 ESM → 顶层 return 报Illegal return statement,exit=1)。新增确定性包裹检查脚本(如lib/check-workflow-syntax.mjs:读入文件 → 剥行首export→ 包进async function __wf(args, agent, parallel, phase, log) { ... }→ 写.tmp/临时文件 →node --check,透传退出码)。全计划验证清单(计划 :69/:104/:330 等)的node --check workflows/coding.mjs全部替换为该脚本。 - 为什么:恒红的唯一确定性门会让实现者习惯性忽略红灯(掩盖本次并行化引入的真语法错),或误以为自己改坏了文件。
- 落点:全计划验证清单、各 Task 验证步。
十、裁决记录(拒绝与改判)
拒绝(1 条):
- 「lane 测试库按波次序号命名(
<schema>_lane<N>)与 halt 取证语义冲突,且与 D3 的 moduleId 命名法不一致」——拒绝。理由:(1) 波次严格串行执行,_lane<N>不存在并发撞名;halt 后整个运行即停(波次边界 break),lane 库被同号覆盖只发生在用户主动重跑之后,取证窗口由用户掌控,且主要取证物是保留的 lane worktree(种子/迁移/证据文件),库内数据本就"不保证存活、保证可复现"(seedGenPrompt 既有哲学);(2) moduleId 命名引入截断规则、截断撞名与-字符的 MySQL 标识符/JDBC 引号问题(见补 8),是更大的正确性风险面;(3) 命名口径已由补 8 统一为序号制,不存在"两套规则"。正确性等同下取更简单方案。
改判说明(条目接受、其个别子建议被替代):
- lens「Seed 阶段完全不在并行隔离设计内」的"锁内段保持默认 schema/默认端口、不注入 lane env"子方案不采纳——与补 7 的 marker 兜底互相打架(marker 会把 lane 内 setup-test-db 确定性指向 lane 库);统一 lane schema(补 10)。stackLock 主张照案采纳(补 9)。
- lens「migration 版本段按 laneIdx 分配不稳定」的"docs02Idx 替代 laneIdx"与"段账本文件"子建议不采纳——补 4 的全局并集 maxV 已使 laneIdx 漂移无害,再加稳定键/账本是多余复杂度。
- lens「Task 8 Step 3 探测判据假阳性」的"test.mjs run() 拼 SPRING_DATASOURCE_URL 注入 + 探测 grep 注入代码特征"子方案不采纳——tdd 内循环不经 test.mjs,覆盖不全;改用占位约定 + 两点联查(补 6)。
- 三条「附录文件不存在」meta findings:接受,以本附录正式落盘为解决;其中对 D1 成立、D5 方向成立的核验结论已并入补 2/补 6。
实施偏差记录
(后续实现代理在此逐条登记与计划/附录字面不符的取舍:条目格式建议「Task 编号 / 偏差内容 / 原因」。)
-
Task 1 Step 2 / 穿线名单外的 4 个函数一并穿线 c:
seedStageContract、behaviorReverifyPrompt、currentBranchPromptM、createFeSkeletonTagPromptM不在计划字面名单,但都含${ROOT}且属模块作用域(Seed 链 lane 化见补 10;后两者由穿线名单内的 runBranchSetup / runFrontendSkeleton 调用)。不穿线则 Step 4 的 grep 审计(残留必须全部落白名单)必然失败,故按审计要求穿线。 -
Task 1 Step 2 / commitBlock 的 c 接在带默认值的 tail 之后:签名为
commitBlock(addPath, msg, tail = '…', c);两处用缺省 tail 的调用点显式传undefined占位(undefined触发参数默认值,null不会)。保持"c 为末位参数"的约定不破例。 -
Task 3 /
if (!c.lane)实现为if (c.lane == null):Task 9 伪代码的 lane 序号从 0 起(lane: i),!c.lane会把 lane 0 误判为主根模式(竞态改全局 phase + 误走主根分支)。Phase A 串行下二者行为完全等价(lane 恒 null)。 -
Task 2 Step 2 / 补 15 的 lane 收口调用点已加进 runModule,但以
if (c.lane != null)门控:补 15 落点指定 Task 2 Step 2,但其语义只对 lane 模式有意义;串行模式下 runMilestone step 1 的 wt-clean 已覆盖同一棵树(主根),无门控会平添一次重复子代理调用、违背"纯重构行为不变"。Phase D 启用 lane 后该段自动生效。 - Task 2 Step 1 / runAction 的 opts.dec 仅接受不消费:ACTION_RESULT_SCHEMA 无 decisions 字段,runAction 内无 recordDecisions 可透传;按计划字面收下 dec(调用点签名与 runStage 统一),函数内注释说明现状。
-
Task 1/2 验证步 /
node --check workflows/coding.mjs替换为node lib/check-workflow-syntax.mjs:按补 18 执行(基线恒红已复现:node v26 对export const meta+顶层 return 报 Illegal return statement)。包裹检查脚本本次新建(lib/check-workflow-syntax.mjs),.tmp/已加入仓库 .gitignore。 -
Task 2 Step 3 / 真实项目 resume 对照未执行,checkbox 留空:非交互子代理环境无真实 ERP 项目与 Workflow 运行时可跑。已用 stub agent/phase/log 对包裹后的脚本做行为冒烟替代验证:① 全 done 项目 → Router 全 skip → 返回
{results:[],pending:[],decisions:[]}、phase 仅 'Router';② 单 todo 模块首个微步骤 agent 返回 null → runModule 结构化返回{module,status:'halted',reason,decisions}(永不 throw)、串行 phase 序列 Router,Router,Milestone、halt 后 RESUME 条目照写。真实 resume 对照留待 Phase A 收尾的人工跑批。 -
Task 2 Step 2 / RESUME flush 成败记账新增模块级
resumeFlushedSet:计划的 runModule 返回形状({module,status,reason?,decisions})没有 flush 成败位,而补 12 的 loop-end 口径("flush 失败模块的 r.decisions ∪ 全局残量")需要它;用顶层 Set 记账而不扩 runModule 返回形状。附带效果:旧水位线下"模块 A flush 失败、后继模块 B flush 成功会把 A 的决策一并视为已 flush"的漏记缺陷被修复——这正是补 12 口径的要求(绝不既丢失又重复)。 - Task 4 Step 2 / docs/02 全序的 order 边以"紧邻前驱链"形态默认输出:计划只说"默认全保留为 order 边"未定边形态;对待跑序逐对全连接是 O(n²) 边爆炸且让松开几乎不可操作。prompt 写成:默认每个待跑模块对其 docs/02 紧邻前驱出一条 order 边(链式边传递地保留全序);松开某条链边必须对该模块与全部前序待跑模块逐对核查"查无 fk/api/seed 证据 ∧ docs/06 无顺序声明",任一对命中证据即对该对补对应 kind 边——语义与计划字面等价、不弱化"保留是默认"。
- Task 4 Step 3 / scheduleViolation 增加"重复模块项"检查:计划字面只列未知 id / 自依赖 / 环;但 deps 数组同 id 出现两项时 annotateDeps 的 Map 会取后值静默丢边,与"宁串勿并"相悖。重复项按同一违例路径处置(adjudicate retry → 仍违例则降级全序),不新增 halt 点。
-
Task 4 / runScheduler 在 todo 后端模块 ≤1 时跳过 scheduler 子代理:此时模块间不存在任何可判边(dependsOn 只能指向 done 模块,恒满足),直接按空边集标注(
annotateDeps(todo, [], allBackendIds),frontend-phase 硬规则照常生效),与"空边集通过校验"完全等价,省一次取证子代理调用。 -
Task 5 /
maxWidth以顶层 const 提前在 Phase B 定义:计划把const maxWidth = Math.max(1, ARGS?.parallel?.maxWidth ?? 3)写在 Task 9 Step 2 伪代码里,但nextWave(remaining, doneSet)两参签名钉死(Task 9 调用形即两参),宽度上限只能闭包取得。Task 9 实现时直接复用本 const,勿重复定义。 -
Task 4/5 验证 / 纯函数冒烟以
.tmp/临时脚本替代:coding.mjs 无法 node --test;除注释内联自证外,已从 coding.mjs 原文正则抽出 serialChainDeps / scheduleViolation / annotateDeps / nextWave 跑 22 个确定性 case(菱形/链式/全独立/环降级 + 全部违例分支 + frontend-phase 硬规则)全绿,脚本即跑即删不入库。 -
Task 8 Step 1 / scripts-test-template 不做
DB_SCHEMA = env || db.schema字面改动,改为 marker→env 回填:该模板实测不读 config-vars.yaml、自身不连库(D5 核验:schema 只被 setup-test-db / seed-demo-data 两脚本与后端 application*.yml 占位消费);按补 6/补 7 语义落为「env 缺省时读.tmp/erp-lane-db回填process.env.ERP_TEST_DB_SCHEMA」,让 gradle test fork 与 e2e globalSetup→bootRun 经 env 继承吃到 lane schema(占位${ERP_TEST_DB_SCHEMA:<缺省 schema>}消费)。另:三模板在 marker 生效时各打一行留痕 log(env 前缀在命令行上自明,marker 是不可见兜底路径,指错库排查需要痕迹)——仅覆盖路径新增输出,缺省路径行为一字不变。 -
Task 8 Step 1 / docs-04 模板锁定段顺带落盘补 5 的
out-of-order: true约定:补 5 落点写明该 docs-04 改动「Task 8 同批」;与补 6 的 application*.yml 占位约定写进同一锁定块(§1.1 后),避免悬空给后续 Task 各自发挥。 -
Task 8 Step 1 / 新建
lib/test-template.test.mjs:计划 Files 只列 setup-test-db 测试「+ 兄弟测试」,test 模板此前无测试文件;为断言 env/marker 真正贯通到全部子进程(D5 要害,5 个闸门步骤逐一探针)新建。占位{{...}}以探针脚本相对路径填充(模拟 skeleton-gen Plan 期填充;占位嵌在模板 JS 单引号字符串内,填充值不能带引号)。 -
Task 8 Step 1 验证 /
node --test lib/目录形式在 node v26 报 MODULE_NOT_FOUND,改跑等价 globnode --test 'lib/*.test.mjs':111 测试全绿(含本次新增 env 覆盖 / marker 优先级 / 贯通 case)。 -
Task 6 Step 2 / createLaneWorktreePromptM 增加第三参 defaultBranch:计划字面签名两参,但 prompt 文本的「不存在 →
worktree add -b <branch> <path> <默认分支>」需要默认分支真值;由调用方传 detectDefaultBranchPromptM 的结果(runBranchSetup lane 分叉已如此接线),不让子代理自行探测 main/master——保持单一真值来源与确定性。 -
Task 6 Step 4 / halt 的 RESUME lane 路径以 halted reason 前缀实现:runModule 返回形状(
{module,status,reason?,decisions})Phase A 已钉死不扩;catch 分支在c.lane != null时给 reason 前缀[lane 保留取证: <c.root>],RESUME halt 条目与终端⛔日志(都渲染 reason)即自然携带 lane 路径。lane 路径本身可由laneRoot(module.id)确定性重算,Task 10 重构图状 pending 记账时可改为结构化字段。 -
Task 8 Step 2 / lane 注入条件取
c.dbSchema非空而非计划字面的c.lane != null:注入内容必须渲染成品 schema 名,c.dbSchema为空时渲染只会产出坏约束(ERP_TEST_DB_SCHEMA=空值前缀);Phase D lane 模式两字段同时填充(lane 必有 dbSchema,建 lane 的前置是探测通过),语义等价且对"误建 lane 未填 schema"的异常态更安全。seedStageContract 同步注入(补 10);behaviorGate*/frontendSkeleton 含 setup-test-db/seed-demo-data 字样但属 frontend-phase 终波宽 1 主根专属,按补 10 豁免口径在注释中登记。 -
Task 8 Step 2-3 / 波次执行器侧接线留给 Task 9,Phase C 先落原语:
readDbSchemaPromptM()(config-vars.yaml 读 schema 基底,FIELD_VALUE_SCHEMA)、laneDbSchema(base, laneIdx)(补 8 序号制<schema>_lane<N>拼接)、laneDbSupportPromptM()(补 6b 两点联查,新增 LANE_DB_PROBE_SCHEMA:{scriptsEnv, ymlPlaceholder, detail?}两布尔独立返回、合取判定与降级 maxWidth=1 留 JS 编排层)均已定义;Task 8 Step 3 checkbox 留空——其字面含波次执行器的降级动作,属 Task 9 Step 3 职责。 -
Task 7/8(顺带)/ 补 9 的 seedGenPrompt、gatePrompt 起栈互斥注入文案已随 lane 注入落盘:文案描述编排层语义(「本 stage 整段在全局起栈互斥内执行,端口探测/残留 pid 回收只允许在持有互斥的本 stage 内做」),stackLock 本体按落点留 Task 9 Step 1;串行(c.dbSchema 空)不渲染,行为不变。补 17 的 seed NN 同号豁免按落点以
c.vBase > 0条件渲染进 seedGenPrompt。 -
Phase C 验证 / 条件渲染冒烟以
.tmp/临时脚本替代(同 Task 4/5 先例):抽取 laneRoot/vBase/laneDbSchema 与 8 个受改 builder 跑确定性断言(vBase 5 组边界值、lane/串行双态渲染、${ERP_TEST_DB_SCHEMA:...}占位字面量未被模板插值、worktree 三分支命令形)全绿,脚本即跑即删不入库;node lib/check-workflow-syntax.mjs通过。 -
Task 9 Step 1 / 两把锁以
makeChainLock()工厂统一生成:计划伪代码为let mainRootLock+ 独立函数;补 9 写明 stackLock 与 withMainRootLock 同构,故以工厂去重。语义逐点等价:链式 then 排队、fn 抛错 catch 复位尾指针、调用方拿到的 run 保留原始 rejection(halt 不被锁吞)。补 13 非重入纪律全文写在工厂定义处注释。 -
Task 9 Step 2 / remaining 过滤以
consumed集实现(补 14 的成员集口径 + 降级裁员细化):补 14 的 waveIds 按"nextWave 产出的整波"过滤,但 Step 3 的"本波次降级 maxWidth=1"会把波内未跑成员裁掉——它们必须留在 remaining 等下波,照搬整波 waveIds 会把未跑模块静默吞掉。consumed = 占用感知前移 halt 的成员 ∪ 实际执行的 wave 成员;键仍取自波次成员、绝不以 rs 反查(补 14 本意完整保留)。 -
Task 9 Step 2 / runtime-null 槽位 reason 带 lane 取证前缀并抢救 decisions:计划字面
reason:'runtime-null';实现为[lane 保留取证: <laneRoot>] runtime-null(与 runModule catch 同口径,Task 10 的 RESUME halt 条目渲染 reason 即自带 lane 路径)。lane ctx 预构造在 thunk 外(ctxs[i]),null 槽位仍能回收已登记的 c.decisions 进补 12 的 loop-end 口径。parallel() 只在 ≥2 宽 lane 波次使用,前缀恒有意义。 -
Task 9 Step 2 / 串行闸(useParallel=false)不调 runScheduler:计划行 12 要求缺省"走现行串行路径一行不差",而 runScheduler 会引入 Schedule phase 与一次取证子代理。串行直接
serialChainDeps(todo)(nextWave 在链式图上每轮恰出 1 个),执行语义与现行逐模块循环一致。唯一新增:占用感知微步骤按补 3b 字面("每个波次含宽 1")在串行波次也跑——宽 1 回退被残留 lane 毒化正是补 3b 的动机;无残留 lane 时为单次只读枚举、零状态变更。 - Task 9 Step 3 / 降级路径回收本次已建 lane:计划只说"任一失败 → 降级宽 1";若建到一半失败即降级,已建 lane 持有的 module-* 分支会让降级后的宽 1 主根 checkout 直接 fatal(降级路径自我毒化,且本波占用感知已跑过救不回)。prepareParallelWave 失败时对本次已建 lane 逐个 bestEffort 移除(刚建的树必净,移除幂等安全)再返回 null。
-
Task 9 Step 3 / 补 3a 的"残留 lane 移除失败"并入同一前移 halt 路径:补 3a 只规定"恢复失败 → halt";恢复成功/本就净但
git worktree remove失败时分支依旧被占,该模块走任何路径都会 fatal→仲裁→halt,故同按 branchSetup-dirty-worktree 口径前移结构化 halt(reason 带 lane 路径),不新增 halt 类。 - Task 9 Step 3 / 占用感知枚举自身失败 fail-open 跳过:补 3 未规定枚举微步骤(listLaneWorktrees)失败的处置;按"绝不因并行机制新增 halt 点"原则 log 后跳过本波感知——残留 lane 若真挡路,由 runBranchSetup 现行 fatal→仲裁→halt 语义兜底(等价不前移)。
-
Phase D 验证 / 行为冒烟以
.tmp/临时脚本替代(同前序先例):stub agent/parallel/phase/log 对包裹后脚本跑 27 个确定性断言全绿——①2 宽波次全 done(marker/lane 清理齐全)②同波次一 done 一 halted + 波次边界收敛 + pending blockedBy ③串行闸不调 scheduler/不建 lane/走主根 checkout ④runtime-null 槽位按位次记 halted ⑤lane DB 探测不支持 → 降级宽 1 后逐波推进全 done ⑥净残留 lane 收口复跑 / 脏残留恢复失败前移 halt 且兄弟照常 ⑦双链带 REQ 并行下 gate/seed 持起栈互斥零交错。脚本即跑即删不入库;node lib/check-workflow-syntax.mjs通过。Task 10 的 coding-start SKILL.md 与 README 两个 checkbox 不在本次(仅 coding.mjs 部分)范围。 -
Task 11 / spec 预生成启用闸
useSpecPrefetch = ARGS?.parallel?.features === true || ARGS?.parallel?.modules === true:计划未给字面闸;按"spec 预生成无物理冲突风险(batch 只写盘不 commit、文件名含 id 不相撞),可与模块并行开关解耦"自主决策——两开关任一开启即启用,前后端 featureLoop 同享。缺省(parallel缺省 /modules:false逃生口且未开 features)全关,维持与 master 串行等价。它不是 Phase F 的 feature 级并行(tdd/fix 链仍严格串行,那才归 features 闸全权管辖、受 Task 14 收益闸约束)。 -
Task 11 /
deriveSpecPrompt(id, phase, c, { batch = false } = {})是"c 为末位参数"约定的唯一特例:计划 Step 2 字面签名即deriveSpecPrompt(id, phase, c, { batch: true })(计划字面优先于 Phase A 接口备忘的穿线约定);opts 带默认值,现有 3 参调用点无须改动。特例已在函数头注释登记,后续阶段勿仿。 -
Task 11 / 统一 commit 微步骤以 bestEffortAction 跑,失败不 halt 而是清空 specById 全量回退主链串行重跑:计划只说"一个微步骤统一 add+commit"未定失败处置;按附录总则"绝不因并行机制新增 halt 点"——预生成写盘内容留在树上,主链 spec 重跑按 prompt 既有"已存在同 id spec 复用日期前缀/就地 Edit"规则复用产物(resume 语义同源),commit 仍失败由现行 commitBlock 的 halt 语义兜底(halt 留在现行路径,不新增类别)。微步骤自身幂等:
git diff --cached --quiet无 staged 变更即视为成功,绝不空 commit 报错。 -
Task 11 / 预生成加
items.length >= 2与todo.length >= 2两道阈值:计划未提阈值;单 feature 时预生成无并行收益、纯多一次统一 commit 微步骤(多烧一次子代理),直接走现行串行路径。2 项中 1 项已 done 时(todo=1)批量 tag check 结果仍被主链消费(doneById 缓存),仅 spec 不进 batch。 - Task 11 / 批量 tag check 直用 agent() 而非 agentR,null/失败槽位不预生成:agentR 的 throw 在 parallel() 内会被折成 null 丢失诊断;批量查失败的项留给主链 agentR 逐项重查(其 halt 语义与现行串行路径逐字一致),且不为完成态未知的项白烧 spec 子代理。
-
Phase E 验证 / 行为冒烟以
.tmp/临时脚本替代(同前序先例):抽出 featureLoop / deriveSpecPrompt / commitBlock 原文配 stub 跑 21 个确定性断言全绿——①预生成全成功(tag check 宽 3 + spec 宽 2、主链零 spec 重跑、统一 commit 含全部成功项、doneById 缓存零重查、plan/req-done 照常)②batch 单项失败落 null 回退主链串行(统一 commit 只含成功项)③统一 commit 失败 specById 清空全回退④闸关全串行(零 parallel() 调用、agentR 逐项)⑤单项不预生成⑥todo 单项不 batch 但缓存照常消费⑦deriveSpecPrompt 双态渲染(batch 无任何 commit 命令、非 batch commitBlock 原样)。脚本即跑即删不入库;node lib/check-workflow-syntax.mjs通过、禁用 builtin grep 零命中。 -
Task 12 /
planFactsPromptM(planPath, c)带末位 c:计划字面签名单参;plan 文件 commit 在模块分支上(lane 模式主根树面可能看不到),按 Phase A 穿线约定走microStepContract(c.root),c 为末位参数。 -
Task 12/13 / feature 测试库命名按补 8 落为
<schema>_lane<N>_f<i>,无截断规则:D3 原案(计划/任务摘要中的"确定性截断的<schema>_<module>_f<i>")被补 8 明文改判;实现取c.dbSchema(模块 lane 模式已是<schema>_lane<N>)直接缀_f<i>,串行主根 N 取 0(laneDbSchema(base, 0)),i = 批内槽位序号。 -
Task 13 / reviewWithFixLoop 增加尾参 opts
{ flipDocs08 = true }:"c 为末位参数"约定的第二个特例(先例 deriveSpecPrompt 的{batch},同因:带默认值的 opts 不动既有调用点)。动机:docs/08 相邻 REQ 行在并行 feature 分支上各自勾选,批后合并大概率同 hunk 冲突——冲突即触发"丢弃分支 + 串行重跑全链",cosmetic 副作用不配这个代价;feature lane 内跳过勾选,批后收口在模块分支统一补落(仍在 approve 之后、tag 之前,语义不变)。 -
Task 13 / feature 并行前置补 lane 测试库支持探测(计划 Task 12/13 未列):串行主根模式跑 feature 并行前按补 6b 两点联查(laneDbSupportPromptM)+ readDbSchemaPromptM 读基底,任一不满足 → 本模块全部批降级串行(log,不 halt)——并行 feature 链的 tdd 内循环测试若同连共享库会互相清库,降级判据与模块级同源。模块 lane 模式
c.dbSchema非空视为波次前置已探测通过,免重查。 -
Task 13 / mergeFeatureBranchPromptM 冲突时自动
git merge --abort:与 milestone 合并"冲突留树给人工"刻意相反——此处计划钉死了自动恢复路径(丢弃分支 → 回模块分支串行重跑),abort 恢复干净模块分支是该路径的前置,树面没有人工要救的东西(注释已写明取舍)。合并经 agentR:null 走既有 NULL_AGENT halt(计划只豁免"冲突不 adjudicate",不豁免 runtime 故障;null 时合并是否落地不可知,绝不盲目丢弃分支)。 -
Task 13 / 批内链失败的处置(计划未规定)按"批边界收敛"落地:兄弟链全部跑完、成功者完成合并 + 打 tag 落地后,统一抛
HALT feature-batch(失败链的 feature lane 整树保留取证,reason 带[feature-lane 保留取证: <path>]前缀);同批合并冲突成员的串行重跑此时搁置(模块即将 halt 不再起新链,resume 由 req-done tag 缺失兜底自动重跑)。链失败是真实 stage halt(串行链同样会停),不属 fail-open 豁免之列。 - Task 13 / 准入分批为"保 docs/02 原序的贪心连续分批":计划未定分批算法。无 facts / 改 schema 的 feature 自成串行项并截断当前批,绝不跨越它重排(后续 feature 可能依赖它的 schema/代码产出);批内成员放弃相对顺序(两两不相交 + 无 schema 改动正是顺序无关的准入前提);lane 建失败的退串成员在批后按序补跑。
-
Task 13 / 模块分支名取
currentBranchPromptM(c)的 HEAD:featureLoop 不接收 module 形参(Phase A 签名钉死featureLoop(items, phase, c)),feature 分支的基分支与合并目标取 c.root 当前 HEAD(runBranchSetup 已确保在module-<id>上),不另起约定;读取失败 → 全部批降级串行(fail-open)。 -
Task 13 / feature lane 路径约定
<模块工作树根>-feats/<id>、feature ctx 共享模块 decisions:路径由 feature id 确定性导出(模块 lane 模式下落在<ROOT>-lanes/前缀内会被 listLaneWorktrees 枚举,但其分支为 feat/,占用感知只按 module- 匹配,互不干扰)。feature ctx 的 decisions 直接引用c.decisions(单线程事件循环 push 原子),RESUME flush 与补 12 口径零改动;vBase 透传仅保字段语义一致(准入已排除 schema 改动,tdd 不取号)。 -
Task 13 / 执行序改变仅在 features 闸开启时生效:plan 全集先行(先串行跑完全部 spec+plan 再提取 facts 分批)只在
useFeatureParallel ∧ phase==='backend' ∧ items.length>=2生效;闸关 / FE phase / 单 feature 维持现行逐 feature 交错链。spec/plan/tdd/verify/review/tag 调用点抽为 featureLoop 内共用闭包(isDone/ensureSpec/runPlanStage/runImplChain/tagDone),并行批与串行链 site/label/allowContinue/dec/root 口径同源一字不差。 -
Phase F 验证 / 行为冒烟以
.tmp/临时脚本替代(同前序先例):stub agent/parallel/phase/log 对包裹后脚本跑 30 个确定性断言全绿——①并行批 happy path(plan 全集先行、改 schema 项不进批、feature lane 注入erp_lane0_f0/_f1、合并→勾 docs/08→tag 收口有序、lane 内跳过勾选、清理 lane+删分支)②合并冲突丢弃重跑(tdd 二跑落模块树、重跑通过才打 tag)③lane DB 不支持全批降级串行(探测惰性缓存单次)④批内链失败批边界收敛 halt(兄弟先合并落地、失败 lane 保留取证、不起后续串行项)⑤闸关串行等价(零 facts/零 feature 微步骤、逐 feature 交错)。脚本即跑即删不入库;node lib/check-workflow-syntax.mjs通过、禁用 builtin grep 零命中、${ROOT}审计残留全部落主根专属白名单。
终审遗留修复(F1–F11)
-
F1 修复:scheduleViolation 增加 deps 完整性校验——todo 后端模块必须在 deps 恰好逐一覆盖,缺项即违例(处置同其它违例:adjudicate retry → 降级全序,不新增 halt 点);annotateDeps 的
|| []保留为防御并注明完整性校验后理论不可达(仅 0/1 模块免调度的空边集路径会走到,语义恰为"无依赖")。 -
F2 修复:featureStageContract 的 lane 注入段(c.dbSchema 非空分支)追加「lane 内禁全量测试入口」硬约束——tdd/verify/fix 只允许目标化测试命令(gradle 按类/按模块过滤、vitest 过滤模式),禁止
node scripts/test.mjs/pnpm e2e:ci等含 e2e/冷起全栈的全量入口(固定端口起栈击杀兄弟 lane),全量回归只发生在 test-gate(编排层起栈互斥保护)。 - F3 修复:runScheduler 自 scheduler agent 调用起整体 try/catch,catch 路径 log 后降级 serialChainDeps——fail-open 不再只依赖 agent() 永不 reject 的契约,runtime 抛错不会让 Workflow 顶层崩溃。
- F4 修复:phase('Schedule') 移到「todoBackendIds ≤ 1 早退」分支之后——todo 为空 / 仅 1 个后端模块时完全不调 scheduler 也不切 Schedule phase,resume 全 done 项目的 UI 终态不再从 Router 漂移。
- F5 修复:schedulerPrompt 共享表边规则(规则 5)补方向约定——按 docs/02 § 二顺序后者依赖前者的单向边,明令禁止双向各输出一条(成环白白损失并行度)。
-
F6 修复:createLaneWorktreePromptM / createFeatureWorktreePromptM 第 1 步改精确匹配语义——按
git worktree list --porcelain行解析worktree <path>字段全等于 path 才算已注册,明令禁止子串包含判断(id 互为前缀的误匹配)。 -
F7 修复:microStepContract 的「所有 git 命令必须用 git -C 」补例外说明——除非步骤明示了其它
-C目标(lane / feature worktree 路径,以步骤原文为准),消除与 lane prompt 内git -C <path>步骤的字面冲突。 -
F8 修复:新增顶层
laneEnvCache(null=未探测 / {fail} / {baseSchema})缓存 lane 库 schema 基底 + lane DB 支持度两点联查——首个 ≥2 宽波次探测后复用,成功失败都缓存;agentR 抛错(瞬时 runtime 故障非环境事实)不缓存留下波重探;maxV 不入缓存每波重读。featureBatchRun 侧已有 featPrep 惰性缓存,不在本项范围。 - F9 修复:laneOccupancySweep 的枚举步改直调 agent() 判空——null 安静 log 后 fail-open 跳过本波感知,不再经 agentR(其 null 路径先打 ⛔ HALT 再被 catch 吞掉,稀释「⛔=真 halt」约定);模块级恢复/移除仍走 agentR(那是真结构化 halt)。
-
F10 修复:currentBranchPromptM 输出段
BRANCH_NAME_SCHEMA笔误改为实际下发的DEFAULT_BRANCH_SCHEMA。 - F11 修复:spec 预生成 runStage 的 dec 改每项临时 sink(specDecsById),仅当该项 specById 成果被主链 ensureSpec 实际消费时并入 c.decisions(delete 防二次并入,不重复打 log);thunk 失败落 null 与统一提交失败清空 specById 时连 sink 一起丢弃——主链重跑会重新登记,同一 stage 决策不再记两遍。