排版规则补「变相征询」禁令;日志补 §49–§52

- CODEBUDDY.md:REPLY-CORE 块由 apply-reply-rules.py 重新生成
  (核心块 562 → 835 字符),新增「变相征询同样禁止」条款。
- 工作区日志 2026-10-06:补 §49(vibe 技能指针假红)、§50(统一用工作区版本=既有设计)、
  §51(技能机制全量体检:stop-dialog-guard 崩溃 + 2 处判据缺陷,均已修并变异对照)、
  §52(提问机制核实:规则每轮都在注入,失的是执行;并补堵变相征询缺口)。
This commit is contained in:
admin committed 2026-10-06 22:14:47 +08:00
1 parent f97541fd29
commit 4106327424
2 files changed
+693 -189

No files matched your search

+409
View File
@@ -613,3 +613,412 @@ vibe-product 上一条会话(10-06 08:15~08:20)已诊断出两条缺陷并**
3. ⚠️ harness 预导入里**没有 `sleep`** ⇒ 脚本头部自己 `import time; def sleep(s): time.sleep(s)`。
4. ⚠️ 探针选择器要跟着结构改:去掉内层 `.cols` 后 `#cols > .cols > .col` **恒为 0 条**
(不是布局坏了)⇒ 改 `#cols > .col`。
## §45 dsh- 技能整合完整性排查 + 决策判据常驻注入落地(2026-10-06 上午)
### 45.1 决策判据常驻注入(用户明令:「决策方法必须加载到每次对话中」)
**用户拍板**:归属=并入会话技能 `session-mechanism`;注入内容=**判据式精简版**。
**根因(三条实测)**:
1. 判据住在 `dsh-decision/references/`,而 `dsh-decision` **不在** `session-mechanism` 安装面;
2. `install.py` 声明表**逐字写着** `⛔ decision_bridge 不在此表内` ⇒ 跑「配置环境」带不上;
3. 🔴🔴 **`session-mechanism/SKILL.md:395` 既定口径「判据实体就在本档正文里,⛔ 不依赖任何其他技能」
与本包实际打架** —— 10-04 那次**只搬了 `01-功能优先协作协议`(问不问)**,
**决策方法论(U27/A6)从未搬入**(证据:本包正文搜 `U27`/`A6` **零命中**)⇒ **口径落空**。
**落地(6 件,一次做完)**:
- 新建 `scripts/hooks/decision-rules-hook.py`(挂 `SessionStart`,注入 1459 字符判据式精简版)
- `install.py`:**声明表 +1 条** + **`OWN_BASENAMES` +1**(⛔ 不加后者 ⇒ `--uninstall` 认不出、旧接线删不掉)
- 逐字搬入 `references/04-决策方法论.md`(33197 B / md5 `ab06e860…`)+ `dsh-decision-method/` 三份素材库
- 钩子指针**从外部包改为包内** ⇒ 真做到「⛔ 不依赖任何其他技能」
- 修掉搬入件里 **1 处源件既有断链**(`素材库-U-提报用户.md` → `素材库-U-用户决策.md`),并加搬运登记块
- `--dry-run` → `--apply` → `--verify` **全绿**(`SessionStart` 2→3 条,`decision_bridge` 4 条未被触碰)
⚠️ **踩到的头号坑**:`RULES` 常量里**中文引号写成了半角 `"`** ⇒ 字符串提前闭合 ⇒
`invalid character '/' (U+FF0F)` **整个钩子加载失败**。⇒ **改这文件后必须跑 `ast.parse`**(已写进文件头)。
### 45.2 dsh- 六个技能「说要整合、实际没整合」排查 → **结论:零遗漏**
| 技能 | 声称 | 实测 |
|---|---|---|
| dsh-decision | 2 原名合并 | ✅ 2 档 + 素材库子目录,清单表齐全 |
| dsh-diagnose | 3 原名合并 | ✅ 3 档,**每档都有 provenance 头 + 原行段声明**(整合范本) |
| dsh-knowledge | 2 原名合并 | ✅ 2 档 + 子目录 |
| dsh-local-env | 2 原名合并 | ✅ 2 档 + 子目录 |
| dsh-workflow | 2 原名合并 | ✅ 2 档 + 2 个子目录 |
| dsh-opensource-release | 无合并声明 | ✅ 5 档齐全 |
**11 个退役名全都有落点**;`references/` 引用 **零真断链**。
⚠️ 疑似断链 5 个(`dsh-users-platform`/`dsh-web-platform`/`dsh-laijing-github`/`dsh-hosting`/`dsh-univer-office`)
**是仓库名/插件名,⛔ 不是技能名**。
⚠️ `editing-guide.md`/`examples.md` 是 `last_change` 里叙述**外部技能** `shuorenhua` 的历史,⛔ 不是本包引用。
### 45.3 我本轮的三次误判(元教训)
1. **"dsh-diagnose 三档无 provenance"** → 撤销:档**都有** provenance 头。
真因=我的检测脚本**判据太窄**(只认文件名/目录名,不认档头「本档覆盖」声明)。
2. **"dsh-decision/workflow 清单表没登记子目录档"** → 撤销:**都登记了**(第 69/75/76 行)。
真因=脚本正则只认 `references/xxx.md`,**认不出目录行** `references/xxx/`。
3. **上一轮"界面四列全空"** → 撤销:**界面是好的**(已完成列 12 张卡)。
真因=`/api/projects` 的 `cols` **只是标签**,卡片在 `projects[].cards`;
我探错了端点(`/api/board` 404 ⇒ 空 JSON ⇒ 误判)。
🔴 **判据(写进 P0-93)**:**"未发现" ≠ "不存在" —— 报缺失之前先证明你的检测器能看见它。**
三次误判全是同一个病:**判据写窄了 ⇒ 假红**(同 `dsh-local-env` 记的「判据写窄=假红」)。
### 45.4 清理
- **5 个 `.bak-*`** → 归档到 `<工作区>/归档/技能bak清理-20261006/`(带技能名前缀防撞名,**可整目录还原**)。
⚠️ 本机 `Add-Type` 与 COM `Shell.Application` **都被沙箱拦** ⇒ 回收站路走不通 ⇒ 用项目既有的「移到归档目录」。
删前安全检查:3 份正本**都在、都比 bak 新、`.py` 语法 OK**。
- **1 个 `__pycache__`**(`dsh-local-env/.../resident-rules.cpython-313.pyc`)→ 直接删(纯缓存)。
### 45.5 项目管理界面:关系纠正(用户第 1、2 问)
**三层**:① **工作区**(目录,装了机制,**3 个**)② **目标**(`goal.json`,**2 个**)③ **任务卡**(`tasks.json`,**12 张**)。
- 用户第 1 问「工作区筛选里为什么加目标描述」⇒ ✅ **我对**:下拉该只列**工作区名**(目录名)。
- 用户第 2 问「已完成里显示的不是目标」⇒ ✅ **用户对**:那是**任务卡**(`t`/`3b-v1`/`ACCEP-FIX-01`…),
且台账 `title` 字段是 `None` ⇒ **回落成卡 id**,所以「看着不像目标」。
- ⏳ **待用户拍板**:一个工作区多个目标 ⇒ 界面读「当前目标」/ 读 `goals/` 历史 / 改多目标模型(三选一)。
## §46 项目管理界面:四列=目标(三改其正)+ 一个区多目标
> 🔴🔴🔴 **本节最该记的一条**:我连续**三次**把界面语义做错,每次都是**自己拍脑袋定模型、
> 没先查数据**。用户三次纠正(「已完成里显示的到底是不是目标」→「里面显示的都是目标
> 目标就是项目」→「但是你现在 已完成里面显示的 就不是目标信息,连一个目标的名称都没有」)。
> ⇒ 判据:**改"数据语义"之前,先把真源文件打开看一眼**(`goal.json` / `goals/*.json`)。
### 46.1 错在哪(三版)
| 版 | 我以为 | 实际 | 用户看到 |
|---|---|---|---|
| 1 | 四列装**任务台账**(`tasks.json`) | 四列该装**目标** | `3b-v1`/`dark-tokens` 一堆任务 id |
| 2 | 一个工作区**只有一个目标**(`goal.json`) | 一个区**可有多个**目标 | 「已完成」只剩 1 张,历史目标不见了 |
| 3 ✅ | 全部目标=`goal.json` + `goals/*.json` | — | 4 个目标名,四列正确 |
### 46.2 数据模型(实测坐实 · ⛔ 别再猜)
- `tmp/supervise-inbox/goal.json` = **当前目标**(恒 1 个)
- `tmp/supervise-inbox/goals/*.json` = **历史目标归档**(0..N 个;换目标时旧的那份挪进来)
- **全部目标 = 当前 + 历史**;`vibe-product` 实测 3 个(当前 1 已完成 + 历史 2)
- `lifecycle` 真身 5 值:`等待/进行中/阻碍/已完成/已暂停`(`collabd.py::GOAL_LIFE_*`)
⇒ 映射四列:等待→待执行|进行中→执行中|阻碍→有阻碍|已完成→已完成;**已暂停无列**
- ⚠️ `lifecycle` **可能带括号后缀**(`已完成(机器可判部分)`,手改进去的)⇒ 必须**剥后缀**再判
### 46.3 改了什么
- `project-board.py`:新增 `goal_life_of()`(**与 `collabd.py` 同源**:剥括号 + 别名表)、
`_load_all_goals()`(当前+历史,历史按 `declared_at` 倒序);
`COLS` 列键改 `waiting/active/blocked/done`;`cards` = 目标卡(一条=一个目标);
`tasks` 另存任务细账;`queue` 统计**去掉 `by` 兜底**(`by` 现在数目标,兜底会静默串数)。
- `project-board.html`:`_cardHtml` 主标题=**目标名**(⛔ 不回落到 id,那正是"看着不像目标")、
加 `.badge.cur`「当前」标记;概要区改「工作区 N 个 / 目标 N 个 / 其中执行中 / 已完成 /
任务台账 x/y」(**目标维度 与 任务维度 分开报**);下拉**只印工作区名**(⛔ 去掉了目标名拼接)。
### 46.4 布局事故(另记)
- 我以「空列白占屏」为由把四列改 `flex` +空列收窄 ⇒ 用户「现在是改乱后的样子」⇒ **已恢复**
`grid-template-columns:repeat(4,minmax(0,1fr))`。**判据:四态列固定维度,必须并排等宽;
"某状态是空的"不是布局问题,⛔ 不许因空列改列宽。**
## §47 详情页判据改卡片 + 修「张冠李戴」缺陷
### 47.1 用户要求
「优化点击目标打开的详情页(右侧滑动的那个),里面的判据用卡片显示,现在列表字段都挤成竖列了」
### 47.2 病根(不只是样式)
- `.flow{display:flex}` 把「判据键」「判据值」摆**同一行左右两侧** ⇒ 抽屉只 560px 宽
+ 值长(实测一条 100+ 字)⇒ **压成窄竖条**,几乎不可读。
- ⚠️ 附带帮凶:代码里 `a.v.slice(0,120)` **截断判据值** ⇒ 用户以为"判据就这么短"。
### 47.3 🔴🔴 **顺带查出的真缺陷:判据张冠李戴**
- 一个工作区**多个目标各有各的判据**(实测 `vibe-product`:当前目标 4 条 /
历史目标 V1-V4 4 条 / 历史目标 N1-N3 3 条)。
- 但服务端 `p.acc` **只装当前目标的**,前端详情页读 `p.acc`
⇒ **点历史目标的卡,显示的是当前目标的判据**。
- ✅ 修法:每条目标卡自带 `acc`(该目标自己的判据);前端 `var acc = t.acc || p.acc || {}`。
- 🔴 同型缺陷:主档字段原读 `p.declared_at`/`p.life_at`/`p.topics`(=当前目标的值)
⇒ 一并改读 `t.xxx`。**判据:详情页一切字段取"卡片自己那份",⛔ 不用工作区那层。**
### 47.4 改了什么
- CSS:新增 `.pcards/.pcard/.phead/.pk/.pv/.pst`(卡片式判据,一卡一条,
垂直布局=键在上值在下,左色条 green/red 区分过/未过)+ `.fcard`(任务细账卡片)。
- JS `openDetail()`:判据渲染改卡片;**去掉 `slice(0,120)`**;`acc` 改取 `t.acc`;
主档字段改 `t.xxx`;抽屉标题改**目标名**(⛔ 原显示 id `vibe-product@cur`)。
### 47.5 验证(实测尺寸,⛔ 不看布尔值)
- 判据卡宽 **527px**(铺满抽屉 560px 内),值框宽 **501px**
- 值 55 字 ⇒ 值框 501×40(2 行正常换行);值 34 字 ⇒ 501×20 ⇒ **不再是竖列**
## §48 UserPromptSubmit 超时:不是脚本慢,是「7 次冷启动 + 串行」
### 48.1 用户报障
`UserPromptSubmit operation blocked by hook` × 4 条,全部 `timed out after 10000ms`:
`reply-style-guard` / `stop-dialog-guard` / `skill-load-guard` / `session-log-guard`。
### 48.2 🔴🔴 根因(实测,⛔ 不是"脚本慢")
| 跑法 | 耗时 |
|---|---|
| 任一条单跑 | 0.23–0.36 s |
| 4 条各自单跑(累计,模拟串行) | **1.247 s** |
| **7 条并发**(多进程) | 0.84 s |
- `UserPromptSubmit` 上**串行挂了 7 条 hook**,每条=**一个独立 Python 进程** ⇒ 宿主每轮
**冷启 7 次解释器**(Windows 上 import `json`/`re`/`datetime` 都要重来)。
- 前 3 条预算 15+30+20 ⇒ 后面 4 条 `timeout=10` **被排队挤破**。
- ⚠️ 别被报错里的路径带偏:有一条写着 `E:/ProgramData.workbuddy/...`(**少一个点**)——
实测该目录**不存在**,只是历史 trace 里的残影,**不是当前接线**。
### 48.3 ✅ 治本:4 条守卫合并成一个进程
- 新增 `scripts/hooks/prompt-guards.py`:一次读 stdin ⇒ 本进程内 `runpy` 依次跑 4 个 guard
(临时接管 stdout 收 JSON)⇒ 合并输出。
- 实测 **1.247 s → 0.357 s(3.5×)**;注入内容**逐字一致**(654 字符 `additionalContext` 完整保留)。
- ⛔ **不许合并**:`supervise-ensure-hook`(常驻确保)、`wb-result-hook`(结果投递)、
`decision_bridge`(别处接线)—— 有副作用/不同语义,合并会改行为。
- 治标(也做了):4 条 timeout 10→30。
### 48.4 🔴 踩到的实现坑(合并入口)
`_Tee.buffer = self`(`TextIOBase` 子类)⇒ `sys.stdout.buffer.write(bytes)` **撞上
`TextIOBase.write`** ⇒ bytes 被 `str()` 成 `b'{"..."}'` ⇒ **JSON 解析必失败**
(现象:报"输出了非 JSON(6228 字节)")。
✅ 正解:`buffer` 用**独立的 `io.BytesIO`**;文本/字节两路**各收各的**。
### 48.5 顺带修的两处
- `reply-style-guard.py` **此前是手工接线**,⛔ 一直不在 `install.py::OWN_BASENAMES`
⇒ `--uninstall` 认不出。已补登记。
- 包内残留 `references/pitfalls.md.bak-p93-*`(我上轮留的)⇒ 移归档,
否则 selftest「包体卫生」FAIL。
### 48.6 验收
`install.py --verify` **全绿**(接线 9 条空载荷自检全 ok)|selftest **PASS 99 / FAIL 0**。
---
## §49 vibe 会话「会话技能没同步最新版」排查(用户第 10 问)
### 49.1 用户报障
`还有 vibe 会话一直说 本会话 会话技能没同步最新版 检查下`
### 49.2 🔴 报障出处(已定位,⛔ 非"猜")
= **`session-rules-check.py` 的 ⑧/B 组 `mem_ptr` 项**(「会话规则机制体检」),
经 `state.py` §5b 每轮随状态快照跑 ⇒ **stdout 摘要每轮打印 ⇒ 用户看到的就是它**。
实跑复现(`--ws .../vibe-product`):
```
🔴 [会话规则] 规则机制有硬缺口(见 fail 项)(fail 3 / warn 3 / ok 8)
✗ [B] 工作区记忆里的技能指针悬空(每轮注入 ⇒ 一直把人引错) —— product-planning
```
落盘标记 `<WS>/.workbuddy/collab/session-rules.json`:`verdict=fail`、`mem_ptr=product-planning`。
### 49.3 🔴🔴 真因:**技能根解析到「全局」库,而被测技能在「工作区」库**
判据(`session-rules-check.py:73` 与 `state.py:339` **同源同病**):
```python
SKILLS = os.environ.get("DSH_SKILLS_ROOT") or os.path.join(CFG, "skills") # CFG=CODEBUDDY_CONFIG_DIR
```
- 本机 `DSH_SKILLS_ROOT` **未设** ⇒ 解析到 **`E:/ProgramData/.workbuddy/skills`(全局)**。
- 而 `product-planning` **只在** `vibe-product/.workbuddy/skills/`(工作区库)**存在**,
全局库**没有**它(实测:`ls E:/ProgramData/.workbuddy/skills/product-planning` → No such file)。
- ⇒ `EXISTING_SKILLS`(=全局库目录名集合)里没有 `product-planning` ⇒ **被判"悬空"**。
- ⚠️ **这是假红**:`MEMORY.md` 里那句「`product-planning/`」指的是**工作区内的技能目录**,
它**真实存在**;判据却只查了全局库 ⇒ **判据的作用域错了,不是指针真断了**。
### 49.4 🔴 顺带查实的两条真问题(都不是这条报障,但确实存在)
① **工作区技能副本整体落后全局(13 个文件内容不同 + 全局独有 8 个)**
逐文件 md5 对比(`tmp/probes/_sm_diff.py`):
| 文件 | 全局 mtime | 副本 mtime |
|---|---|---|
| `SKILL.md` | 10-05 17:36 | 10-05 16:20 |
| `scripts/collabd.py` | **10-06 04:43** | 10-05 16:20 |
| `scripts/goalctl.py` | **10-06 08:44** | 10-05 15:33 |
| `scripts/selftest.py` | **10-06 08:42** | 10-05 12:08 |
| `scripts/board.py` | **10-06 05:08** | 10-05 15:25 |
| `assets/board.html` | **10-06 05:06** | 10-05 12:09 |
| `references/pitfalls.md` | **10-06 10:32** | 10-05 16:20 |
| `install.py` | 10-06 10:28 | **10-04 07:52** |
| ⛔ 全局独有 | `scripts/hooks/prompt-guards.py`(本轮新建)、`scripts/hooks/decision-rules-hook.py`、`references/04-决策方法论.md` 等 8 个 | — |
⇒ **副本缺的正是本轮与近两轮的改动**(`prompt-guards.py` 合并入口、`decision-rules-hook`、
决策方法论文档)。⚠️ 与 §43.3 第 3 条「副本跑的是旧代码」**同一根因,且已从"09-29"恶化到"10-05"**。
② **`snap_sync` 恒 warn**:`vibe-product/CODEBUDDY.md` **不存在** ⇒ 判据第 338 行直接 warn
(「找不到权威规则文件」)。⚠️ 本工作区确实没有 `CODEBUDDY.md` ⇒ 这条**不是故障,是判据前提不成立**。
### 49.5 ⛔ 我差点误报的一条(**已用探针证否,记下来防复发**)
终端 `head`/`cut` 读副本 `SKILL.md` 时显示 `浼氳瘽鏈哄埗`(=「会话机制」的 mojibake),
**看起来像文件被 GBK 写坏**。⇒ 写探针判**原始字节**(`tmp/probes/_enc_probe.py`):
`utf-8 解码 ✅ 通过`、`无乱码特征`、`CR=0 LF=938` ⇒ **文件本身完全正常**,
乱码只是 **Git Bash 终端显示层**的问题。🔴 **教训:判编码必须读字节,⛔ 不能信终端回显。**
### 49.6 结论与处置建议(⛔ 本条只做诊断,未动手改)
| 项 | 判 |
|---|---|
| 用户看到的 `mem_ptr` fail | 🔴 **判据作用域错误(假红)** ⇒ 正解是判据改为「全局 ∪ 工作区」两个库都查 |
| 副本落后全局 13 文件 | ✅ **真问题** ⇒ 按 `副本使用说明.md §六 四步同步纪律`(先 diff、只覆盖真差异、⛔ `roots.env` 绝不覆盖、跑一次自测) |
| `snap_sync` warn | ⚠️ 判据前提不成立(本区无 `CODEBUDDY.md`)⇒ 应改判「本区无权威文件 ⇒ 跳过(not ok)」 |
---
## §50 「统一都用工作区版本」——核对结论:**这本来就是这个包的既定设计**
### 50.1 用户提问
`要不要统一都用 工作区版本,修改全局版本后 同步到工作区?`
### 50.2 🔴 结论:这套策略**早就定了**,而且写在 `deploy_code.py` 的文件头里(2026-10-03 用户定案)
用户原话(引自 `deploy_code.py` 头注逐字):
> 「**跨工作区使用会话协作技能,除了看板共用,其余都是独立的,包括程序和相关文件**」
⇒ 现行架构成型如下(⛔ 不是我提的新方案,是**既有设计未被执行**):
- **技能目录 `E:/ProgramData/.workbuddy/skills/session-mechanism/` = 源(唯一真身)**
- **每个工作区 `.workbuddy/collab/` = 自己的副本**(各区独立跑、互不牵连)
- **分发工具 = `scripts/deploy_code.py --ws <工作区>`**(清单 `DEFAULT_FILES` 7 个文件)
- **看板 `board.html` 是唯一共用件**(⛔ 不在分发清单内)
### 50.3 🔴 佐证:两个常驻**确实都在跑各自工作区的副本**(进程级取证,⛔ 非看文件)
`Get-CimInstance Win32_Process` 实测 argv:
- `PID 64392` → `ai1net-dsh-server/.workbuddy/collab/supervise-launch.py`
- `PID 21888` → `vibe-product/.workbuddy/collab/collabd.py --supervise`
- ⇒ 机制已是「各区独立」,**问题不在架构,在"改完源忘了跑 `deploy_code.py`"**。
### 50.4 🔴 实测漂移(`ai1net` 副本 vs 全局源,逐文件 md5)
**清单内 7 个(`deploy_code.py::DEFAULT_FILES`)**:
- ❌ `collabd.py`(副本 10-05 20:03|源 **10-06 04:43**)—— 常驻本体,**漂得最要命**
- ❌ `goalctl.py`(副本 10-05 17:01|源 **10-06 08:44**)
- ✅ `collabctl.py` / `guard.py` / `session-rules-check.py` / `init_workspace.py` / `supervise-launch.py`
**清单外但也漂了(首版漏网,同一教训 P0-57 同族)**:
- ❌ `board.py`(副本 10-05 15:25|源 10-06 05:08)
- ❌ `selftest.py`(副本 10-05 17:14|源 10-06 08:42)
- ❌ `stop-collab.py`(副本 10-01 18:51|源 10-02 21:30)
- ❌ `wake-session.py`(副本 09-30 12:54|源 10-02 21:26)
### 50.5 与 §49 的关系(⛔ 别把两件事混了)
- §49 的 `mem_ptr` 假红 = **判据作用域错**(`vibe` 侧、`product-planning`)—— 是**体检误报**。
- §50 的副本漂移 = **分发没跑**(`ai1net` 侧,`collabd.py`/`goalctl.py`)—— 是**代码真的旧**。
- 两者**同族**(都是「改了一处、别处没跟上」),但**修法完全不同**:前者改判据,后者跑分发。
### 50.6 ⛔ 本轮仍未动手(只取证)
---
## §51 「重启后自决策方法生效了吗」→ 顺势全量体检技能机制,修 3 个真缺陷
### 51.1 用户两问
① 「已经重启,现在自决策方法生效了吗」 ② 「发现问题 检查所有技能相关机制 是否正常」
### 51.2 ✅ 原问答复:**已生效**(进程级+内容级双证)
- `decision-rules-hook.py`(SessionStart)实测 `inject ... len=1459`,14:14:53 就在跑;
注入正文=「【决策判据 · 常驻(违反即事故)】… 🔴 目标不打折,路径取最小代价(U27 + A6)…」。
- `prompt-guards.py`(UserPromptSubmit 合并入口)实测 rc=0、产出 654 字符、**4 guard 合计 0.058 s**
(stderr 自报 `reply=0.020s stop=0.024s skill=0.011s session=0.003s`)。
- 结论:**你上一轮做的 hook 合并 + 决策判据注入,重启后都在跑。**
### 51.3 🔴 但全量体检挖出 **3 个真缺陷**(都已修 + 变异对照)
**① `stop-dialog-guard.py` 每轮崩溃 —— 整条钩子实际是死的(最严重)**
- 症状:`stop-dialog-guard.log` **86 条 `EXCEPTION|ValueError: not enough values to unpack (expected 3, got 2)`**,首条 05:09:26,**每轮复现**。
- 真因:`session_budget()` 的**返回元组长度不一致** —— 两条早退路径 `return None, None`(**2 值**),
而末尾 `return last_in, n_calls, prev_in`(**3 值**),调用方第 611 行按 **3 值**解包。
- 触发条件:transcript **> 64 MiB** 或文件不在。**本会话 transcript 实测 192.8 MB** ⇒ 每轮必命中。
- 🔴 **为什么一直没人发现**:本钩子是 **fail-open**(异常仍 `sys.exit(0)`)
⇒ 宿主零报错、`install.py --verify` 只判 `rc=0` ⇒ **判它 "ok"(假绿)**。
而实际死掉的是**整条**:水位与收口 / 接续机制起点 / 预算告警 / 门禁自检 / 路径自检。
- 修:两处早退改 `return None, None, None` + docstring 补齐三元说明。
- 验:同 transcript 复跑 ⇒ **EXCEPTION 停在 86 不再增长**,且**首次**写出完整日志行
`invoked(user-prompt)|mode=inject|…`(以前在到达之前就崩了)。
**② `session-rules-check.py::KEY_HOOKS` 未随合并更新 ⇒ 每轮「关键钩子不在册」假红**
- 真因:上一轮把 4 条 UserPromptSubmit 守卫合并成 `prompt-guards.py`,而本判据仍按**旧文件名**找
⇒ 惩罚的正是「按要求做过的合并」。
- 修:`KEY_HOOKS` 第 2 元由**单名**改**一组名**(命中任一即算在册)+ 匹配/报错两处同步。
- 验:`hook_reg` fail → **ok**(ai1net fail 3→2)。
**③ `snap_sync` 用 mtime 当内容判据 ⇒ 连续 4 天假红**
- 真因:原判据 `getmtime(auth) - getmtime(snap) > 0.01 天` ⇒ 权威 `CODEBUDDY.md` 只要**被 touch 过**就报
「快照比权威旧」,而**快照内容一个字都没差**(实测 md5 逐字节相同 `f54e48d4…`、`diff` 0 行)。
- 🔴🔴 **我中途踩的坑(必须记)**:第一次修成「调 `resident-rules.py --check`」,
**变异对照当场证否** —— 把快照**截断到 400 字节**,它照样报 `✅ 关键规则齐备`(rc=0)
⇒ **判据变恒绿,比原来的假红更坏**。真因:`--check` 校验的是**目标 CODEBUDDY.md 里规则齐不齐**,
**根本不含"与快照比对"**。
✅ 正解:**导入抽取器本体**(`importlib` 加载 `resident-rules.py`,把它的 `SNAP` 常量临时改指临时文件,
调它自己的 `snapshot()` 取"应有内容")⇒ 与磁盘真快照比对 ⇒ ⛔ 不抄第二份抽取逻辑、⛔ 不碰真快照。
- 验(四段):正常→ok|**变异(截断)→fail(判据不是恒绿)**|还原→ok|无 `CODEBUDDY.md` 的区→warn「本项不适用」。
**④ 顺带:`mem_ptr` 只查全局技能库 ⇒ 工作区自带技能被判悬空(vibe 假红,即 §49)**
- 真因:`EXISTING_SKILLS` 只 `listdir(SKILLS)`(全局根),而 `product-planning` **只在**工作区根的技能库。
- 修:`_skill_names()` = **全局根 ∪ `<WS>/.workbuddy/skills`**;判据问"**本机取不取得到**",⛔ 不问"全局库有没有"。
- 验:vibe `mem_ptr` fail → **ok**;合成区(含假技能名)仍能报 fail(**非恒绿**)。
### 51.4 🔴 元教训(写进 pitfall P0-95)
- **「换了个看起来更对的调用」≠「判据变强了」** —— 第 ③ 条我差点交付一个恒绿判据。
⇒ **凡改判据,必须重跑变异对照**(真去破坏一次,看它会不会报)。
- **fail-open + 只看 rc=0 的体检 = 假绿温床**:`install.py --verify` 9 条全 "ok",其中一条其实是死的。
⇒ 判「钩子活着」要**读它自己的日志尾部**(有没有 EXCEPTION / 有没有写出完整 `invoked` 行),⛔ 不看 rc。
### 51.5 分发与回归
- `deploy_code.py --ws <区> --only session-rules-check.py` ⇒ ai1net / vibe **md5 均一致**(`b2b46899`)。
- `install.py --verify` **全绿**(PASS 99 / FAIL 0)。
- ⚠️ `session-rules-check.py` 由 `state.py` **每轮现调** ⇒ 免重启;⛔ **`collabd.py` 改了才需重启常驻**。
### 51.6 改动文件
- `~/.workbuddy/skills/session-mechanism/scripts/hooks/stop-dialog-guard.py`
- `~/.workbuddy/skills/session-mechanism/scripts/session-rules-check.py`(4 处)
- `~/.workbuddy/skills/dsh-local-env/.../常驻规则-快照.md`(重生成本无变化,仅 mtime)
- 备份:`tmp/bak-resident-rules-20261006/`
---
## §52 「提问机制又失效了?」「需要决策的格式没了吗?」→ 核实:**机制全在跑,失的是我的执行**
### 52.1 用户三问(同一件事)
① 「是不是 提问机制又失效了 为什么提问不给 选项」② 「需要决策的 格式排版都没了吗」③ 「这些规则应该固化到 每次会话中上下文中」
### 52.2 🔴 核实结论:规则**本来就每轮都在上下文里**(不是失效,是我没照做)
三处独立证据,全部实测:
- **每轮注入在跑**:`reply-style-guard.log` 在你最近三条消息上**逐条命中** ——
`15:49:10` / `15:49:38` / `15:49:59`,每条都是 `core=562 字符` + `HIT … 1647 字节`。
⇒ **你每说一句话,排版规则就被注入一次**。
- **规则文本本来就在里面**:`REPLY-CORE` 块(`CODEBUDDY.md:313-322`)**第七条**逐字写着
「**待拍板项**:放回复**最后一节**,逐条编号;每条写清「问题 + 说明(影响谁/断多久/花多少钱/有无不可逆)
+ 各候选的优点与缺点 + 倾向」」—— 我要的格式,规则里**一条不缺**。
- **无 `CODEBUDDY.md` 的工作区也不漏**:`reply-style-guard._core()` 是**三级回退**
(① `<WS>/CODEBUDDY.md` → ② **包内** `references/03-回复排版-核心块.md` → ③ 外部技能那份),
实测 `vibe-product` / `agent-product` **都没有 `CODEBUDDY.md`**,走第②级照样有注入。
- **SessionStart 另有一路**:`decision_bridge.py` 注入 378 字符《提问规范》(含"选项:每个候选各占一段、
必须写清优点与缺点"),14:14:53 实测在跑;本会话 transcript 里「提问规范」出现 **65 次**。
⇒ **结论:机制没坏、没退役、没静默。你上一轮看不到选项,是我把自己写的"待处理清单"当成了汇报,
没按规则做成待拍板项。** 这是执行问题,不是机制问题。
### 52.3 🔴 但我确实发现一个真缺口 ——「变相征询」没被规则明文堵住
- 我上轮用的是「**先只报不动**」+「**等你发话**」—— 它**不是征询句**(逃过 `stop-dialog-guard` 的收尾判据),
也**不是**标准的"待拍板项"(没写候选优缺点)⇒ **形式上两头都不沾,实质上就是要用户拍板**。
- 旧规则只堵了「征询句收尾」与「待拍板项要写全」两处,**中间这条缝没人管**。
- ✅ 处置(单源改造,未抄第二份):
① 权威源 `agent-operating-rules/references/回复排版-核心块.md` 加一条
「🔴🔴 **「变相征询」同样禁止**:凡是要用户拿主意的事(含"先只报不动/等你发话/我倾向X你看呢"这类
**不带选项的待定清单**)一律按待拍板项写(问题+说明+**各候选优缺点**+倾向);反过来自决的写成陈述句」;
② 同步内联副本 `session-mechanism/references/03-回复排版-核心块.md`;
③ 用既有注入器 `agent-operating-rules/scripts/apply-reply-rules.py --apply --ws <区>` 重生成 `CODEBUDDY.md` 的块。
### 52.4 验收(逐项实测)
- `apply-reply-rules.py --check`:改前**报不一致**(`a791f6be… vs a04b29fa…`)⇒ 改后**一致 ✅**。
- 核心块 **562 → 835 字符**;每轮注入实测 **927 字符**,且 `含「变相征询」= True`、`含「待拍板项」= True`。
- 备份 `CODEBUDDY.md.bak-replycore-20261006-161328` 已挪出工作区根 → `tmp/bak-replycore-20261006/`。
### 52.5 顺带查实(不是缺陷,记下来免得下次再查)
- `ai1net-decision-laya/runtime/services.paused`(10-06 04:24)= **只暂停推理类服务**("决策只落库"那一档),
**不影响提问规范门**(门是纯本地字符串判定,不调模型、不起进程)。
- `decision_questions` 表最新一行停在 **10-06 01:46**,且最近几行 `gate_verdict=weak/blocked` ——
⚠️ 该表只在**识别到提问**时落行;上轮那套「不带选项的清单」两路都没命中,所以**没落行**(与 52.3 同因)。
- `apply-reply-rules.py` **不在** session-mechanism 包内、而在 `agent-operating-rules` 里
(包内那份只是"内联副本",会话技能的钩子优先读 `<WS>/CODEBUDDY.md` ⇒ **改口径要两处同步 + 重跑注入器**)。
### 52.6 改动文件
- `~/.workbuddy/skills/agent-operating-rules/references/回复排版-核心块.md`(权威源 · +1 条)
- `~/.workbuddy/skills/session-mechanism/references/03-回复排版-核心块.md`(内联副本 · 同步)
- `E:/ProgramData/AIProject/ai1net-dsh-server/CODEBUDDY.md`(REPLY-CORE 块由注入器重生成)