Files
dsh_ai1net_server/.workbuddy/memory/2026-10-09.md
T
admin c1b5e4d966 chore(工作区): 全量入库 + 补齐 .gitignore(以工作区为准)
- 变更规模:新增 514 / 修改 62 / 重命名 155 / 删除 4(归档重组与文档轮次)
- .gitignore 修:`归档/**/db-cwd归一-备份-*/` —— 原规则写绝对层级(归档/db-cwd归一-…),
  目录搬进 归档/配置与备份/ 后**静默失效**,43 MB 的 DB 备份又变成未跟踪
- .gitignore 补:嵌套 git 内部数据(归档/内嵌git-20261008/、归档/skills-git-旧线-20261007/dotgit-原样移出/)
- .gitignore 补:运行态与部署副本(.workbuddy/collab/、.workbuddy/tools/、.workbuddy/.load-pending、.workbuddy/tmp-*)
- .gitignore 补:备份件(*.bak-*)
- 未跟踪文件从 2190 降到 890(其余为 归档/ 归档件与 .workbuddy/memory/ 知识文件,按口径入库)
2026-10-10 23:13:22 +08:00

24 KiB
Raw Blame History

2026-10-09 工作日志

R9 唯一例外落地:授权检查会话自解除孤儿锁(用户令)

用户原话(当日):

「如果 工作区 项目机制 对应的会话异常 导致未解锁的情况下, 可以授权 检查会话自解除孤儿锁」

起因(实测病例):contentm_agent 一把域锁 contentm_agent/执行会话 的持有者会话 a257d30f 被 terminated(起跑 00:48、末次活动 00:53)⇒ 锁没人释放 ⇒ 目标第 4 步 blocked。 R9「AI 一律不得删锁 / 不得接管」只有行为约定、没有任何执行面,理由写的是 「平台无心跳机制、AI 没有任何判据能确认对方已死」⇒ 检查会话连开 11 棒(22–31,约 5 小时) 每棒都撞同一面墙、零派活,每 30 min 一棒(CHECK_COOLDOWN_S)。

判据(本次补上的"判据",直接推翻上面那句理由):宿主库 workbuddy.db → sessions.status

  • last_activity_at(只读 SQL):
持有者状态 处置
working 一律不动(R9 原样)
terminated / error / archived 可解除(异常退出/已归档,不会再来释放)
completed 且静默 ≥ 30 min 可解除(已不在执行;时间闸只为不掐掉"刚收工、随时续聊"的会话)
查不到 sid / 库读不到 / OWNER 没记 sid 一律不动(fail-closed)

实测两个存量孤儿:e17c96e0(completed,挂 10 天,ai1net-dsh-anywhere)、 a257d30f(terminated,contentm_agent)——两种状态都会留孤儿锁,故 completed 档必须收。

落地(6 处,单一执行面):

  1. 🆕 session-mechanism/scripts/lock/orphan-lock.py —— 判定 + 解除的唯一执行面; --dry-run 先看;写 .locks/.orphan-release.log 流水。⛔ 别自己写 rm -rf。
  2. collabd.py 检查会话 prompt(结果检查 + 目标检查两条都加了):遇「域锁被占」 先跑 orphan-lock.py 判死活,判"已退出"⇒ 授权自解除后继续派活;判"活着/判不出"⇒ 照旧停手。 同时补上「写明阻碍」的出口:台账 --report … --state blocked --reason + NEED-USER.md 里明确喊「需用户介入」(原先只说"写明阻碍",没说写哪儿 = 用户看不到)。 ⚠️ prompt 注入用「标记 + 事后 replace」,⛔ 不往 %s 实参元组加位(那处位置对齐,错位即 fatal 静默)。
  3. selftest.py 新增用例(16 项,含变异对照):working 的锁一把都不许动、 宿主库读不到 ⇒ 一把都不动 —— 只验"terminated 能解除"的野实现同样会全绿,故反向断言非有不可。 ⇒ 全套 PASS 107 / FAIL 0(原 106)。
  4. R9 条文同步(原先只有"绝对禁止",不改就是两份打架): 工作区 CODEBUDDY.md §3 R9 行(定义处)、文档库 dsh-server-docs/CODEBUDDY.md、 技能 references/rules.md、SKILL.md;另两份 handoff-guard.sh(技能包 + 文档库 07-scripts/) 的"抢域锁失败"提示里加指针。

