SOP:HANDOFF 机制空档补齐(SessionStart 未命中提示 + lustre_ins 首份快照)(2026-09-23)¶
交付物目录
handoff-bootstrap-20260923/;实现体apply_changes.py(幂等),核验verify_changes.py(只读)。 第 5 章列出与实现等价的命令序列,供阅读与手工复现,以实现体为准。
1. 背景¶
2026-09-23 用户在 /home/rain/Documents/lustre_ins 下发现:连续数轮会话都没有 HANDOFF 注入,「是不是失效了」。
排查结论是机制没坏,卡在死循环起点:没有 docs/HANDOFF.md → Hook 无内容可注入 → 下一个会话也拿不到「本项目该有交接快照」的提示 → 更不会有人去建第一个。
证据:sessionstart-hook/state/session-start.log 中工作区为 lustre_ins 的四次记录全部为 handoff=- bytes=0(14:06:58、22:15:03、23:26:23、23:28:00),而同期 token_proxy 自身每次都是 handoff=.../token_proxy/docs/HANDOFF.md bytes≈9100。
根因分两层:
| 层 | 内容 |
|---|---|
| 直接原因 | lustre_ins 的 docs/ 下只有 RUNBOOK.md / SOP.md / troubleshoot.md,从未有过 HANDOFF.md;脚本按设计「找不到即不注入」(2026-09-23 第二次修复特意去掉跨项目兜底,避免把 token_proxy 的快照误注入别的项目——那条兜底只会注入错误内容,不构成记忆) |
| 机制原因 | writing-handoff 技能只规定「怎么写 HANDOFF」,没有「新项目何时该建第一个」的触发点;Hook 未命中时完全静默,失效不可见 |
2. 目标¶
- 未命中不再静默:
SessionStart在找不到 HANDOFF 时注入一行提示,给下一个会话建立交接快照的锚点; - 命中路径零回归:命中
docs/HANDOFF.md的项目,注入内容与行为与改动前完全一致; - lustre_ins 有第一份
docs/HANDOFF.md,且能被 Hook 正常注入,且 ≤ 10 KB(writing-handoff硬上限); - 用户规则中已失效的回归口径(
tests_guard_cases.py,2026-09-23 误删且经决定不重建)改指真实可用的用例表。
3. 前置条件¶
| 项 | 值 |
|---|---|
| 主机 | 192.168.31.250(用户 rain) |
| 工程根 | /home/rain/Documents/services/token_proxy |
| 解释器 | ./venv/bin/python(3.12);脚本无第三方依赖,系统 python3 亦可 |
| 写权限 | ~/.trae-cn/user_rules/ 与 /home/rain/Documents/lustre_ins/docs/(当前用户即可,无需 sudo) |
| 不需要 | 不改 hooks.json → 无需重启 Trae;不重启 token-proxy 代理;无在跑作业 |
4. 影响范围¶
单机变更。改动 2 个文件、新增 3 个文件:
| 动作 | 对象 |
|---|---|
| 覆盖(有备份) | sessionstart-hook/session-start-inject.py、~/.trae-cn/user_rules/rule-skill-index.md |
| 新增 | lustre_ins/docs/HANDOFF.md、handoff-bootstrap-20260923/{SOP.md,apply_changes.py,verify_changes.py,backup/} |
- 无需停服,无窗口期要求;
token-proxy服务、logs/*.jsonl、在跑作业均不受影响。 - 影响面:
SessionStart是全局 Hook,改动对所有项目生效——未命中 Handoff 的项目从现在起会多看到一行提示(每会话一次,约 90 字节)。 - 规则改动需开新会话才生效(
user_rules的既有约定)。
5. 分步操作¶
# 实现体:handoff-bootstrap-20260923/apply_changes.py(以它为准;下列序列供阅读与手工复现)
# 高危动作:C1 覆盖 session-start-inject.py、C2 覆盖 rule-skill-index.md
# ——覆盖前自动备份到 <本目录>/backup/<原绝对路径去首斜杠>/<原名>.bak.<时间戳>
# 1) 预演:预检(文件存在性 + 锚点唯一性)与计划,不写任何文件
./venv/bin/python handoff-bootstrap-20260923/apply_changes.py
# 预期:两行 [预演] 将修改:…(分别 3 处 / 1 处锚点替换)
# 末行「预演结束:待变更 2 项,已达标 0 项(预演不写文件)」
# 2) 执行:备份 → 锚点替换 → new 段落校验 → 语法检查 → 落盘 → 登记台账
./venv/bin/python handoff-bootstrap-20260923/apply_changes.py --apply
# 预期:两行 [高危] 覆盖类动作保护性备份…
# 两行「已修改:…(新 SHA256 …)」+「台账已登记 2 条」
# 末行「执行结束:变更 2 项,跳过 0 项」
# 3) 幂等复跑:第二次应零变更、全跳过
./venv/bin/python handoff-bootstrap-20260923/apply_changes.py --apply
# 预期:两行「跳过(目标已是本次版本)」+「执行结束:变更 0 项,跳过 2 项」
# 4) 核验(只读,会向 state/session-start.log 追加测试留痕)
./venv/bin/python handoff-bootstrap-20260923/verify_changes.py
# 预期:结果:11/11 通过
# 5) 回滚(仅在需要撤销时执行)
./venv/bin/python handoff-bootstrap-20260923/apply_changes.py --revert
# 预期:「[高危] 用备份覆盖:…」两行 +「回滚结束:已恢复 2 项,跳过 0 项」
锚点替换是「要求锚点恰好出现 1 次,否则报错退出」的全或无设计:锚点漂移时不会产生半改状态。调试期间两次中止(锚点前缀取错、插入型替换的校验误判)均止于写盘之前,文件未改动,留档见 backup/README.md 末两行。
6. 验证¶
| # | 用例 | 命令要点 | 预期 | 实测(2026-09-23 23:55) |
|---|---|---|---|---|
| V1 | 脚本可编译+常量就位 | compile() + 特征串检查 |
通过 | 通过(含 MISSING_HANDOFF_NOTICE = ( 与修复记录「(六)」) |
| V2 | 未命中给出提示 | 无 HANDOFF 的临时工作区喂 {cwd} |
含提示、退出码 0、stderr 空 | 通过(rc=0、stderr 空、输出 97 字节) |
| V3 | 命中 token_proxy | 以工程根为工作区 | 注入正文且不含提示 | 通过(5412 字节) |
| V4 | 命中 lustre_ins | 以项目根为工作区 | 注入本项目 HANDOFF 且不含提示 | 通过(3625 字节,含 edafs) |
| V5 | 规则口径已改 | 读 rule-skill-index.md |
已指第 6 章用例表、不再提旧用例文件 | 通过 |
| V6 | 新快照合规 | stat 大小 |
存在且 ≤ 10240 字节 | 通过(6011 字节) |
| V7 | 回滚可用 | 备份存在 + 台账登记 | 两份目标均可回滚 | 通过(脚本 3 份 / 规则 1 份,全部在册) |
| V8 | 接线齐全 | 正则提取 hooks.json 内 $HOME/*.py |
三条路径均存在 | 通过 |
V2/V3/V4 的判据同时含「退出码 0」与「stderr 为空」两条红线(非 0 退出会把 stderr 塞进模型上下文)。
7. 回滚¶
回滚 = 用本次备份覆盖回原文件,命令见第 5 章第 5 步。触发条件(任一达标即回滚):
verify_changes.py的 V1/V3(命中回归)或 V8(接线)失败;- 新会话实测
SessionStart无中文指令注入(说明脚本整体失效); - 未命中提示文案被证实造成误解或干扰。
备份位置与台账:~~handoff-bootstrap-20260923/backup/README.md(4 条,含 2 条调试期等价副本)~~ —— 该 backup 目录已于 2026-10-02 按台账判据「验证通过后」删除(见 backlog-20261002/SOP.md 第 5.1 节);apply_changes.py --revert 依赖的同目录备份已不存在,--revert 路径失效,如需回滚须先按第 5 章第 2 步手工生成快照。
lustre_ins/docs/HANDOFF.md 为纯新增,回滚即删除该文件;它不参与 --revert。
回滚后验证:重跑 verify_changes.py,应回到「V2 失败、V3 通过」的变更前形态;再跑一次 apply_changes.py 预演确认可重新落地。
8. 风险说明¶
| 风险 | 说明与缓解 |
|---|---|
改坏注入脚本 → SessionStart 整体静默失效(比现状更糟) |
只加分支、不动命中路径;顶层 try/except → exit 0;改前自动备份;落盘前做语法检查 + new 段落校验;V1~V4 覆盖正反例 |
| 每会话多注入约 90 字节固定成本 | 提示仅两行,且只在未命中时出现;命中项目零新增 |
| 非运维项目也会看到这条提示 | 未加开关(YAGNI)。若日后觉得干扰,可在脚本内加常量开关,属小改 |
| 提示被误读为「项目有问题」 | 文案为条件式(「若本轮形成实质结论或完成变更」),不强制动作 |
| 全局规则改动影响所有项目 | 仅改一行口径、不涉语义;V5 保证不残留旧表述;改完开新会话生效 |
附录:交付物清单¶
| 文件 | 说明 |
|---|---|
handoff-bootstrap-20260923/SOP.md |
本文件(八章 + 附录) |
handoff-bootstrap-20260923/apply_changes.py |
幂等实现体:预检 → 备份 → 锚点替换 → 校验 → 落盘 → 台账;含 --revert |
handoff-bootstrap-20260923/verify_changes.py |
只读核验(V1~V8) |
handoff-bootstrap-20260923/backup/README.md |
备份台账(4 条)+ 测试留痕登记 |
handoff-bootstrap-20260923/logs/apply.log |
实现体执行日志 |
/home/rain/Documents/lustre_ins/docs/HANDOFF.md |
新建的项目首份交接快照(6011 字节) |
被改文件:sessionstart-hook/session-start-inject.py、~/.trae-cn/user_rules/rule-skill-index.md(改动要点已记入 docs/SOP.md 第四十七节)。
来源:
/home/rain/Documents/services/token_proxy/handoff-bootstrap-20260923/SOP.md| 最后更新:2026-10-02 15:19(由build/sync.py同步,请勿在站点侧修改)