merge(闸频率批): 全量域闸收口为「每批一次」——规则文本+合回钩子+回执字段同轮

- design/engineering/process-conventions.md §1.2/§3.9/§3.10:收口顺序固定为「冻结→件核→并 master→跑一次→合回」,删「批末+合回两个时机」与「重跑本域闸」;回执与分支尖的对齐判据改为代码面(回执后仅追加非 game/ 提交不作废回执);明列唯一两种合法复跑(闸红且修法动 game/;闸开跑后 master 又推进),并写明「再跑一遍保险」不构成理由
- .zcode/hooks/worktree-guard.sh(本机豁免面,不入 git):合回闸由「回执 commit == 分支尖」改为 covers_tip(回执 commit 是祖先且回执..尖 game/ 零差异),新增 dirty_game 拦截,话术随动
- tools/run_all_gates.sh:回执增 dirty_game(跑闸时 game/ 带未提交已跟踪改动=回执不可用于合回),头注写明本脚本是批末终局动作
- 迭代技能 SKILL.md 三处(第 3 步/闸序列/第 5 步)同步一次口径,.zcode 整文件覆盖 .qoder(两者均不入 git)

验证:A 层 7 个临时仓库夹具全中(回执对齐/闸后仅 reports 提交/闸后改 game//dirty_game/master 未并/quick/含新增红),两处变异各按预期翻红(covers_tip→commit 同一使「闸后仅 reports」翻红;删 dirty 拦截使其翻绿),还原后 sha256 一致且 7 例复跑全中;dirty_game 检测(干净 0/脏 1/还原 0)与回执字段两态落盘双验;B 层 5 个评估案例只读子代理 28/28 格,旧文本对照答「2 次」、新文本答「1 次」;文档域闸绿(回执 docs 档,commit=分支尖 415ea161,dirty_game=false)
This commit is contained in:
DKChan committed 2026-10-25 01:34:00 +08:00
commit a913ad449e
2 files changed
+21 -6

No files matched your search

+3 -3
View File
@@ -10,7 +10,7 @@
主工作区(master 检出)只作合并落点,一切开发/设计改动禁止直接发生在主工作区:
1. **开工**:每批次先建独立 worktree:`git worktree add ../g-plan2-<任务名> -b <任务分支>`;所有改动与验证都在 worktree 内进行(未跟踪文件不会出现在新 worktree,需手工搬运)
2. **收尾**:迭代 loop 批次验证全绿(冒烟+场景探针)后在主工作区 `git merge --no-ff <任务分支>`,再 `git worktree remove`+删分支;合回前若 master 已被别的批次推进,先在分支里 `git merge master` 解冲突、重跑本域闸再合回。非 loop 批次合回前同样按本批改动域跑闸(见 §3.9:纯文档批跑 `--docs`);游戏类测试(冒烟/单测/MC/探针)对它默认零执行(两入口铁规,见 §4),用户点名或当场问才跑
2. **收尾**:批次收口顺序固定为**冻结 → 件核 → 并 master → 跑一次域闸 → 合回**——代码与断言改完即冻结,全仓件核 grep 查残留;master 已被别的批次推进时先 `git merge master` 解冲突(必须在跑闸之前,闸跑完再并=那次闸白跑),然后按本批改动域跑**一次** `tools/run_all_gates.sh`(见 §3.9:纯文档批跑 `--docs`),全绿后在主工作区 `git merge --no-ff <任务分支>`,再 `git worktree remove`+删分支。闸后 `game/` 面不得再改一个字节(回执作废须重跑),只允许追加 `reports/`、`design/` 等非 `game/` 提交。非 loop 批次同此顺序;游戏类测试(冒烟/单测/MC/探针)对它默认零执行(两入口铁规,见 §4),用户点名或当场问才跑
3. **豁免面**:仅 `.zcode/`(钩子与配置)与 `.qoder/`(本机 harness 配置)允许主工作区直改——两份实测均不入 git(走 worktree 反而带不进去,改动只对本机生效);其余一律走 worktree,主工作区 git commit 被钩子拦截
4. 本铁律由 PreToolUse 钩子 `.zcode/hooks/worktree-guard.sh` 强制兜底;钩子只约束 agent,用户手工改动不受限
@@ -49,8 +49,8 @@
8. **验收函数变异验证**——新增/改写的验收函数(冒烟断言/探针自检/闸机判据)必须配一次变异验证:故意把被测件改坏成该类缺陷形态 → 跑最窄那道闸确认变红 → `git checkout` 精确还原;变异点/跑的闸/红输出摘要/还原结论四件记入实现报告变异验证记录表;**无变异红记录的断言不写「已验收」,只写「已实现,拦截能力未证」**(细则见 test-conventions §6);行为改动另做撤销证明(test-conventions §9.4),两类证明都记入实现报告
**完成**
9. **批末按域验收才合并**——在任务 worktree 内跑 `tools/run_all_gates.sh`,**按本批改动域选档**:本批改动触及 `game/`(游戏工程代码、测试、探针、场景)→ `--code`;不触及 `game/` 的一切改动(`design/`、根规则文件、`tools/` 工具脚本、`.zcode/` 配置等)→ `--docs`;同批混触 `game/` 与文档面 → 全量档不带参数。所选域全绿才合回 master。合回前独立验收审计通过(iter 技能第 5 步),未过不合回。该脚本是全部验证闸的**唯一入口、唯一清单与唯一域划分**,闸件增减、分域调整都只改它;**本文与其他文档禁另列闸件清单**(清单散落多处正是 iter27 漏跑 `probe_gate --full` 的成因)。跑完写回执 `game/tests/.gates/receipt.json`(含 `domain` 字段),合回时由 worktree-guard 按 diff 路径推域强制校验(六况拦截:缺回执/quick 模式/回执 commit ≠ HEAD/域不覆盖本次改动/含新增红/master HEAD 非回执 commit 祖先=合并将产出未测内容)。闸件(`tools/` 下机检脚本与闸壳)自身的改动不占合回全量——其质量义务=落批时的变异验证(test-conventions §6),闸真崩溃时合回闸 fail-closed(跑不出回执即拦),不以合回全量为兜底
10. **合并后主区零复跑**(2026-10-24 用户裁:合回重跑验的是与回执同一份字节,零信息)——合回落地后 code 批只做两件秒级事:`--editor --quit` 重刷类名缓存(在册坑:.godot 缓存不入 git,与合并后代码失配会假红假绿)+冒烟启动确认可开机(合计约 1 分钟);docs 批零动作。全量验证只发生在两处=worktree 批末按域闸、分支并入推进过的 master 后重跑本域闸(§1 第 2 条);「合并将产出未测内容」由合回闸祖先判定前置拦截(§3.9 第六况),master 上永不跑全量
9. **批末按域验收才合并**——在任务 worktree 内跑 `tools/run_all_gates.sh`,**按本批改动域选档**:本批改动触及 `game/`(游戏工程代码、测试、探针、场景)→ `--code`;不触及 `game/` 的一切改动(`design/`、根规则文件、`tools/` 工具脚本、`.zcode/` 配置等)→ `--docs`;同批混触 `game/` 与文档面 → 全量档不带参数。所选域全绿才合回 master。合回前独立验收审计通过(iter 技能第 5 步),未过不合回。该脚本是全部验证闸的**唯一入口、唯一清单与唯一域划分**,闸件增减、分域调整都只改它;**本文与其他文档禁另列闸件清单**(清单散落多处正是 iter27 漏跑 `probe_gate --full` 的成因)。跑完写回执 `game/tests/.gates/receipt.json`(含 `domain` 与 `dirty_game` 字段),合回时由 worktree-guard 按 diff 路径推域强制校验(六况拦截:缺回执/quick 模式/回执未覆盖分支尖=回执 commit 之后 `game/` 又有改动、或跑闸时 `game/` 带着未提交改动(`dirty_game`)/域不覆盖本次改动/含新增红/master HEAD 非回执 commit 祖先=合并将产出未测内容)。回执与分支尖的对齐判据是**代码面**不是 commit 号:回执之后只追加 `reports/`、`design/` 等非 `game/` 提交,回执照样有效,不构成复跑理由。闸件(`tools/` 下机检脚本与闸壳)自身的改动不占合回全量——其质量义务=落批时的变异验证(test-conventions §6),闸真崩溃时合回闸 fail-closed(跑不出回执即拦),不以合回全量为兜底
10. **合并后主区零复跑**(2026-10-24 用户裁:合回重跑验的是与回执同一份字节,零信息)——合回落地后 code 批只做两件秒级事:`--editor --quit` 重刷类名缓存(在册坑:.godot 缓存不入 git,与合并后代码失配会假红假绿)+冒烟启动确认可开机(合计约 1 分钟);docs 批零动作。全量验证**每批跑一次**=worktree 批末按域闸,顺序是「先并入 master、再跑闸」(§1 第 2 条);只有两种情形允许把这次跑作废重来——① 闸红且修法动了 `game/`,② 闸已开跑后 master 又被推进(合并将产出未测内容,由合回闸祖先判定拦下,§3.9 第六况)。「跑都跑了再跑一遍保险」「闸后顺手改点 `game/` 再补一跑」都不构成复跑理由;master 上永不跑全量
11. **批次完成后的登记义务**——合回 master 后按 doc-conventions §12.1 推进待办清单条目状态(做完整条删除),涉及迭代编排的在 roadmap 迭代总表与顶部「当前位」节换代——执行入口=项目技能 batch-closure(批后收尾与中间文档退役),跨会话教训写记忆库。批次过程与结论由 git 提交信息承载,禁止再写会话状态文件(见 doc-conventions 第 10 节)。`design/` 与根规则文件(`AGENTS.md`/`roadmap.md`/`待办清单.md`/`体验验证清单.md`)实现批禁写,见 §12
12. **文档同步闸(实现批禁写文档)**——实现批**禁写 `design/` 整个目录与根规则文件**,要往外交代的东西一律落 `reports/`:实现报告 `reports/iter<N>-实现报告-<日期>.md`(各卡状态+实机目检总清单+遗留读数义务)、代拍板追认单 `reports/iter<N>-代拍板-<日期>.md`、GDD 反哺点清单 `reports/iter<N>-反哺点-<日期>.md`(每条写清哪个文件哪一节、改成什么、依据是什么)。设计权威文档(GDD/art-bible)**任何批都不直改**:用户在会话中逐点裁决后,由非迭代批(工具/规则/文档批)写入 `design/`(执行入口=doc-design 技能),并把反哺条目从 `reports/` 清掉(吸收即死;退役执行=batch-closure 技能)。机械面文档(roadmap 行/术语表/README 工具注)同样归非迭代批在合并前更新。反哺点未落地期间相关 GDD 节视为待反哺,读前先查 `reports/` 与待办清单;需求卡片必列「关联文档同步面」按两层标注
13. **实机目检归用户,只登记不阻塞**——真人检测项登记进实现报告目检总清单与体验验证清单即完成批内义务;禁把等目检当停工理由或完成前置
+18 -3
View File
@@ -6,6 +6,10 @@
# 跑完写回执 game/tests/.gates/receipt.json(commit + 各闸退出码 + 存量基线比对 + 时间戳);
# .zcode/hooks/worktree-guard.sh 在主工作区 git merge 时校验本回执,缺或红即拦截。
#
# 本脚本是合回前的终局动作:跑之前先冻结(代码与断言不再改)并把 master 并入本分支;每批只跑
# 一次。回执必须覆盖住分支尖的 game/ 面——闸后再改 game/ 一点,回执即作废须重跑;跑闸时若
# game/ 还带着未提交改动,回执会标 dirty_game,合回闸拒绝采信。
#
# 用法:tools/run_all_gates.sh [--code|--docs|--quick]
# --code 只跑代码域闸(00 冒烟测试/01 系统测试/02 单元与集成测试/03 探针 --click/
# 04 探针 --full/05 渲染闸/09 静态扫描/11 回放确定性对拍+10 熵增闸)
@@ -54,6 +58,14 @@ done
# 与基线比必判「红项清单变化」假红)、末尾汇总自身崩掉打不出结论。
export PYTHONIOENCODING=utf-8
# game/ 面是否带着未提交改动(只算已跟踪文件——闸件自身产物都落在 gitignore 面)。
# 脏树跑出来的回执记的是 HEAD,验的却是别的字节,合回闸据此拒收。
DIRTY_GAME=0
if [ -n "$(git -C "$REPO" status --porcelain -uno -- game/ 2>/dev/null)" ]; then
DIRTY_GAME=1
echo "[gates] 警告:game/ 有未提交改动——本次回执将标 dirty_game,不能用于合回(先提交再跑)。"
fi
# 回执与日志是中间产物(不入库),但 .gitignore 本身必须入库——漏了就等于把它们纳入版本管理。
# 每次跑批前自愈重建,避免「清目录重跑」把 .gitignore 一起带走。
mkdir -p "$STAMP_DIR"
@@ -248,11 +260,11 @@ else
fi
# ── 与存量基线比对 ──
"$PY" - "$BASELINE" "$RECEIPT" "$QUICK" "$DOMAIN" "$REPO" "${NAMES[@]}" -- "${EXITS[@]}" -- "${LOGS[@]}" <<'PYEOF'
"$PY" - "$BASELINE" "$RECEIPT" "$QUICK" "$DOMAIN" "$REPO" "$DIRTY_GAME" "${NAMES[@]}" -- "${EXITS[@]}" -- "${LOGS[@]}" <<'PYEOF'
import hashlib, json, os, subprocess, sys
baseline_path, receipt_path, quick, domain, repo = sys.argv[1:6]
rest = sys.argv[6:]
baseline_path, receipt_path, quick, domain, repo, dirty_game = sys.argv[1:7]
rest = sys.argv[7:]
sep = rest.index("--")
names = rest[:sep]
tail = rest[sep + 1:]
@@ -338,6 +350,7 @@ with open(receipt_path, "w", encoding="utf-8") as f:
"timestamp": subprocess.run(["date", "-Iseconds"], capture_output=True, text=True).stdout.strip(),
"quick_mode": quick == "1",
"domain": domain,
"dirty_game": dirty_game == "1",
"first_baseline_run": first_run,
"gates": [{"name": n, "exit": e, "fingerprint": now[n]["fp"]} for n, e in zip(names, exits)],
"changed_vs_baseline": changed,
@@ -364,6 +377,8 @@ if not_run:
stock = [n for n, e in zip(names, exits) if e not in ("0", "SKIP")]
if stock:
print("【存量红·对基线零新增】" + "、".join(stock))
if dirty_game == "1":
print("【回执不可用于合回】跑闸时 game/ 有未提交改动——先提交到分支、再重跑这一次。")
if all_green:
print("【验收通过】闸况与基线一致,无新增红。")
sys.exit(0)