ddea70b7-fe75-48d1-aadc-ef1a5f5d4814 ·「确认guest用户数据迁移到106服务器」Skill 调用计数 = 0),只用常驻规则在推。R7-边界② 说 systemd 单元"只报告不动手",§1/R8 说"直接做");②「决策方法」这个判据被放在了"需要时才看"的指针表里,而它其实是会致违规的实体规则。用户说的是"停下来问我"。取证后可以精确定位——正文里显式上抛只有 1 处,另有 2 处"变相上抛"(用"遗留 / 你说了算"把决定权交回去,形态不是问句,效果相同)。
| 时间 | 形态 | 内容与用户反应 |
|---|---|---|
| 00:21:50 A idx=562 |
变相上抛 |
「五、遗留(含回头条件)」挂起 3 条:① 106 缺 ffmpeg(说要 40 分钟从中转搬)② 106 没有账号 provisioner("我可以接着把 provisioner 铺到 106")③ 存量用户依赖未共享化。 2 分钟后 用户 U3 逐条打掉:「这些106直接去网上下载不就行了」(AI 一测内网源 43 MB/s,几十秒装完)·「该谁处理就开发对应功能让谁处理,不要做这种零时方案」·「啥意思没看懂,什么存量用户」。 |
| 00:28:23 A idx=651 |
显式上抛 |
「## 四、待你拍板」两问: 1. 106 上那套旧控制面要不要停(A 停用并禁用 / B 维持现状,格式完全符合规范,还写了优缺点和"我倾向 A") 2. D1–D6 先做哪些("先 D1+D2" / "先 D5",同样写了优缺点和"我倾向先 D1+D2") 45 秒后 用户 U5:「还是删除把 A. 停用并禁用 删除数据」 58 秒后 用户 U6:「D1–D6 先做哪些 按照你的规划执行,中间有问题参考决策方法」 |
| 00:40:26 A idx=888 |
变相上抛 |
「四、遗留」第 2 条:代码尚未 commit:…都在代码仓工作树里(按纪律没擅自提交,你说了算),且 A idx=915 又重复了一次。注:这条本身合规( §4 提交边界 明文"未明确要求不 commit"),但"你说了算"这个形态等于把球踢回去。
|
§1 判据"有客观可判的优劣 ⇒ 自己定",这属于明显更优,该自己拍掉。用户点名"参考决策方法"之后,AI 依然没有加载 dsh-decision-method。整个会话 265 次工具调用里,Skill 出现 0 次,也没有 Read 过任何 SKILL.md。
⇒ 那份方法论从未进入上下文。AI 全程只靠常驻的 CODEBUDDY.md §1 判据在推。
全程没有用提问工具,问题全写在正文里。
⇒ 印证 §1 里已有的那条判断:「提问闸门」hook 只拦得到工具,拦不到正文里的征询。这条设计上的漏洞这次真实发生了。
这是本次最关键的一条。reasoning 里留有完整的推理链(R idx=605 → 609 → 642):
// 它的原始思考,逐字引用 "按 R7-边界:106 是平台的服务器(我 lane 内?)—— 嗯,'平台级 / 全局 / 别人 lane' 提到 systemd 单元属'别人 lane' ⇒ 只报告不动手?" "按 R7-边界:systemd 单元属'平台级' ⇒ 只报告不动手。 然后报告 + 提问(遗留控制面是否清理 —— 这是'真门禁'吗? 停旧服务 = 影响面可能超范围 + 别人 lane ⇒ 应该问 ✓)" "待拍板项:106 旧控制面(A 停用/B 维持)—— 这是真取舍(各有优缺)✓ 其他:D1-D6 的开发优先级 —— 这属于'先做哪个'= 业务优先级 ⇒ 边界外 ⇒ 要问" "按 §1:'业务目标与优先级(做不做、先做哪个)'属边界外 ⇒ 必须问 ✓"
⇒ AI 不是凭直觉上抛的,它是"照章办事"推出来的。所以病症不在态度,在规则本身:它在两条打架的规则里选了保守的那条,又把有客观排序的事归到了"业务优先级"。
「106 上的 systemd 单元能不能动」这一个问题,CODEBUDDY.md 里有三处给出相反结论:
| 规则位置 | 原文要点 | 推出的动作 |
|---|---|---|
| §1 第 27 行 | 开发环境服务器 ⇒ 重启 / 停实例 / 改配额或 env / 改 nginx·nft 直接做,只需动手前一句话说明 | 直接做 |
| §3 R8 | (已修正为)"均可直接做,不必再等确认" | 直接做 |
| §3 R7-边界② | /var/lib/**、全局符号链接、systemd 单元、nginx·nft、别人的 profile/产物 ⇒ 一律只报告、不动手 | 只报告 |
| §2 第 58 行 | 要动生产 ⇒ "先按 R8 说清…并取得确认"(与 R8 自己打架) | 等确认 |
AI 撞上这个交叉地带时,选了保守侧。但 R7-边界② 的错不在措辞严,而在于它把"平台级 / 全局"与"别人 lane"并列了——于是 AI 把自己家的 106 节点读成了"别人的东西"。事实上 AI 在更早的 R idx=79 里还判对过:「这不属于'别人 lane'(106 是我们自己的 worker 节点,属于平台)」。同一个对象,前后两次判断相反。
CODEBUDDY.md 开头明确写着分层判定标准:
而「决策方法」被放在了 §2 「什么时候去查什么」—— 动作触发的指针表 里,也就是被定性为"需要时才看、看了更准、不看也不违规"的那一类。
可事实上——"该不该上抛"判错 = 直接违反 §1 提问判据,那是被明文列为"必须实体常驻"的章节。文件在第 67 行自己也承认了这个风险:
⇒ 结论:一个会致违规的判据,被自家标准判成了"可给指针",结果就是这次——用户点名了,指针也没被走。
| AI 的判断 | 它引用的依据 | 按用户口径应当如何 |
|---|---|---|
| 「106 旧控制面停/留」要问 | R7-边界② "systemd 单元 ⇒ 只报告" |
自决(直接删) 对象是我们自己的 106;且"旧控制面早于集群化切换、已运行 1 天 19 小时"这个不确定点完全可以自查(was 有访问日志/依赖引用),不该把"我不确定"当上抛理由。用户 U5 / U7 两次自己给出了"删除"。 |
| 「D1–D6 先做哪几个」要问 | §1 "业务目标与优先级 属边界外" |
自决 AI 自己已排出客观顺序(3 项 P0、D1+D2 能闭环"新用户落 106 起不来"的真实缺陷)⇒ 属明显更优,按 §1"候选只有优点 ⇒ 自己拍掉"。用户 U6:「按照你的规划执行」。 |
| 「ffmpeg 要不要传」写进遗留 | R idx=159 "暂缓(问用户?或直接传)" |
自决(先查再传) 该查的是"目标机自己能不能装"——AI 只测了公网和 GitHub,没测内网源。用户一句话点破后,内网源 43 MB/s 几十秒装完。 |
避免把复盘读成"AI 什么都不自己干"。同一会话里,U6 之后 AI 实际上是这样跑的:
src/worker/agent.ts 的 /launch 是唯一同时握有 uid 和"这台机器"的位置 → 新增 ensureOsAccount() → 补 5 例测试并入 npm run verify(全绿)→ 两节点部署 → 用测试 uid 端到端验证通过。bootstrap-worker.sh(幂等 + --check + --prune-legacy),补齐了 dshs doctor 漏掉的四项自检,两节点全绿退出码 0。/root/dsh-users-platform 时打断了 worker 的 node_modules 软链 → 用 47 的 lock 在 106 npm ci --ignore-scripts 重建;自己写的 ensureOsAccount 用"账号名"做校验导致 guest 起不来 → 改成只看 uid。⇒ 所以真正的病灶很窄:只在"平台级资源"和"业务优先级"这两个交叉地带判错了方向。
| 文件 | 改动 |
|---|---|
| CODEBUDDY.md §1 新增 |
🔀 规则冲突裁决顺序:同一对象被多条规则给出相反结论时,按 R8 → §1 边界内自决清单 → 其余红线 取首个命中项,⛔ 不再"自行取保守侧"。⛔ 冲突 ≠ 门禁:规则打架不构成上抛理由;门禁只有两类(不可逆破坏性操作 / 边界外六类)。 🔑 "平台级" ≠ "别人的":自己的 47 / 106 / 本工作区上的 dshs*·dsh-* 单元、/var/lib/dshs/**、nginx·nft ⇒ 按 §1+R8 直接做。📌 上抛前三问:① 是我们自己的资源吗?② 关键不确定点查证了吗?③ 第一名是否明显更优?—— 任一为"是"即自决。 |
| CODEBUDDY.md §2 第 58 行 |
删掉与 R8 矛盾的"并取得确认",改为"直接做,动手前一句话说明;只有破坏性不可逆才先出清单"。 |
| CODEBUDDY.md §2 第 61 行 |
「用户点名决策方法 ⇒ 必须立即 Skill(dsh-decision-method),不得凭记忆代替、不得只靠 §1 判据」——把指针升级为硬要求。 |
| CODEBUDDY.md §3 R7-边界② |
明确②的适用对象 = "别人的 / 归属不明",⛔ 不含我们自己的 47/106/本工作区资源。判据看"归属",不看"是不是平台组件"。 |
| 技能 dsh-decision-method |
2.7.4 → 2.7.5:§4.4 硬约束 2 修正("与红线冲突 → 红线赢"过宽,正是本次上抛诱因)+ 新增 §4.5 规则冲突裁决顺序 + 上抛前三问(含本案例);description 补触发词。 |
| 技能 dsh-feature-first |
1.7.0 → 1.7.1(5 处)——这才是 AI 判定"该不该上抛"的主依据,上一轮漏了: · §3.2 删掉「只有真会中断的(重启服务 / 停实例 / drain / 改配额·env / 改 nginx·nft)才上抛」→ 这正是本次上抛的直接依据;补上"平台级 ≠ 别人的"。 · §3.4 第 1 类加出口:方向已定 + 候选有客观排序 ⇒ 「先做哪个」属边界内自决(AI 把"D1–D6 先做哪些"归进来才上抛的)。 · §3.4 第 7 类 去掉 R8;§6 自检第 4 条改写 + 新增第 0 条;§7 加 R8 例外注。 · §5.4 新增硬约束 10「并列内容逐条分段」+ 反模式 14(你这次提的排版要求)。 |
| .codebuddy/rules/ 条件规则 |
按路径自动注入的规则里也有同源冲突: · server-ops.md:表格「✅ 会,须先知会」→ 改为「直接做,动手前一句话说明,⛔ 不必等确认」。· frontend-ui.md:补 R8 修正注 + 按 R11 说明"无收益的重启仍属劣化"。 |
| 技能 workbuddy-session-forensics |
三个数据源的结论全部作废重写(本次实测发现旧版严重过时): · workbuddy.db.sessions 表已可用且最新(67 行)→ 升为首选入口· 转录 projects/<目录名>/<sid>.jsonl 本机完全可读(旧版"近期会话没有"是踩了目录名的坑:工作区搬迁后新会话进 e-ProgramData-AI技能-…,旧会话留在 d-AI技能-…)· edge-sync.log 已不存在· 新增 §2b jsonl 抽取法(⚠️ 文本元素是 input_text/output_text,不是 text —— 按 text 抽会全空且不报错)+ §2c「分析 AI 为何上抛」三段取证法 |
三处同步已对账一致:dsh-decision-method md5 7ae701b7…、dsh-feature-first md5 6ca922e2… ⇒ 本机 = 文档库 = 镜像 /opt/dsh/docs/skills/;新钩子 md5 9041fe37… 本机 = 镜像。对账一致数 177 → 178。
其余 3 项差异(bash-output-guard.py / stop-dialog-guard.py / DEPLOY-本部署.md)是既有差异、不属本次改动 —— 按纪律只报告、不动手。
这是根因的机制层。技能加载是软的 —— 由模型判断相关性,CODEBUDDY.md 第 67 行自己就写着「不能保证」。所以不能把它当唯一防线。四层硬化全部落地,代价都很低,且互不排斥:
| 层次 | 做什么 | 可靠性 |
|---|---|---|
| ① 判据实体化 根本解 |
把"该不该上抛"的判据直接写进 CODEBUDDY.md §1(规则冲突裁决顺序 + 上抛前三问)—— 即使技能永远不加载,判据也在。 |
最可靠 每次会话必加载 |
| ② 机制层强制 新钩子 |
新建 scripts/skill-load-guard.py(UserPromptSubmit 钩子):扫用户输入,命中「决策方法 / 参考决策 / 按你的规划 / 别问我 / 自行决策」⇒ 注入一条紧邻用户消息的强制加载指令(位置比静态规则文件显著得多)。自作用域(只在本工作区生效,其他项目一律放行);急停双闸;低频自证日志。 |
强 不依赖模型自觉 需完全重启才生效 |
| ③ description 零成本 |
给 dsh-decision-method 的 description 补触发词:「用户点名决策方法 ⇒ 必须立即加载,不得凭记忆代替」。description 每轮都在上下文里。 |
中 仍是提示 |
| ④ 自检清单 | dsh-feature-first §6 新增第 0 条:「用户本轮点名了某个技能 / 方法吗 ⇒ 必须先加载」。 |
中 靠自觉 |
| 输入 | 结果 |
|---|---|
| 含"参考决策方法"· 本工作区 | ✅ 输出 additionalContext,含强制加载指令 + 本案例 |
| 普通提问 · 本工作区 | ✅ 无输出(不添乱) |
| 含关键词 · cwd = 其他项目 | ✅ 无输出(自作用域生效,不污染别的项目) |
⚠️ 钩子和 CODEBUDDY.md 都需完全重启 WorkBuddy 才加载(关窗 ≠ 退出)。重启后想确认钩子活着,看 .workbuddy/skill-load-guard.log —— 没出现 HIT 行只说明还没命中过,不等于没装好。
settings.json 里 stop-dialog-guard.py(上一个同类钩子)的安装命令带着 -S -E —— 而该脚本自己的 docstring 就明确警告:
-E…本机设了 PYTHONUTF8=1,而 -E 会把它全部忽略 ⇒ stdin 回退 cp936 ⇒ 含中文的 payload 解码即炸。2026-09-15 实测:-S -E 曾让本钩子"看起来从未被调用"整整一天。」已去掉 -E(保留 -S)。即:那个钩子此前很可能一直没真正生效过 —— 与本次问题同源:机制看着装了,实际没跑。
数据源(全部本机、只读):
取数中间件(已归档,不再散在工作区根):