验证(进程级):

  • orphan-lock.py --dry-run 活体对照 —— cc07347c(working)判 不动 ✅ / e17c96e0 判可解除 ✅。
  • 现场自愈:孤儿锁 06:26 被释放后,第 32 棒检查会话真派了活 ⇒ 06:33 新执行棒 cc07347c「重做界面补闸门与上报(第二棒)」持域锁 working ⇒ 目标第 4 步重新推进。
  • 分发 + 重启(「部署≠生效」):deploy_code.py 三区,collabd.py md5=58e17cf0; collabctl.py off/on 重启 ai1net-dsh-server(pid 73304→37040)、vibe-product(26992→54096), 心跳 <90 s、看板 20099 仍在监听。

遗留(未做,需另起)

  • 🔴 空转闸缺口仍在:queue_pending_of() / queue_pending() 的四态口径里 blocked 与 pending/running 同样计入"未完成" ⇒ 队列只剩一条 blocked 时,闸③「sessions-ended 必须队列非空」恒真 ⇒ 唯一节流只有 CHECK_COOLDOWN_S=30 min ⇒ 每 30 min 空转一棒、无上限。 本次只是把"能自解的那类阻碍"消掉了;其它类型的阻碍照样会一直空转。 拟补法(未实施):队列无 pending/running 且只剩 blocked ⇒ 冷却从 30 min 抬到 2 h(含兜底复检)。
  • 🔴 contentm_agent 总闸 = off,常驻却仍在跑(pid 40016,08-08 08:21 设 off、08:45 起进程)—— 违反「只有 collabctl 能起进程」的总闸不变式。⇒ 该区未重启,检查会话仍是旧文案。 未擅自改(那是它的配置)。要切 on 并重启:collabctl.py on --ws contentm_agent。
  • ⚠️ handoff-guard.sh 有两份且已漂移 18 行(技能包 = 新,术语已收敛"任务会话"+roots.env 外置; 文档库 07-scripts/ = 旧)。会话两条路径都在用(转录取证:07-scripts/… 32 次、技能包路径 20 次)。
  • ⚠️ 未推送:技能仓(E:/ProgramData/.workbuddy/skills)与文档库均有本次未提交改动。 ⚠️ 当时另有一条会话 a80f300d「复制技能并推送」在跑,避免抢 git add 竞争 ⇒ 本轮不推。

补:空转缺口改走「既有规则」—— 遇阻碍 ⇒ 置「阻碍」⇒ 机制暂停(用户纠正)

用户纠正原话:> 「其他类型 阻碍 遇到阻碍修改目标状态 暂停机制 让用户决策 ,不是有这个规则吗」

🔴 我上一轮的结论被纠正:我提"降频冷却(30 min→2 h)"是绕开既有规则,⛔ 已作废。 既有规则(用户口径第①/②条:完成就收工;阻碍也不用开)确实存在,问题只是没接上。

真因(三条腿互相锁死,缺一即空转):

  1. 闸③「sessions-ended 队列必须非空」对一条 blocked 恒真 ⇒ 唯一节流只剩 CHECK_COOLDOWN_S。
  2. 闸① 2026-10-04 收窄时把 sessions-ended 整条腿从"目标状态闸"摘了出去 ⇒ 就算置了「阻碍」也照样建。
  3. CHECK_PROMPT_RESULT 第 3 条明写「⛔ 本轮你没有「改目标状态」的出口」⇒ 看得见的没权; 而有权改状态的「目标检查」要队列为空才触发 ⇒ 队列里躺着 blocked 时永不触发 ⇒ 有权的看不见。 ⇒ 结果:每 30 分钟空转一棒、连开 11 棒、约 5 小时、零派活。

