用于 quant-scanner-v8(及同类双机/多会话共写仓库)的双机交接治理。 触发场景——主人说「最近双机联合改动大,是不是遇到很多交接方面的问题」 「有什么办法解决么」「不浪费时间也不浪费token」「交接太乱了」, 或你要写/读一份 `docs/ops/handover/*.md` 交接档之前, **或你要动 quant-scanner-v8 仓库的任何文件之前** —— 本协议亦管「动手前的并发预检」, 入口索引见仓根 `AGENTS.md`。 核心:把「每事一档」的聊天式交接,改为「唯一状态源 + 代码注释 + 仅在需人拍板时写档」。 一句话:**交接档是给人拍板用的,不是给机器同步状态用的。**
Instal·la
npx skillscat add ah-quant999/quant-scanner-v8/v8-handoff-gateway Instal·la des del registre de SkillsCat.
🔴 载体与版本(2026-09-21 入仓 · 阿狸咪评审
handoff-skill-inrepoP1 的落地)本文件(仓内
docs/ops/skills/v8-handoff-gateway/SKILL.md)是唯一权威载体。~/.workbuddy/skills/v8-handoff-gateway/SKILL.md是派生物 —— 双机各自cp得到。
入仓前实测:阿狸咪机~/.workbuddy/skills/下此 skill MISSING、仓内find -name "*handoff-gateway*"0 命中
⇒ 协议自述目标「让 AI 会话自动遵守,不靠人记」对另一台机已落空。纪律:改协议必须「先改仓内 → 再
cp到本地」。反向(本地改了没进仓)会造成双机分叉 ——
那正是本仓在index.html上已吃过两次亏的同一形态。同步命令(双机通用):
cp docs/ops/skills/v8-handoff-gateway/SKILL.md ~/.workbuddy/skills/v8-handoff-gateway/SKILL.md2026-09-21 12:0x 新增协议要素(随入仓一并固化)
① 并发写协议 = A + C + D(原项
handoff-concurrent-write)
- A 一次 push 只改自己 owner 的 item。
- C 本地 git 路径:写前
git pull --rebase,冲突即 abort,禁裸 push。- D 🔴 API 路径(本仓实际主用
base_tree+force:false):
该路径不产生 merge 冲突 —— 若「现取 tip」再推同一文件,自己的 blob 会静默顶掉对方改动。
⇒ 比冲突严重一档:冲突会喊,覆盖不会。
故:推单文件前重取该文件 blob sha;与读取时不一致 ⇒ 不是 abort,而是基于新内容重放
(本文件写入语义是「追加/改自己项」,天然可重放)。
这与本仓既有铁律「基线漂移预检:字节数不等即重基线重放,绝不 force」同型。② 状态源有 [13/13] 门禁了(原项
handoff-yaml-guard)pre_deploy_audit.py的check_handoff_ledger()断言:yaml可解析 / 每项 id+priority+owner+status 齐全 / status ∈ 白名单 /pending-verify必有verify_how/ 无未来时戳(容差 5min)/ids ⊇ docs/ops/HANDOFF.ledger.json的 ids /next_id_seq ≥ ledger.max_next_id_seq。🔴 为什么需要外部锚
HANDOFF.ledger.json:单文件下next_id_seq与条目序号会一起回滚,
「文件内部自洽性」永远检查不出被旧版本覆盖。
⇒ 推HANDOFF.yaml时必须同一提交刷新 ledger:追加新 id、抬高max_next_id_seq;
每月清理已完成项时同批更新ids。ledger 缺失 ⇒ 门禁放行并告警(守卫自身不成单点)。
双机交接网关(HANDOFF Gateway)
🔴🔴 主人工作守则(2026-09-23 主人令 · 正式入协议 · 双机所有会话/专家通用)
来源:主人 2026-09-23 原话 ——「写到双机新的那个协议里去,不要我每个专家都说一遍」。
本节对所有专家、所有会话、两台机一体生效:任何会话开工读到此节即视为已被告知,
无需主人对每个专家重复声明,也不得以「没轮到我」为由豁免。
1. 提问边界(只有一种情况才问)
只有不确定主人的意思和意图时,才问主人的意见和建议;
其余情形一律用专家的科学方法自主行事 ——
发现问题就直接一劳永逸修复,修好后审计没问题就直接上线
(与 2026-09-21「修好+审计完直接上线」常驻授权同源,此处重申并入协议)。
2. 邮件纪律(告警闸门)
不要一直发邮件 —— 只能发「不能自愈、不能解决、必须主人出面」的邮件。
进度汇报 / 自愈成功 / 已自行解决 / 计划内重试中 / 误报复核通过 —— 一律不发。
3. 三件套(每笔改动收口,缺一不可)
① 审计 —— 改完必审计(源码级断言 + 线上真值验证,不以 run 结论代替);
② 四方对齐 —— 云端、双机、运维页、逻辑详解页四处口径一致(含卡名/任务/producer 可反查);
③ 推仓防覆盖 + 交接 —— 推仓入库走防覆盖(expect-base / 基线断言,绝不 force),
并按本协议与另一台机交接(改 HANDOFF.yaml 状态字段,不写回执档)。
4. 全程说中文(与主人及所有专家交互、所有交付文档一律中文)
所有与主人及专家的对话、所有交付文档 / 报告 / 交接件 / 代码注释中的说明文字,一律使用简体中文;
代码标识符(变量名、函数名、分支名等)可保留英文,但禁止在中文叙述里夹带英文混答、
禁止用英文写交付结论或状态汇报。本守则与用户级长期记忆「🔴🔴 全程简体中文」同源,此处并入协议,双机一律执行。
0. 为什么需要这个 skill(实测病根)
2026-09-21 实测本仓 docs/ops/handover/:
| 指标 | 实测值 |
|---|---|
| 交接档总数 | 518 个 |
| 近 7 天字节数 | 431 KB |
| 峰值日(09-20) | 62 档/天,21 时一小时 9 档 |
| 「回执_未回执交接对齐」纯握手档 | 22 个 / 417 KB / 均值 19 KB,零技术内容 |
| 09-20 单档标题重复次数 | 同一标题 7 次(催办) |
三层病根:
- 把「聊天」当「交接」 —— 回执档(「收到」「已核验」)+ 催办档(「还没回我」)占大头,零技术内容。
- 同一件事拆成 3–5 档 —— 典型链条:
改 → 核验 → 催办三连。
实测 06:58(改动) / 07:05(收口) / 01:47(催办) 是同一件事。 - 状态散落 —— 「什么还没做」散在几百个档里;下一个会话(甚至同一台机的我)
无法快速知道当前挂账,只能重新实测一遍 ⇒ 重复劳动 = 双重浪费。
真实代价:每档 = 写 + push + 对方拉 + 读 + 回写。双机各一次。
且每档永久进 git 历史,git log/grep 检索被稀释。
1. 铁律(改任何交接行为前先读)
- 🔴 状态只写
docs/ops/HANDOFF.yaml,不散落在档里。
查「还有什么没做完」= 读这一个文件,不翻 handover 目录。 - 🔴 技术细节写进代码注释。 「改了哪行、为什么这么改」就地可查 ——
改代码的人必然看到注释,但不会去翻 500 个档。 - 🔴 改动记录写进 git commit message。 这是天然的交接收件箱:
git log --oneline一行可查,不占额外文件、不需额外推送。 - 🔴 严禁「回执档」「催办档」。
收到 = 改HANDOFF.yaml的status字段(一次编辑,几行)。
需要催办 = 说明档里没写清「谁该做什么」⇒ 修根因,不加催办。 - 🔴 只在两种情况写 .md 档:
(a) 需要人拍板(决策类,含选项与推荐);
(b) 跨机需知的结构性变更(改了双机共同依赖的协议/口径)。
技术改动、核验结果、进度汇报 —— 一律不写档。 - 🔴
pending-verify状态不得宣称「已修复」。
判据纪律:代码改了≠产物重跑了≠被 run 验证生效。
必须填verified_at才能置done。
1.7 🔴 写档前的「3 秒判定测试」(2026-09-21 补 —— 协议上线当天即被自己违反后加)
实测病根不是「大家不认规则」,是「判定边界模糊」:协议 2026-09-21 08:45 上线,
当天 08:45 之后仍新增 20 档(目标 5–10 档/天;全天共 36 档),其中 2 份是协议后写出的「回执」档,
且体量最大的那份出自本协议一侧(小九)自己。
数字由 docs/ops/scripts/handover_rate.py 统计,可复跑。
| 逐条问(任一「是」才写档) | 是 ⇒ | 否 ⇒ |
|---|---|---|
| ① 这份档的内容,能用一行 commit message 说完吗? | 不写档,写 commit message | 问 ② |
| ② 里面有要我以外的人拍板的选项吗(含选项对比 + 明确推荐)? | 写档 (a) | 问 ③ |
| ③ 另一个会话 / 另一台机必须改变自己的行为(协议 / 口径 / 常量)才能不踩坑吗? | 写档 (b) | 不写档 |
🔴 反向必查(比测试本身更重要):动笔前先读 HANDOFF.yaml ——
若这件事已有 item,只改 item 的 status / evidence,一律不写档。
实测反例:08:55 新增的那一档,正文讲的正是已在 yaml 登记的 maharo-daily-s4u。
🔴 写档后必须做的一件事:在 HANDOFF.yaml 该事已有的 item 里补一行 doc: <档文件名> ——
否则下一个会话只读状态源,看不到还有一份档存在(档就成了暗账)。
🔴 用词禁令(消除歧义):本协议中「回执」一词仅指被 §1.4 废止的「回执档」。
需要对方做事 / 拍板时,一律写「待 <机名> 处理」「需 <机名> 拍板」,
不要写「需 <机名> 回执」 —— 后者会被读成「要求对方再发一份回执档」,与 §1.4 正面冲突。
(2026-09-21 主人即据此措辞提出疑问:「新的交接方式不是不用回执了吗?」)
机器判据(不靠自觉):docs/ops/scripts/handover_rate.py 周跑一次,把
「近 7 天新增档数 / 回执催办类档数 / 协议上线后新增档数」写入 HANDOFF.yaml 的 meta.handover_stats。
先可量化,再谈阻断 —— 超阈值只记数 + 汇报,不阻断 deploy。
2. 状态源格式(docs/ops/HANDOFF.yaml)
meta:
updated: "YYYY-MM-DD HH:MM"
updated_by: "小九(lemoncat-cn)" # 或 阿狸咪(alimi-cn)
next_id_seq: 12
items:
- id: build13-timeout-guard # 稳定标识,用于跨档引用
priority: P0 # P0 / P1 / P2
owner: shared # 小九 / 阿狸咪 / shared
status: pending-verify # todo/doing/pending-verify/blocked/done
what: "第13步超时护栏缺口:只包 git 命令未包 python 重建"
fix: "已加 timeout 300 + 失败即 exit 1"
evidence: "commit 61bc3fc03070"
verify_how: "等下一轮 run 核对 step[13] 耗时 ≤20min" # pending-verify 必填
verified_at: null # done 时必填
updated: "2026-09-21 08:40"status 语义(最易误用的一栏)
| status | 含义 | 对外可否称「已修」 |
|---|---|---|
todo |
未开始 | 否 |
doing |
进行中,有 active owner | 否 |
pending-verify |
改动已上线,尚未被运行验证 | 🔴 否 |
blocked |
等外部条件(等 run / 等对方 / 等拍板) | 否 |
done |
已验证生效(须填 verified_at) |
可 |
🔴 编辑状态源的 4 条硬约束(2026-09-21 批④ 实测入仓)
改 HANDOFF.yaml 之前逐条过。四条都踩过,且四条都不会报错 —— 全是静默的。
1. 正文必须写进 note: > 块标量;note_to 只做收件人标记。note_to: 阿狸咪 后面若不跟 note,对方按 §3.5 去读 note 会读到空 ⇒
你写了等于没写(悬空 note_to)。正文不要做成裸行接在别的字段后面 ——
那样会被 YAML 归给前一个字段,note 仍为空。
2. 插新键前先 dump 目标 item 的 4 空格顶层键清单查重。
YAML 同 item 重复顶层键取后者,较早那份被静默丢弃,而 grep 还会说谎
(明明写着 todo,生效的是 doing)。已有 note: 键时 ⇒ 追加进该块标量末尾,不新建。
# 查重(对某个 item)
from collections import Counter
import re
blk = item_block(y, "<ID>")
dup = {k: v for k, v in Counter(re.findall(r"(?m)^ ([A-Za-z_]\w*):", blk)).items() if v > 1}
assert not dup, dup3. 改了 item 内容 ⇒ 同批刷新 HANDOFF.ledger.json 的 id_content_hashes。
官方口径(2026-09-21 实测复现):
blk = 从 " - id: <ID>" 起到下一个 " - id:" 前(含其前的 # ── 注释,**不切**)
blk = re.sub(r"^(\s*updated:\s*).*$", r"\g<1><ts>", blk, flags=re.M) # 归一化为**字面量** <ts>
h = hashlib.sha256(blk.rstrip().encode("utf-8")).hexdigest()[:16]⚠️ 归一化值写字面量 <ts>,不是新时间戳实值(写实值会让锚静默失效)。
⚠️ 该哈希不在 CI 门禁里(pre_deploy_audit.py 里 id_content_hash 命中 0 处)
⇒ 漏刷不会红,只会让锚失效。
4. 一切时刻用 TZ=Asia/Shanghai date 实取。
未来时戳会污染本仓「取 MAX 时点」的新鲜度判据,并被 [13/13] 门禁抓住
硬阻断 deploy(2026-09-21 18:25 CST 起实测阻断 4 轮 CI,下游部署步全 skipped)。
3. SOP
3.1 开工前(成本 1 次读取)
# 读状态源,确认当前挂账
python -c "
import io,yaml
d=yaml.safe_load(io.open('docs/ops/HANDOFF.yaml',encoding='utf-8').read())
for i in d['items']:
if i['status']!='done':
print('[%-14s] %-28s %s' % (i['status'], i['id'], i['owner']))
"不要先去 ls docs/ops/handover/ | head -30 —— 那是 518 个档,读不完且没重点。
3.2 做完一件事
- 技术改动 ⇒ 写代码注释(含「为什么」)+ commit message(含改动理由)。
- 更新
HANDOFF.yaml对应项的status/evidence/updated。 - 若状态是
pending-verify,到此为止 —— 不发档、不发回执。
3.3 需要对方做事
在 HANDOFF.yaml 里加一条 owner: 阿狸咪 的 item,把 verify_how 写成可跑的命令。
不要为此写一份 .md 档。
3.4 需要主人拍板
这才写 .md 档,且必须含:选项对比 + 明确推荐 + 理由。
文件名格式:YYYY-MM-DD_HHMM_小九给主人_<决策主题>.md
3.5 收尾(替代「回执」)
在 HANDOFF.yaml 改状态字段即完成握手。
若确需对方留意,在 item 里加 note_to: 阿狸咪 字段。
4. 判据陷阱
| 陷阱 | 正解 |
|---|---|
档里写「已修复」但其实是 代码改了 |
查 status 是否 done 且有 verified_at |
| 「对方没回」⇒ 写催办档 | 说明 owner/verify_how 没写清 ⇒ 修 item,不加档 |
| 「这次改动很重要,得写详细点」 | 重要 ⇒ 注释写详细。档的数量不因重要性增加 |
| 想用交接档做「工作记录/日志」 | 用 git log。交接档是决策与跨机协议载体,不是日志 |
5. 判据口诀
档 = 给人拍板的。
状态 = 给机器同步的(HANDOFF.yaml)。
细节 = 给改代码的人看的(注释)。
历史 = 给检索用的(commit message)。
四者不要混。混了就是今天的 518 个档。
6. 历史档处置(不删,只归档)
518 个旧档不删除(是审计证据链),但:
- 在
docs/ops/handover/README.md说明「本目录为历史归档,当前挂账见../HANDOFF.yaml」。 - 新增档一律进
docs/ops/handover/但数量受 §1.5 约束(只拍板类 + 协议类)。 - 需要查旧档时用
grep -l <关键词>精确定位,不要ls | head通读。
🔵 2026-09-21 实战教训(随本批一并入仓)
本节已随本批(commit 见
git log)一并入仓,双机本地副本与仓内逐字节一致。
本节内容全部来自 2026-09-21 午休窗一次真实上线的实战取证,非推测。
1. 🔴 「冲突」与「静默覆盖」是两个不同严重度的问题
设计并发防护时,最常见的错误是把它们混为一谈:
| 路径 | base 落后时的表现 | 严重度 |
|---|---|---|
| 本地 git push | non-fast-forward 报错(rejected) |
🟢 安全失败 —— 会喊 |
Git Data API(base_tree + force:false) |
不报错,自己的 blob 静默顶掉对方对同文件的改动 | 🔴 危险 —— 不会喊 |
⇒ 本仓双机实际主用 API 路径,故只防「merge 冲突」是不够的。
正解(协议 D 条):推单文件前重取该文件 blob sha;与读取时不一致
⇒ 不是 abort,而是基于新内容重放(账本类文件的写入语义是「追加/改自己项」,天然可重放)。
实战证据(2026-09-21 12:20 午休窗):一次 7 文件推送连续 2 轮 422 Update is not a fast forward,
第 3 轮成功 —— 取 tip 到 PATCH ref 之间被 cache-bot churn 顶掉。
若当时是「不重取 tip 直接 force」,那两次就是静默覆盖。
2. 🔴 单文件账本的「自洽性检测」永远检测不出回滚 ⇒ 必须有外部锚
这是把状态源收敛为单文件后新引入的盲区(不是原有问题的放大):
单文件场景:next_id_seq 回退 ⟺ 条目序号也一起回退 ⇒ 文件内部「看起来完全自洽」
多文件场景:某一份被旧版本覆盖,另外几分仍在 ⇒ 交叉比对能发现⇒ 必须有一个文件之外、只增不减的锚。本仓落地:docs/ops/HANDOFF.ledger.json
({max_next_id_seq, ids[]}),门禁断言 ids ⊇ ledger.ids + next_id_seq ≥ max_next_id_seq。
维护纪律:推账本时同一提交刷新 ledger;清理已完成项时同批更新 ids。
ledger 缺失 ⇒ 门禁放行并告警(守卫自身不得成为新的单点阻断源)。
3. 🔴🔴 改代码时,断言比代码更容易写错(本轮血训,5 处中 3 处属此类)
本轮我写错的断言/测试数量 > 写错的代码数量。逐项列出,都是有迷惑性的假阴性:
| 症状 | 真因 | 正确写法 |
|---|---|---|
| 「门禁没抓住未来时戳」 | 反测用例把新 updated 插在已有 key 之前 ⇒ YAML 同 key 后者胜,被原值覆盖 |
必须替换已有值,不插入 |
| 「某文件本地与线上不一致」 | len(str) 是字符数不是字节数(中文 UTF-8 3 字节) |
比 len(s.encode("utf-8")) 或直接比 bytes |
| 「7 个文件字节数全部虚高 38%」 | /git/blobs 用 Accept: application/vnd.github+json ⇒ 返回 JSON 包装(base64 + 双引号转义) |
必须 application/vnd.github.raw |
相减抛 TypeError(非返错值) |
datetime.now(timezone.utc) 是 aware,strptime 产出 naive |
.replace(tzinfo=None) 统一口径 |
| 门禁「挂载了却没执行」 | 被检查函数抛未捕获异常 ⇒ 前面 N 项已打印 ✅、第 N+1 项静默消失、exit code 非 0 | 离线单独调用该函数抓 traceback,别只看总输出 |
通则:① 反测(负向测试)必须单独验证,不能只看「正向绿了」;
② 断言里的「数量/长度/字节」必须写明单位口径;
③ 新挂的门禁,先离线单跑该项确认无异常,再看整体。
4. 🔴 一次性上线快照的清单会过期
本机定时快照(scheduledAt 的一次性自动化)在创建时刻固化了「要推哪些文件、基线多少」。
若其后又改了产物(本轮:创建后又写了 3 项裁决),快照会按旧清单推
⇒ 轻则丢掉新改动,重则与外部锚失配(ledger 与账本对不上,门禁红)。
纪律:快照触发前若产物已变更 ⇒ 暂停快照 → 手推 → 删除快照,
不要「让快照跑完再补推」(会产生两次 build + 中间态不一致)。
5. 「离线跑通」≠「CI 里生效」
门禁/脚本在本地跑绿,只证明它能跑;要证明「已挂进链路并生效」,
必须看该 commit 对应的 CI run,且看 step 级结论而非 run 级:
- 门禁 step
success且下游部署 step 未skipped⇒ 才说明门禁真的通过了(而不是被绕过)。 - ⚠️ 注意
continue-on-error: true的 step 在 API 里恒报 success,需靠「下游步是否 skipped」反推。
6. 🔴 被静默覆盖的对象,可能不是业务改动,而是防复发护栏
2026-09-21 批④ 实测:v8_health_check.py 的 _RETIRED_FILES / _LOW_FREQ_FILES 新增项,
由一个提交加入、40 分钟后被另一个提交抹掉,后续 4 个版本一路带病 18 小时;
期间推送侧(api_push_raw.py)护栏仍在 ⇒ 两侧口径分叉 18 小时且零红灯。
为何比「覆盖业务改动」更凶:业务改动下次提交或比对时可能被发现;
护栏丢了自己不会报错,直到事故复发才暴露 —— 而那时你早忘了它曾经存在。
纪律:
- 护栏类改动(白名单 / 退役集 / 阈值常量)必须双端同批登记,并在事后逐版本取证
(git log -p数该符号在各版本的命中数),不能只查「业务逻辑在不在」。 - 护栏应配在位自检(断言其存在且取值正确)并挂进门禁;否则任何一次读-改-写都可能再抹掉它。
7. 🔴 本协议如何被自动发现(2026-09-21 补 —— 此前零自动通道)
实测缺口(2026-09-21 19:0x,小九机,按远端 blob 取证):
| 可能的通道 | 实测结果 |
|---|---|
仓根 AGENTS.md / .codebuddy/ / CLAUDE.md / .cursorrules / copilot-instructions.md |
全部不存在 |
| 全仓检索 | 6476 个 blob 仅 1 处命中本协议 —— 即协议自身,无任何分发机制 |
README.md(根级唯一候选入口,899 B) |
只写 2026-09-10 的旧交接档规范;检索 handoff / HANDOFF.yaml / note_to = 0 |
本机 skill description 触发场景 |
只覆盖「主人问交接问题」⇒ 「动手改文件」不触发 |
| 双机 skill 目录同步机制 | 不存在 |
| 用户级记忆(本机,会话必注入) | 含「交接」9 处,但 HANDOFF / note_to = 0 ⇒ 是旧 HANDOVER 套路,不含本协议指针 |
⇒ 结论:「协议存在」≠「会话会读到」。 这正是 handoff-skill-inrepo 里阿狸咪留的
那个口子(「若小九认为仓内路径不够用,请给出具体场景如会话启动无法读仓」)所指的场景,
且比设想更靠前:问题不在「读不到」,而在「根本不会去读」。
落地(不违背 handoff-skill-inrepo 的 resolution —— 不 cp 第二副本):
改用自动注入型入口(WorkBuddy 官方支持项目根 AGENTS.md 与 .codebuddy/,会话启动即加载)。
| 文件 | 角色 |
|---|---|
AGENTS.md(仓根) |
入口索引:必读清单 + 速查 + 硬禁区 + 文件地图。不含协议正文 |
.codebuddy/CODEBUDDY.md |
指针(WorkBuddy 项目级记忆,自动加载) |
README.md |
人读入口,指向 AGENTS.md |
为什么这不违背「认仓内为唯一权威」:上面三份都不含协议正文,只回答「往哪读」
⇒ 权威仍只有本文件一份,不存在「哪份是权威」的歧义。
同一形态的教训(本协议自身的第 7 条铁律):
「有规则」与「规则会被执行」之间,必须有一条机器或平台保证的通路。
靠「人会去翻」= 靠自觉 = 已经被证明会失效(见 §0 病根:协议上线当天即被自己违反)。
8. 🔴 守卫「写对了」不等于「会被触发」—— 可达性盲区
2026-09-21 批⑥ 实测:[13/13] 交接状态源守卫 于当日 12:02 上线、且当时确有「CI 实测通过」的记录,
但当日 19:2x 复查发现 —— 它此后从未被触发过(除非碰巧同批改了别的文件)。
根因:v8_build_deploy.yml 的 push.paths 白名单不含 docs/ops/** 与入口文件。
只改 HANDOFF.yaml 的 push 不触发 build_deploy ⇒ 守卫根本没机会跑。
实测证据:commit ae2a7cdc7a40(改了 HANDOFF.yaml / AGENTS.md / 本文件 / README.md /.codebuddy/**)触发的 run 只有 1 条 —— pages build and deployment;v8 构建部署(云端ubuntu) 根本没跑。
为什么这是「双盲区」的第三次变形:
前两次是「闸门不覆盖某个产物」(09-11 FOUR_VOLUME_60M / 09-18 BACKTEST_TDX,见guard_double_blind.py 立项目的);这次是「闸门覆盖不到自己该检查的那次变更」——
守卫的判据完全正确、文件完全在位,只是永远不会被调用。
判据(新增,永久适用):
任何新增守卫 / 门禁上线时,必须同时回答一个问题:「谁会触发它?」
若它的检查对象(文件 / 目录)不在 workflow 的paths(或on:触发条件)内
⇒ 它等同不存在,哪怕代码写得再对、本地跑得再绿。
修法:push.paths 精确补 5 条 ——
- 'AGENTS.md'
- '.codebuddy/**'
- 'docs/ops/HANDOFF.yaml'
- 'docs/ops/HANDOFF.ledger.json'
- '.github/scripts/pre_deploy_audit.py'⚠️ 刻意不用 docs/ops/** —— 那会让每一次「改交接档」(双机高频)都触发一次全量构建,
属于用风暴换覆盖。
验收方式(不可省):推送后必须确认该 commit 真的触发了 build_deploy,
且门禁 step = success、下游部署 step 未被 skipped。
「本地跑绿」≠「CI 里跑过」≠「在 CI 里被触发过」—— 这是三件不同的事(与教训 5 同源)。
顺带记录(本批第二个算法事实):ledger.id_content_hashes 中**「末项」的哈希会随后续追加而变** ——
因为官方块边界是「本项 - id: 起 → 下一个 - id: 前」,而末项直到文件末。
⇒ 追加 item 时,「原末项」的哈希必然变化(与 all-generic-redline-cap-cliff 的漂移同源)。
同族教训(本协议自身在批⑤ 踩到,批⑥ 修正):
批⑤ 新增 §7 时用的是「把 锚点 替换为 新内容 + 锚点」的写法,本意是「追加到文件末尾」,
但那个锚点其实是 §6 的标题 ⇒ §7 被插到了 §6 之前,编号与顺序错位。
而当时那批的验收断言只查了「§7 标题存在」「§6 标题只剩 1 处」,没有查两者的先后顺序 ⇒ 漏过;
直到批⑥ 想追加 §8 时才发现。
纪律:插入类改动的断言必须落到「位置关系」上(
find(a) < find(b)),
不能只查「存在性」。存在性断言对「插错位置」零抵抗 ——
这与本协议 §3 已记的「断言比代码更容易写错」是同一家族,但更隐蔽:
代码是对的,只是验收标准不够强。
补充(批⑧ 实测,2026-09-21 19:3x–19:5x)——
可达性的「第四个形态」:文件确实到位,但加载路径本身不通**。**
前三个形态都默认了一个前提 ——「会话总能看到仓根的文件」。批⑧ 实测把这个前提也推翻了:
官方口径(CodeBuddy 文档
/cli/memory原文):记忆文件
「在启动 CodeBuddy Code 时自动加载到上下文中」,且项目级是
「从当前工作目录向上递归加载所有CODEBUDDY.md和AGENTS.md」;/cli/codebuddy-dir补充:「也可将记忆文件放在根目录的AGENTS.md(不在.codebuddy内),
两种位置等效」。
⇒ 「自动加载」认的是「本地工作树」,不是「远端仓库」。
(旁证:本机E:\workbuddy\resources\app.asar.unpacked\cli\product.json中AGENTS.md命中 7 次、CODEBUDDY.md命中 13 次,机制确有实现。)实测缺口:
AGENTS.md/.codebuddy/CODEBUDDY.md早在批⑤ 就已上线,
但双机本地工作树都没有 —— 因为本仓常用 API 隔离索引推送(按设计绕过工作树)。
⇒ 若会话工作目录指向仓根,实际加载到 0 ⇒ 入口形同不存在。
修法(已入库,与推仓同批执行):推完入口文件必须同步到本地工作树 ——
python C:/Users/Administrator/.workbuddy/scripts/v8_handoff_edit_kit.py ai-entry-sync它取三份真身(AGENTS.md / .codebuddy/CODEBUDDY.md / 本文件)逐字节落到E:\qs_workspaces\quant-scanner-v8 + E:\v8data\qs8-tmp + 本机 skill 副本~/.workbuddy/skills/v8-handoff-gateway/。
验收:重跑一次,输出应全部为「= 已一致」;出现「↑ 已更新」= 上次漏同步。
判据只认 md5 与线上 blob 相同,不看时间戳、不看 commit sha。
本机用户级兜底(覆盖「会话工作区不在 v8 仓根」的场景):~/.codebuddy/CODEBUDDY.md + ~/.workbuddy/CODEBUDDY.md(同一份 1020 B 指针)——
只做「把会话导到项目入口文件」,不重复任何项目内规则。
(~/.workbuddy/MEMORY.md 是 WorkBuddy 的用户级记忆通道,所有项目注入,同样已写入本入口指引。)
⚠️ 本门自身的可达性受限 —— 如实登记:docs/ops/skills/**、SECURITY_RULES.md、TIME_ORDER.md 不在 push.paths 内
⇒ 单独修改本文件不会触发 build_deploy ⇒ [14/14] 对这几项的检查属于
「随其他高频变更顺带执行」,不是即时。
这是上面「刻意不用 docs/ops/**」那条取舍的已知代价,写在这里以免后人误以为有即时门禁。