落地(本轮):

  • collabd.py CHECK_PROMPT_RESULT 第 3 条改写:结果检查开一个出口(只此一个)—— collabd.py --set-life 阻碍 --by "<会话名>" --check-why "<一句现状>"; ⚠️ 「为什么」旗标是 --check-why(⛔ 不是 --why,那是 --retire 的,写错静默忽略); 要求回读 goal.json;写明恢复=主会话跑 --set-life 进行中。
  • collabd.py 闸① 改两档:queue-empty 非「进行中」⇒ 不建(原样);sessions-ended 只拦「阻碍」, 「已完成」仍放行(⛔ 别把 2026-10-04 修的病搬回来)。docstring「五道闸」→「六道闸」。
  • prompt 注入用第二个标记 @@COLLABD_PY@@(同样事后 replace,⛔ 不动位置对齐的实参元组)。
  • selftest.py:新增用例(6 项含变异对照:阻碍⇒不建/已完成⇒仍要建); 改了旧断言——t_check_prompt_cmds_exist 的「结果检查 ⛔ 一个 --set-life 都不许有」改为新口径; t_two_kinds_of_check_agent 里那两行删除(同一判据写两处 ⇒ 改一处漏一处,本轮实测就是这么报红的), 旗标级校验只保留一处。⇒ 全套 PASS 108 / FAIL 0。
  • 规则文本:references/rules.md 新增 §10「遇阻碍 ⇒ 改目标状态「阻碍」⇒ 机制暂停 ⇒ 让用户决策」 (此前这条口径只在代码注释里,规则文件一个字没有 —— 这也是它没生效的原因之一)。

部署与验证:deploy_code.py 三区,collabd.py md5=5906584e; collabctl off/on 重启 ai1net-dsh-server(74244)/vibe-product(52452); contentm_agent 被跨区自愈拉起(40016→7388)⇒ 三区进程全部跑上新代码(未动它的总闸)。 判据:启动器 supervise-launch.py 只跑本区副本 <ws>/.workbuddy/collab/collabd.py,该副本 md5 == 源。

待拍板(挂起)

  • 🔴 contentm_agent 总闸 = off,常驻却仍在跑(且被跨区自愈反复拉起)—— 违反「只有 collabctl 能起进程」的总闸不变式;off 状态下任何 ensure 都可能把它全停。
  • ⚠️ selftest.py 里 t_check_cooldown_no_pileup 被定义了两次(同名,后者覆盖前者)⇒ 实际只跑一份。
  • ⚠️ 未推送:技能仓与文档库均有本次改动未提交。

提交(用户令:「确认修改无误后,提交仓库(知道应该提交哪里吗)」)

技能仓 = E:/ProgramData/.workbuddy/skills 本身,远端唯一一个: [email protected]:admin/workbuddy_skills.git(master,core.autocrlf=true)⇒ 已提交并推送。

  • 提交 c76f5be:7 份、+532/−29。只 git add 自己改的(⛔ 不用 -A): SKILL.md、references/{manifest,rules}.md、scripts/{collabd,selftest}.py、 scripts/lock/handoff-guard.sh、新增 scripts/lock/orphan-lock.py。
  • 推送 c36a3f4..c76f5be master -> master;本地 == origin/master,工作区未提交 0 个。
  • 提交前按「改完包必跑」跑 install.py --manifest(77 份、语法失败 0),新文件登记在 manifest 第 184 行。
  • 入库前扫凭据:命中 token/PASSWORD 全是 CODEBUDDY_GATEWAY_PASSWORD 这类变量名与散文 (design-tokens.css、「零 token」)⇒ 无明文;改动集里也没有凭据类文件名。
  • 一致性判据:仓库存档 == 工作副本 == 三区运行副本,四处 md5 全等 5906584e…。

另两处改动不在技能仓里(本轮未提交,就地保留):

落点 所属仓 远端现状
工作区 CODEBUDDY.md E:/ProgramData/AIProject/ai1net-dsh-server(511 存量改动) [email protected]:maogeigei/dsh_shenxian_workspace.git ⇒ SSH 主机密钥已变更、连不上
文档库 CODEBUDDY.md + 07-scripts/handoff-guard.sh D:/github/dsh_shenxian(174 存量改动;dsh-server-docs 是它的子目录,本身不是仓) origin 双推:work.alotbuy.com(连不上)+ https://cnb.cool/maogeigei/dsh-shenxian.git(实测可达)

⚠️ 新增两条环境事实:① work.alotbuy.com 的 SSH 主机密钥已变更(REMOTE HOST IDENTIFICATION HAS CHANGED)⇒ 凡指向该主机的远端一律推不出去(两个仓都中);② 文档库仓在 D:/github/dsh_shenxian,⛔ 不是 dsh-server-docs 自己。

用户裁定(同日):「不用处理」 ⇒ 文档库那两处(CODEBUDDY.md R9 例外、07-scripts/handoff-guard.sh 提示指针) 不提交、不推,就地保留。⚠️ 后果已知并接受:文档库那份 R9 条文仍是「绝对禁止」,与已上线的机制口径不一致 ⇒ ⛔ 将来别再为这件事重新提问;哪天要动文档库时顺手带上即可。

用户追问:「同步仓库不是应该遵循忽略的规则吗,谁让把所有技能都提交仓库的」

核实结论(三条,都有读数):

  1. 不是我这轮干的。c76f5be 的 --stat 只有 7 个文件、全在 session-mechanism/ 下(+532/−29); 推送输出 c36a3f4..c76f5be ⇒ 远端只前进 1 个提交。
  2. 忽略规则在生效、且 0 项违规:git status --ignored 显示 13 项被正确挡在仓外 (session-mechanism/install.log、session-mechanism/logs/、browser-harness/.venv/、 browser-harness/uv.lock、*.egg-info/、两处 __pycache__/、4 个宿主迁移标记 JSON、 browser-harness/.browser-harness-dev/);git ls-files -i -c --exclude-standard = 空 (=已跟踪文件里没有被忽略规则本应挡住的)。唯一像凭据的 browser-harness/.env.example 是示例、无密钥。
  3. 是 10-08 那条提交 e03465c 把技能全加进来的(提交信息写「按用户令提交:把此前未纳管的 9 个技能目录一并入库」)—— 不是本线。仓库演化:19101ac(10-05) 只收 session-mechanism → 777f7fe(10-07) 改推五个 → 9721876(10-07) 加 product-planning/oil-ui-pro → e03465c(10-08) 再加 9 个 ⇒ 现为 15 个目录 / 4945 个文件。 ⚠️ 用户当时口头点名的是 5 个(browser-harness/humanizer/humanizer-zh/product-planning/session-mechanism)。 ⚠️ 体积大头=product-planning 的 open-design 素材(2192 个 html、939 个 json、若干 1.4–2.0 MB 的 png)。
  4. ⚠️ 别把"提交信息自称按用户令"当作用户原话(本轮教训):要引用口径就引原话,认不下来就说不确定。

⇒ 待用户拍板:要不要把仓库从 15 个目录收回到点名的那几个(撤销当前版本 / 保持 / 重写历史三档, 倾向只撤当前版本——可逆、不动历史)。⛔ 未获指示前不动仓库。

用户裁定「A 肯定要收回」⇒ 已收回(提交 0204001)

做法:git rm -r --cached 撤出 2026-10-08 e03465c 一次并入的 9 个目录 (AI HOT、draw-ui、dsh-diagnose、dsh-knowledge、dsh-local-env、dsh-opensource-release、 dsh-workflow、oil-motion、skills-security-check)—— 203 个文件,本地一个都没删 (撤销前后本地磁盘都是 5711 个文件,这是本轮的硬判据)。

🔴 根因修法(对应「不是应该遵循忽略的规则吗」):.gitignore 由黑名单改白名单 —— 根下 /* 一律挡掉,再逐个放行 6 个技能 + .gitignore + 改包纪律.md。 为什么非改不可:黑名单只列"已知不要的",新冒出来的技能目录会被任何一次 git add -A 顺手捎进仓库 —— 2026-10-08 那 9 个就是这么进来的。白名单从机制上堵死这条路。 ✅ 验证:git check-ignore 对撤出的 9 个全部命中(⛔ 不会被 add),对保留的 6 个全部不命中。

保留范围(6 个):browser-harness、humanizer、humanizer-zh、product-planning、session-mechanism, 外加 oil-ui-pro —— 它是 product-planning 第③段的功能依赖,撤掉两边都会留悬空引用。

读数:跟踪文件 4945 → 4742(−203,与撤出数逐字吻合);顶层条目 7(6 技能 + .gitignore + 改包纪律.md); HEAD == origin/master == 0204001,未提交 0;推送 c76f5be..0204001。本地磁盘仍 5711。

⚠️ 已知代价(本轮不处理):撤出的目录仍留在历史里 ⇒ 新克隆体积不变(要缩得重写历史,用户选的就是不动历史); session-mechanism 里几处「⇒ 技能 X」的散文指针会指向不在仓的技能(不影响运行)。 ⚠️ 补记:contentm_agent 总闸仍是 off 而常驻仍在跑(pid 7388→39204,被跨区自愈反复拉起)——仍未裁定。

「别的电脑在那个技能夹里取仓,会不会动到已有文件」—— 已实测(git 2.55)

实验脚本:tmp/probes/_clone_test.sh、_clone_test2.sh(⛔ 全程在临时目录,不碰真仓)。

① 默认行为=不动已有文件(fail-closed):在已有 zz-other-skill/、note-local.txt、 session-mechanism/roots.env 的目录里 git checkout master(不带 -f)⇒ git 直接中止: error: The following untracked working tree files would be overwritten by checkout: … Aborting,rc=1。 ⇒ 已有文件一个都没变;代价是内容也拿不到。

② 带 -f:zz-other-skill/、note-local.txt 仍在(未跟踪文件不删); 但 session-mechanism/roots.env 被仓库版本覆盖(本机路径丢了)⚠️ —— 因为它是已入库的文件。

③ 按需取(推荐做法,实测 0 改动):

git init
git remote add origin <仓地址>
git config core.sparseCheckout true
printf '%s\n' product-planning/ humanizer/ > .git/info/sparse-checkout   # ← 只点名你要的
git fetch origin master
git checkout master

⇒ 实测:已有文件 0 改动、roots.env 未被碰、只写入点名的两个目录。 ⚠️ 但它取不到你已有的同名技能(git 会因同名文件中止)——那种场景得先把旧的挪开。

④ 白名单的连带收益(在别的电脑上):git status 保持干净 ⇒ 那台机器自己别的技能 既不会被误当作"待提交",也不会被 git add -A 捎进仓库。

⑤ 🔴 唯一的真例外:session-mechanism/roots.env(本机路径契约)与 session-mechanism/references/manifest.md(本机 md5 清单)都是 install.py 本机生成的、 却入了库(.gitignore 里还专门写了"刻意入库")。⇒ 整目录取会把别人的路径换成我们的。 修法(已列待拍板,未动):从库里撤出这两份(改入库 *.example / 由本机生成), ⛔ 本地文件保留(git rm --cached)。

追加:用户澄清语义 ⇒ 「同名的该覆盖就覆盖,不要影响其他文件」(已实测,_clone_test3.sh)

结论:这正是 git checkout -f 的原生语义,不需要改仓库。 做法:git init → git remote add origin <地址> → git fetch origin master → git checkout -f -b master FETCH_HEAD。 ⚠️ 关键就是那个 -f:不带它时 git 会因同名文件直接中止(_clone_test.sh 实测 rc=1、什么都取不到)。

实测读数(6 个预制文件,三组对照):

  • 同名 ⇒ 被覆盖:product-planning/SKILL.md、session-mechanism/roots.env 都换成了仓库版本 ✅
  • 本机独有 ⇒ 原封不动:根目录文件、别的技能目录、以及同一技能目录里的本机独有文件 (session-mechanism/我的便签.md、product-planning/本机旧文档.md)全在 ✅
  • 本机独有技能目录(zz-other-skill/)完整保留 ✅

⚠️ 该模式的固有代价(由"checkout 从不删未跟踪文件"直接推得,同一轮实测坐实): 仓库里删过的旧文件不会从本机自动清掉 ⇒ 会新旧共存,要清得自己删。 ⇒ 这也解释了为什么之前撤出 9 个技能目录后,别的机器上那 9 个不会被清走(它们只是不再被跟踪)。

用户令:「那两份 不说具体 我怎么知道,看看会话提问的规则是否有缺陷」⇒ 审计并修

先答:那两份 = session-mechanism/roots.env(路径契约)+ session-mechanism/references/manifest.md(包内清单)。

审计结论:有缺陷,而且三条。

  1. 🔴🔴 判据用错了维度(主因):旧措辞「⛔ 提问正文不许出现的:包名 · 环境变量 · 文件路径 · commit/sha · 表名/字段名 · 类名/函数名」——按"词性"一刀切;而同一段末尾又写「正文只留 能决定下一步的内容」⇒ 两句自相矛盾。真遇到"要决定的对象本身就是两份文件"时, AI(我)按前半句执行、把文件名删了 ⇒ 待拍板项不可答。
  2. 🔴 同一判据散在 6 处、措辞各不同(违反本项目自己的「禁重复判定标准」): CODEBUDDY.md §1(4 类)/rules.md(3 类,少"类名")/作业总规矩 §1.7(6 类)/ 02-功能优先协作协议(结论层零技术标识)/素材库-U-用户决策(零技术标识)/注入块(0 类,只有"不用指代")。
  3. 🔴🔴 "每轮注入的那份"与"包内那份"是两份(本轮实测坐实,最隐蔽): reply-style-guard.py::_core() 是三级回退 —— ① 本工作区 CODEBUDDY.md 里 <!-- REPLY-CORE:BEGIN…END --> 之间那段(每轮注入的就是它)② 包内 references/03-回复排版-核心块.md ③ 外部技能那份。⇒ 我先只改了 ② ⇒ 注入的仍是旧条文, 而表面上"文件都改了、自测全绿",查不出来。

修法(判据改成按功能判,一句话):

🔑 少了这个标识,用户还能不能决定下一步? 不能 ⇒ 必须写进正文;能 ⇒ 必须删/下沉「技术附录」。

  • ✅ 决定对象必须点名(要撤哪份文件 / 改哪个技能 / 推哪个仓 / 动哪台机器);
  • ⛔ 只服务实现的细节下沉附录(函数名/变量名/行号/sha/表名字段名/内部编号)。

改动(6 个文件,全部改完并验证): AIProject/ai1net-dsh-server/CODEBUDDY.md(§1 那条 + 标记块内)/ references/03-回复排版-核心块.md/references/rules.md/references/02-功能优先协作协议.md(2 处)/ references/作业规矩/00-作业总规矩…md §1.7/scripts/selftest.py。 新增防漂移自测:t_reply_core_same_source —— 工作区块 与 包内块 实质内容必须逐字一致 (⛔ 只允许首行注释不同)⇒ 从机制上堵住"改一处漏一处"。全套 PASS 109 / FAIL 0。 ✅ 验证:_core() 现从 CODEBUDDY.md 取块、长度 1274→1814、"含新判据=True、含旧禁令=False" ⇒ 下一轮起注入的就是新条文(钩子现读现用,⛔ 不必重启宿主)。 ⚠️ 未提交(按「未明确要求 ⇒ 不 commit/push」);改动已即时生效,不影响运行。

用户令:①「A 方案」②「写/改技能规则文件时一律用肯定表述(新建、生成也一样;经验沉淀/红线避坑类除外)」

① A 方案落地(仓操作,提交 5004909): git rm --cached session-mechanism/roots.env + session-mechanism/references/manifest.md (本地文件一律保留���实测 624 B / 18 568 B 仍在);新增入库样例 roots.env.example(占位符、无本机路径); .gitignore 加这两份的忽略行(⛔ 防 git add -A 捎回)并改掉原「刻意入库」注释。 ⇒ 别的电脑取仓时不再被"同名覆盖"换成我们的路径。

② 「一律用肯定表述」写进注入面(原来只在 rules.md,注入面看不到 ⇒ 所以我又写了一批负向表述)

  • 口径(用户逐字):写或改技能与规则文件时一律写「要什么、怎么做」;新建、生成时同样适用; 例外=以「经验沉淀」或「红线/避坑」为目的的技能与规则(写法=正向目标 + 括号里的踩坑依据)。
  • 落点:REPLY-CORE 段(工作区 + 包内,逐字一致)· rules.md §9 · 改包纪律.md 新增第 5 条硬纪律(「二、四条」→「五条」)· 01-文档索引.md 指针 · 并把我在注入块里加的那段负向表述改成正向主导。

🔴🔴 顺带抓到并修掉一个「我自己造成的净变差」(值得记住): 注入块有 1400 字上限、且从开头截 ⇒ 我加条文把它从 1274 顶到 1414+ ⇒ 尾部三条(「变相征询同样禁止」「不用征询句收尾」「本工作区另有定稿」)静默消失、不再注入; 而模型那边只看到一句"超长已截断",看不出少了哪几条规则。 ⇒ 修:上限 1400 → 2000;并在 t_reply_core_same_source 加断言 块长 ≤ 上限(以后顶破就报红)。 ✅ 验证:被截断=False;尾部三条都回来。 📌 教训(通用):凡"每轮注入 / 有长度上限"的面,改文字必须量长度 —— "改完自测全绿"不等于"改的内容真进了上下文"(同族:_core 三级回退那份)。

验证:全套自测 PASS 109 / FAIL 0;py 语法全绿;install.py --manifest 重算(78 份,语法失败 0); HEAD == origin/master == 5004909,工作区干净;roots.env / manifest.md 仓里已删、盘上仍在。

⚠️ 未推送:工作区 CODEBUDDY.md(注入块真源)也改了,但它属 dsh_shenxian_workspace 仓、 远端 work.alotbuy.com 已失效(SSH 主机密钥变更)⇒ 推不出去,本地生效。