diff --git a/.workbuddy/memory/2026-10-06.md b/.workbuddy/memory/2026-10-06.md index cf9449e..256e2ed 100644 --- a/.workbuddy/memory/2026-10-06.md +++ b/.workbuddy/memory/2026-10-06.md @@ -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 +``` +落盘标记 `/.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()` = **全局根 ∪ `/.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()` 是**三级回退** + (① `/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` 里 + (包内那份只是"内联副本",会话技能的钩子优先读 `/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 块由注入器重生成) diff --git a/CODEBUDDY.md b/CODEBUDDY.md index 59beff9..6a84e44 100644 --- a/CODEBUDDY.md +++ b/CODEBUDDY.md @@ -1,20 +1,20 @@ # DSH 平台项目 — 项目指令(每次会话自动加载) -> **本文件只放两类东西**:① **动作前必须生效的规则**(不常驻就会出事)② **「什么时候去查什么」的指针**。 -> 知识型内容一律**不在此复制** —— 按 §2 的触发条件**现查**。 +> 🔴 **当前结论(先读 · 最后更新 2026-09-30 05:56)** +> · **文档记录规则**(用户 09-30 定):**最新结论放最前** · 历史**倒序** · 只留最近 5 轮(**教训/用户定案原话/实测读数**三类**豁免**)· 超出的**归档**(⛔ 不删)⇒ 规范 `agent-operating-rules §1.7a`;归档落 `$WS/归档/`,**按类别分**(索引见 `$WS/归档/README-索引.md`)。 +> · **协作机制**:唯一权威 ⇒ `~/.workbuddy/skills/session-mechanism/references/architecture.md`(其头部有「当前结论」节);⛔ `交付物/` 里 4 份"多会话协同"平行件 **2026-09-30 已退役**,⛔ 不作用判据。 +> · **取现状**:⛔ 不拿旧文档/旧实测单当现状 —— **先按 mtime 取最新那份**(`agent-operating-rules §1.7b`);⛔ 也不拿"任务图节点 done"当"判据通过"。 -### ⛔ 分层判定标准(防止把该常驻的东西做成指针) +> 🔧 **PY**=`E:/ProgramData/.workbuddy/binaries/python/versions/3.13.12/python.exe` | **WS**=`E:/ProgramData/AIProject/ai1net-dsh-server` | **DOC**=`D:\github\dsh_shenxian\dsh-server-docs` | 脚本目录 = `$DOC\07-scripts`。 +> 📎 **【详】**=`$WS/docs/规则与载体/规则详解_红线与实证_20260924.md`(各条**解释段 / 历史 / 实证 / 命令全文**,章节 §A–§H);⛔ **冲突以本文件为准**。 +> ⚠️ **章节顺序按注入优先级排**(🔴 DSH 侧**按 `maxBytes` 预算注入,默认 65,536 B**,**不是「只取前 8,000 字符」**;超限时**先丢整份宽泛文件、再截断最具体文件**)⇒ 实体章排前 · 指针章排末尾(清单见下)。 -> **这条内容如果不看,会不会导致「违规」或「事故」?** -> - **会 → 必须常驻实体内容**(写在本文件 / MEMORY.md 里,不许只给指针) -> - **只是"更慢、更绕" → 才可以只给指针** +**⛔ 动作前必须执行的三条**:① **先跑状态**(1 次调用顶十几轮探索)`"$PY" "$WS/state.py"`(`--online` 加远端基线)—— **跑完之前不许 Glob/Grep 全库摸底**|② **改文件之前先"抢"锁**(不是"检查")`bash $DOC\07-scripts/handoff-guard.sh --claim-exec "<会话名>"` —— **抢到之前不要动文件**,抢不到 = 停手(判据 ⇒ §6)|③ **推送前** `bash scripts/docs-sync-check.sh`(对账)+ `PUSH=1 bash "$DOC\07-scripts/handoff-guard.sh"`(**幽灵文件硬判定**)。 -**因此以下内容一律以实体形式常驻,任何"瘦身"都不得把它们降级为指针**: -§1 提问判据 · §3 红线 R1–R10(全表) · §4 提交边界 · §5 规划与执行分离 · §6 并发纪律 · §7 环境要点 · §8 会导致事故的实测事实。 -**可以是指针的只有**:平台背景知识、UI 规范细节、档案模板、历史方案 —— 即"需要时才看、看了更准,不看也不违规"的那类。 +### ⛔ 分层判定标准 -> 加载顺序:用户级 `~/.codebuddy/CODEBUDDY.md` → 本文件 → `.codebuddy/rules/*.md`(条件规则)→ `D:\github\dsh_shenxian\dsh-server-docs\`(单一来源)。 -> **改本文件需重启才会重载。** +> 不看会致「违规 / 事故」⇒ **必须实体**;只是"更慢更绕" ⇒ 可只给指针。**必须实体**:头部 · 本文 · §1 · §3(红线全表)· §4 · §5 · §6。**可只给指针**(2026-09-24 用户拍板:§7 §8 转引用):§2 · §7 · §8 · §9(卫生 + 目录规范合并章)—— 背景知识 / 环境命令 / 事故详解 / 档案模板 / 历史方案。 +> 加载序:DSH 全局 `AGENTS.md`(`$DSH_HOME/AGENTS.md`)→ 本文件 → `.codebuddy/rules/*.md` → 文档库。🔴 **改本文件下一请求即生效**(⛔ 不必重启;只有 hook / profile / preset 才需**完全重启**)。两层冲突的裁决 ⇒ 全局 `AGENTS.md` **§11**(含两条例外:DSH 宿主机制判据不可被本层覆盖;两套锁各管各的域)。 --- @@ -22,211 +22,306 @@ > **只问「超过现有判断方法边界」的问题。**(用户原话) -- **边界内 → 一律自决,不要问**:技术选型 / 实现路径 / 命名与数据结构 / 性能与资源调参 / 部署与同步 / 排查方法 / 版本与依赖 / 兼容与降级 / 方法内的方案取舍 / 文档与档案的技术内容。 - - ⚠️ **「部署 / 上线」明确属于上面这一项**(2026-09-13 用户纠正原话:「为什么要等我确认才部署呢,**我看线上效果才知道是否满足需求**」):**不中断在线用户**的上线动作(`scp` 产物到 `/opt/dsh/artifacts` + `ensure-biz-plugins.cjs --all` 换包、静态页改动、候选池投放)**做完即上线,不要问** —— 用户要先看线上效果才能判断需求是否被满足。 - - **⚠️ 2026-09-13 用户明令:「这个是开发环境服务器,不用担心中断用户」** ⇒ 上一条的「只有会中断在线用户的动作才走 R8 先知会」**改成**:重启 / 停实例 scope / drain / 改配额或 env / 改 nginx·nft **直接做**,只需**动手前一句话说明**在做什么(可回溯用)。**仍未放开**:不可逆的破坏性操作(删数据 / 迁 DB / 清目录)仍先出清单。 -- **边界外 → 必须问**:① 业务目标与优先级(做不做、先做哪个)② 花钱与资源承诺 ③ 对外承诺(备案 / 资质 / 合同 —— ⚠️ 见下方"技术讨论里不谈法规")④ 需用户提供的账号凭据或审批 ⑤ 无客观优劣的体验偏好(审美 / 文案 / 默认值)⑥ 影响面超出本平台 ⑦ **红线门禁** ⑧ 方法确实判不准。 -- 🔴 **技术讨论里不谈法规**(2026-09-19 用户明令 —— 起因:我在 relay/中继链讨论里反复引 237 号文并据此上抛。用户原话:「**后续禁止再提 237 号文件**」「**他妈老子是找你来普法的吗**」): - **⛔ 不引用法规条文当论据**(含条号、文号、判定"落入/不落入适用范围")|**⛔ 不主动提示合规风险**|**⛔ 不把合规写进技术方案的负面清单或验收判据**|**⛔ 不把"要不要做"的理由挂到合规上**。 - ✅ **只在两种情况下才谈**:① **你主动问起**;② 讨论对象本身就是"**对外承诺 / 资质 / 合同**"这类事。 - ⇒ 技术问题**只按技术判据答**(能力/成本/复杂度/可验证性/运维线);价值判断与后果由**你**决定。 - 📌 范围 = **本工作区全部线**(DSH 覆盖网络线 + 分布式数据链路线 + 后续新线)。⚠️ 与既有「**方案只做技术实现,合规不进方案**」是同一条,本条只是把它**扩到全部线并写成硬规则**。 -- **判据**:这件事有没有**客观可判的优劣**?有 → 自己定;没有 → 问用户。 - ⚠️ **上抛门槛 = 存在「真取舍」**(2026-09-15 用户明令):「**需要我确认的方案需要说明优点和缺点,现在没法判断,假如只有优点或只有缺点那不需要我判断**」⇒ 把候选各写 **优点 + 缺点**:某个**只有优点**(明显更优)或**只有缺点** ⇒ **自己拍掉**;只有**各有优有劣、客观标准分不出高下**才上抛,且**必须逐项列出优点与缺点**。 -- ⛔ **不许捆包**:要问红线就**只问那一句**(要不要现在动生产 / 影响谁 / 断多久 / 能否避开);技术方案自己定好、当**已定项**陈述("我按 X 做…已定,可推翻")。**一轮最多一个问题,同类不连问两次。** -- ✅ **回话前自检(发出任何回复前过一遍 · 2026-09-15 加)** —— 起因:**「提问闸门」hook 只能拦 `AskUserQuestion` 工具,而真实的上抛大多发生在正文里**。 - **实证**:本工作区日志 `tool=AskUserQuestion` 调用数 = **09-12: 43 / 09-13: 3 / 09-14: 0 / 09-15: 0**(09-14 起该工具基本不用)⇒ **hook 那条路径几乎不被走到 ⇒ 拦不到正文里的征询**。 - ⛔ **禁止用征询句收尾**:出现「**要我…吗 / 是否要我 / 需要我…吗 / 要不要我 / 请确认 / 你看怎么办**」时,**先按上一条判据重判三问**: - ① 命中**真门禁**吗(不可逆破坏性操作 / 边界外六类)?**没命中 → 删掉这句,自己做完,改成陈述句**("我接着做 X"); - ② 我是不是在**把已经定下来的事再问一遍**?是 → 删; - ③ 我要问的这件事,**候选之间是「真取舍」吗**(各有优有劣、客观标准分不出高下)?—— 若某个**只有优点 / 只有缺点** ⇒ **自己拍掉**;是真取舍 → 才允许问,且**一轮只问这一句**、**逐项写出优点与缺点**。 - 📌 需要用户拍板时:**位置 = 整条回复的最后一节**(⛔ 不许埋在中间)、**按有序段落逐条编号**、**每个候选必须写「优点 / 缺点」两栏**、**候选竖排成段**(A / B / C **各占一行**,⛔ 不横排、⛔ 不做成表格的列)(2026-09-15 用户明令:「**能根据决策方法 自行决策的就自决策继续处理,不能决策的问题和需确认内容放在回复的最后,按照有序段落展示**」+「**需要我确认的方案需要说明优点和缺点,现在没法判断,假如只有优点或只有缺点那不需要我判断**」+「**每个需要我决策的问题的潜在解决方案 A B C 也按照段落式排版,别横着排列**」);**形态 = 陈述句**(问题 + 各候选优缺点 + 我的倾向),⛔ **不是**甩征询句("要不要我继续" / "说一声即可")。 -- 📐 **排版按 `dsh-feature-first §5.4`**(可扫读九条 + 形态骨架 + 十三条反模式):**首屏 3 行给判定 · 层级 ≤3 · 每节 ≤7 行 · 加粗只留关键词 · 表格 ≤5 列 · 一条信息只说一次**;**待你拍板项落在最后一节、逐条编号、每个候选带「优点 / 缺点」且竖排成段(不横排、不做成表格的列)**;执行信息 / 报障 / 提问**各有现成骨架,不新造**;细节进「技术附录」,正文只留"能决定下一步"的信息。 -- 🎯 **要的是解决问题,不是将就妥协**(2026-09-15 用户明令):面对风险/缺陷**默认目标是解决**;**降级目标 / 延期 / 静默兜底**三种**都不算解决**。只有**客观不可逾越**(技术不可行 / 上游未支持 / 需你提供凭据或窗口)才允许"暂时接受",且必须写明 ① 卡在哪(证据)② 已做到哪一步 ③ **什么条件一出现必须回头解决**。⚠️ 与 §2「最小代价路径」不矛盾:**目标不打折,路径取最小代价**(细节:素材库 **U27 / U26 / A6**)。 -- 🟢 **只做正向迭代**(2026-09-15 用户明令,红线 **R11**):任何改动**只要让项目在某一维度净变差**(目标/方向/架构/功能/性能/安全/交互/UI/便利性/扩展性)⇒ **立即停下复盘**;**拿不出正向做法 ⇒ 立即停止、禁止继续执行**(细节:素材库 **U28**)。 -- 🔀 **规则冲突裁决顺序**(2026-09-16 加 —— 起因:会话 `ddea70b7`「确认guest用户数据迁移」在"106 旧控制面要不要停"上停下来问用户,用户随后**两次**(U5 / U7)自行要求"删除";复盘见 `.workbuddy/memory/2026-09-16.md`): - **同一对象被 §1 / §2 指针 / §3 红线给出相反结论时**,按下序取**首个命中项**,⛔ 不再"自行取保守侧": - ① **R8**(开发环境服务器 ⇒ 该动就动,只需动手前一句话说明)→ ② **§1 边界内自决清单**(部署 / 重启 / 改配置 / nginx·nft / drain / 技术选型)→ ③ **§3 其余红线**(R5 权限扩大 / R7 批量写入 / R9 锁 / R10 uid —— 这几条**永远是硬约束**,不参与裁决)。 - ⛔ **冲突 ≠ 门禁**:两条规则打架**不构成**上抛理由;门禁**只有 §1 列的"真门禁"两类**(不可逆破坏性操作 / 边界外六类)。 - 🔑 **"平台级" ≠ "别人的"**(本次误判的根源):**我们自己的 47 / 106 / 本工作区**上的 `dshs*`·`dsh-*` 单元、`/var/lib/dshs/**`、nginx·nft、端口 —— 都是**本平台自己的资源** ⇒ 按 §1 第 27 行 + R8 **直接做**;R7-边界② 说的"只报告不动手"**只针对"别人的 / 归属不明"的对象**。 - ⛔ **禁止把"我有倾向"降级成"建议 + 待你拍板"**:候选能排出优劣 ⇒ **直接做完**并写一句「我选了什么(可推翻)」。 - 📌 **上抛前必答三问**(任一条足以自决,缺一不可全答"否"才允许上抛):① 对象是**我们自己的平台资源**吗?→ 是 ⇒ 自决;② 我**查证过**关键不确定点了吗(如"还有谁在用")?→ 没查 ⇒ 先查,不许把"不确定"当上抛理由;③ 候选排完序,**第一名是否明显更优**?→ 是 ⇒ 自决。 +- **边界内 ⇒ 自决,不要问**:技术选型 / 实现路径 / 命名与数据结构 / 调参 / 部署与同步 / 排查方法 / 版本依赖 / 兼容降级 / 方案取舍 / 文档技术内容。**「部署上线」属此项 ⇒ 做完即上线,不要问**;生产变更**直接做**、只需**动手前一句话说明**(判据 ⇒ **R8**;详 A1/A2)。 +- **边界外 ⇒ 必须问**:① 业务目标与优先级 ② 花钱与资源承诺 ③ 对外承诺 ④ 需用户提供的凭据或审批 ⑤ 无客观优劣的偏好 ⑥ 影响面超出本平台 ⑦ 红线门禁 ⑧ 判不准。 +- 🔴 **讨论技术不谈法规**:⛔ 不引条文当论据 / 不提合规风险 / 不写进方案与验收判据;**只在你问起、或对象就是"对外承诺 / 资质 / 合同"时才谈**(详 A3)。 +- **判据**:有没有**客观可判的优劣**?有 ⇒ 自决,没有 ⇒ 问。**上抛门槛 = 存在真取舍**:候选**只有优点或只有缺点 ⇒ 自己拍掉**;各有优劣才上抛,且**逐项写优点 + 缺点**(详 A4)。 +- ⛔ **不许捆包**:要问红线**只问那一句**;技术方案自己定好、当**已定项**陈述。 +- ⛔ **禁用征询句收尾**(「要我…吗 / 请确认 / 你看怎么办」)⇒ 按三问重判(真门禁?已定的事又问?真取舍?),没命中就**删掉、自己做完**(详 A5)。 +- 🔴 **上抛 / 待拍板内容必须自包含(发送前就按规范写)**:⛔ **作用域 = 一切「要用户拿主意」的输出**(提问 · 末尾待拍板清单 · 候选 · 表格盘点 —— 换形态不豁免)。四要素:① **问题** —— 一句话说清要决定什么(⛔ 不用指代)② **说明** —— 为什么要你定("影响谁 / 断多久 / 花多少钱")③ 每候选写**优点 + 缺点**,末行给**倾向** ④ 一轮一问;⛔ 不出现包名 / 路径 / 变量名 / 类名(详 A6)。 +**📐 回复排版(🔴 2026-10-01 用户定稿 · 本条覆盖旧版;六轮迭代后定下,照抄即可,⛔ 别自己另发明)** + +- **骨架**:`# 大类`(**已完成** / **待处理任务**)分节 → `## 任务名` 分事 → 每件事写两段:**当前状态** + **待处理事项**。 + 🔴 **大类标题必须比任务名大一号**(2026-10-01 用户加)—— 大类别用**一级标题 `#`**、字号最大;任务名用**二级标题 `##`**;⛔ 不许大类与任务名同号(同为 `##` ⇒ 层级压平、看不出哪几件事属于同一个大类)。 +- **当前状态**:每条一个**圆点**(`- `),**用一句陈述句说重点**;复杂情况、依据、细节放**句末圆括号**里。 +- **待处理事项**:用**序号**(`1、2、3、`,不是 `1.`);每条写完整句子,可以不止一句。 +- **附件**:本板块若有文件要展示或引用,写在**该板块最末**一行(`附件:<路径>`)。 +- **大类顺序**:**已完成的大类放最前**,`待处理任务` 放**最后**。 +- **三禁**:⛔ **表格**(=省略讲理,人要来回跳读)|⛔ **长散文**(不是写小说,整篇不分段=没版式)|⛔ **碎标签堆叠**(`- ` 碎片 + 加粗小标题 + `⇒` 串句)。 +- 🔴 **强遵循(2026-10-02 · 机制保障见 `agent-operating-rules` 技能)**:本节=**每轮硬约束**,命中三禁任一条 ⇒ 该条回复**作废重写**。机器可读副本由技能注入在下方标记块;**每轮注入**由钩子 `reply-style-guard.py` 承担;本环境侧另有逐字探针(`resident-rules.py --check`)。 +- 🔴 **用户原话(五条 · 每次纠正改一版,合起来才是完整口径)**: + ①「**为什么回复的内容 那么人机 把我都看抑郁了,禁止用表格,全部用文字排版**」 + ②「**不是只用句子就行了 要排版 不是让你写小说**」 + ③「**排版不清晰,要有大标题小标题 小标题 多项要段落排版**」+「**段落还有序号**」 + ④「**当前状态:内容如果有多条情况 按无序段落排版**」+「**圆点符号 无序段落每个段落前要加**」+「**当前状态 每个段落用 一句陈述句说重点,有复杂情况可以放在末尾()中**」 + ⑤「**如果有对应附件需要展示或引用 放在对应板块 最后**」 + ⑥「**已完成 待处理任务 这些大类别 用更大字体标题**」(2026-10-01 12:12) +- ⚠️ 本条**只管「给人读的回复」**;注释/日志/解析用字段不受限(给程序读的随便)。 +- 🎯 **要解决问题,不将就妥协**(降级 / 延期 / 静默兜底**都不算解决**);🟢 **只做正向迭代**(= R11)(详 A8)。 +- 🔀 **冲突裁决序**:① **R8** → ② **§1 边界内自决清单** → ③ **其余红线**(R5 / R7 / R9 / R10 **永远硬约束**);⛔ **冲突 ≠ 门禁**。**上抛前必答三问**:对象是我们自己的资源吗?查证过关键不确定点吗?第一名明显更优吗?任一"是" ⇒ 自决(详 A9)。 --- -## 2. 「什么时候去查什么」—— 动作触发的指针(**不要凭记忆答,先查**) +## 1.5 项目开发 · **任务执行关键步骤**(🔴 每次会话必须遵循 · 跳步=违规) -| 当你准备… | 去查 | +> 🔴 **每个会话、每个任务都按本顺序走;⛔ 不准跳步、⛔ 不准"边做边想"。** +> 📖 **全文(逐阶段动作、命令、判据)⇒ `$WS/交付物/项目开发-任务执行关键步骤-20260929.md`**(本节省 = 实体精要,冲突以本文为准) + +### A 开工三件(🔴 **首选=一行入口** · 下列原三步=**入口内部实现**,⛔ 不必手敲) + +🔴 **首选**(把原三步合进 **1 次调用**,2026-10-01 起): + `"$PY" "$WS/scripts/dsh.py" open "<会话名>" <目标文件...>` + ⇒ 入口内部依次跑:① `state.py` ② `preflight-lock.sh` ③ `handoff-guard.sh --claim-exec … --domains ai1net-dsh-server/` + ⇒ ⚠️ **必须带目标文件**(不带 ⇒ 入口**跳过**第②步 preflight);入口 rc≠0 ⇒ **停手 + 报告**(R9) + +🔴 **两条例外(入口做不到 ⇒ 手动)**: + · 目标文件命中 **【E】机制层**(`CODEBUDDY.md` / 钩子 / 技能文件等**全平台共用**的)⇒ 必须**独占**:`bash $DOC\07-scripts/handoff-guard.sh --claim-exec "<会话名>"`(⛔ **不带** `--domains`);抢不到 ⇒ 停手 + · 工作域**不是** `ai1net-dsh-server/` ⇒ 手敲 `--domains <你的域>/` + +(以下 = 入口内部实现,逐字保留供排查对照;**语义未变**) + +1. **跑状态**:`"$PY" "$WS/state.py"`(`--online` 加远端基线)—— 🔴 **跑完之前不许 Glob/Grep 全库摸底**(1 次调用顶十几轮探索) +2. **判可锁定范围**:`bash $DOC\07-scripts/preflight-lock.sh "<会话名>" <目标文件...>` —— 【D】未归类或【E】机制层非空 ⇒ **rc=1 拒开工** +3. **抢锁**(是"抢"不是"看"):`bash $DOC\07-scripts/handoff-guard.sh --claim-exec "<会话名>" [--domains <域>]` —— ⛔ 不带 `--domains` = **退化为独占**(机制层必走)|**抢不到 ⇒ 停手 + 报告**(R9) + 🔴 **默认一律带 `--domains <本工作区域>/`**(如 `ai1net-dsh-server/`);⛔ **别图省事省略** —— 省略 ⇒ **全局独占** ⇒ **别人连域锁都抢不了**(2026-09-29 实测:`482f3cd4` / `9368300d` 两次中途停手,**皆因我持了全局独占锁**)。 + ⇒ **只有真正"全平台共用"的改动**(`config` / `crypto` / `isolation` / 锁与钩子**本身**)才用独占。 + +### B 六阶段(先 → 后 · 每个阶段只列"卡门禁"的那几步) + +| 阶段 | 关键动作 | |---|---| -| **想知道"当前什么状态"(锁 / git 基线 / 待办 / 上次收口点)** | **先跑这一条**:`"E:/ProgramData/.workbuddy/binaries/python/versions/3.13.12/python.exe" "E:/ProgramData/AIProject/aliyun-dsh-server/state.py"`(约 30 行输出;`--online` 追加远端基线比对)。**它 1 次调用顶你十几轮探索** ⇒ ⛔ 跑完它之前**不许** Glob/Grep 全库摸底 | -| 回答"现在是什么状态 / 该读哪篇" | `D:\github\dsh_shenxian\dsh-server-docs\BRIEF.md`(现行事实,30 秒读完);**覆盖网络线**另有唯一入口 = 工作区根 `接续入口_覆盖网络线_20260916.md` | -| **改任何文件之前(第一步,不是"检查"是"抢")** | **先抢全局执行锁**:`bash D:\github\dsh_shenxian\dsh-server-docs\07-scripts/handoff-guard.sh --claim-exec "<你的会话名>"` —— **抢到之前不要动任何文件**;**抢不到 = 有会话在跑 = 停手**。抢到后再跑一次信息模式,看占用与越界改动 | -| 写 / 改前端页面(`web/*.html`、`.css`、client bundle) | `D:\github\dsh_shenxian\dsh-server-docs\01-规范\06-工作台UI规范.md`(**强制基线**;冲突时以其**实测 Token** 为准) | -| **改完 UI(尤其 client bundle)怎么验收 / 为什么"看不到变化"** | **`规范/06-工作台UI规范.md §7`**(生效链路四关 + 三段式验收 + 四个误判)。要点:client bundle 在**实例启动时**加载 ⇒ **改完必须重启实例**;浏览器侧由 dsh 的 `rev`(内容 hash)+ 平台代理层 `Cache-Control: no-cache` 保证自动更新 | -| **"我做完了吗"怎么判(本机改完 ≠ 交付)**(2026-09-15 加 —— 同类已 3 次) | 收尾前逐层走完**生效链路**并在**用户可见面**复验:静态页 → `scp`(⚠️ CDN/CF 缓存坑)|平台 TS → `build` + `systemctl restart dshs`|自研插件 → tgz → 候选池 → 实例启用 → **重启实例**|文档库/技能 → scp + 对账。⛔ **「本机改完了 / build 通过了 / 本地打包完成 / 已 commit」四条都不算交付**(清单:`dsh-change-workflow` **阶段 5 §0 交付门禁**;判据:素材库 **A25**)。⚠️ 但**别回头问"要不要部署"** —— 部署属我 lane 内执行细节(§3 R7-边界 + U20/X9),**直接做** | -| 新建改造档案 | `D:\github\dsh_shenxian\dsh-server-docs\05-交接单/README.md §二`(8 段模板)+ **先原子占号**(`mkdir <目录>/.lock-`) | -| 判断"某功能当时怎么改的" | `D:\github\dsh_shenxian\dsh-server-docs\调整方案/-<主题>.md`(先读**头部** TL;DR / 状态,再决定是否读全文) | -| 部署 / 构建 / 回滚 / 依赖版本 | `D:\github\dsh_shenxian\dsh-server-docs\DEPLOY-本部署.md` | -| 查待办 | 未规划 → `D:\github\dsh_shenxian\dsh-server-docs\01-规范\03-路线图与待办.md §二`;已规划待执行 → `D:\github\dsh_shenxian\dsh-server-docs\05-交接单/README.md §一` | -| **要动生产**(重启 `dshs` / drain scope / 铺插件 / 改实例 env 或配额 / 停别人留下的单元) | ⚠️ **2026-09-16 收口:与 §1 第 27 行 + R8 对齐 —— 直接做,动手前一句话说明在动什么即可,⛔ 不再"取得确认"**(旧表述"先按 R8 说清并取得确认"曾与 R8 本身矛盾,是会话 `ddea70b7` 上抛的诱因之一)。**只有"破坏性且不可逆"(删数据 / 迁 DB / 清目录)才先出清单** | -| **投放任何业务功能插件(第三方 / 自研)** | **只有一种方式:admin 在门户导入候选池 → 用户在实例「功能管理」里自助启用/禁用**(**不铺 profile**)。机制 / 边界 / 回滚见档案 16 / 36 / 65(**§8 有硬性说明**) | -| **推送到服务器之前** | `bash scripts/docs-sync-check.sh`(对账)+ `MINE="<我的文件>" PUSH=1 bash "D:\github\dsh_shenxian\dsh-server-docs\07-scripts\handoff-guard.sh"`(**幽灵文件硬判定**) | -| 做功能需求 / 方案决策 | 加载技能 `dsh-feature-first`(**谁定什么**)+ `dsh-decision-method`(**怎么定得对**,含 §4.4 技术实现裁决顺序)。⚠️ **用户点名「决策方法」/「参考决策方法」/「按决策方法」⇒ 必须立即 `Skill(dsh-decision-method)`,⛔ 不得凭记忆代替、不得只靠本文件 §1 判据**(2026-09-16 实证:会话 `ddea70b7` 用户 U6 明确点名后,AI 全程 `Skill` 调用 **0 次**,仍按旧判据上抛 → 复用本条) | -| **用户问「是否已实现 / 能不能 / 为什么不行」**(2026-09-13 用户要求) | 用 `dsh-feature-first **§5.1 结论骨架**`:**判定 → 为什么不行 → 需你拍板(真需要才写)→ 我接着做(陈述句)**。三条铁律见 §5.3:**主位是用户问的那件事**(AI 的进度/失误/计划不得占前两节)· **结论层零技术标识** · **禁征询式收尾**(已定的下一步直接做) | -| 落地一次改造 | 加载技能 `dsh-change-workflow`(六阶段 + 档案模板 + 并行调度) | -| **登记接续棒 / 排下一棒**(收尾四件套第 ② 件) | 技能 `dsh-auto-handoff-chain` **§3.1.1 排期两条铁律**:① **首个(唯一)接续棒 = 收口 + 5~8 分钟**(⛔ 不是"棒与棒之间")② **每条线同一时刻只挂一个**(下一棒由当棒收官时再排,⛔ 不预登记队列)。⚠️ 两条都已踩过(2026-09-18 用户当场纠正两次) | -| 复盘"用户到底怎么决策的" | `bash scripts/extract-user-voice.py`(抽全部历史会话的用户原话) | -| 判断"服务器文件是否等于我的改前基线" | `git hash-object`(**比 md5 可靠**,不受 CRLF/编码影响) | +| **0 需求识别** | ① **开工前置检查**(先看可用技能列表 ⇒ 有对应**立刻 `Skill(...)`**;再看本机/项目已有资产;装任何东西前自问「**已有的能否满足**」)② 环境盘点 ③ **分清「方案请求」vs「直接执行」** ④ 关键分叉用选项表 | +| **1 调研** | ① 🔴 **报障第一步:先拿真实失败请求**(`journalctl` grep 4xx/5xx,**别猜**)② 只读官方包定位机制(⛔ 不写官方主程序)③ **实测 > 推断** ④ **双端 md5 对账** | +| **2 规划** | ① 方案对比表(≥2 项 + 利弊 + 建议)② 🔴 **红线 R1–R11 逐条自查** ③ 影响评估(命中 R5 另附「权限影响评估」)④ `04-调整方案/` **原子占号**建档 | +| **3 开发** | ① 小步:**读一条 → 改一条 → 验一条** ② 覆盖前**先备份** `bak-<功能>-<日期>/` ③ `npm run build`(零报错)→ `systemctl restart dshs` ④ 本机直连验证 | +| **4 验证** | ① 用例**必须同时覆盖「有请求体」与「无请求体」**(只测 POST 会漏掉 GET/SSE)② 浏览器**只用 `browser-harness`**,**第一步 `list_tabs()`**(不是你开的浏览器 ⇒ 立刻停手)③ 🔴 **交付前必问「出厂了吗」** | +| **5 归档清理** | ① **交付门禁**(见 C)② **四件套(顺序固定)**:`docs-audit` → `docs-index-stats --write` → `docs-manifest` → `docs-sync-check` ③ **三层沉淀**:治本(skill/档案)/失误(append-only 教训)/记忆(`AGENTS.md` 或工作区规则)④ 改技能时**本机 → 服务器单向推** + md5 双端一致 | -> ⚠️ **技能的加载由模型判断相关性,不能保证**。所以:**凡"动作前必须生效"的规则,必须写在本文件里(§1/§3/§4/§5/§6);技能只承载"需要时去拿的方法论"。** +### C 交付门禁(阶段 5 §0 · 逐层自问三句) + +「这次改的是**哪一层**?这一层的**生效链路**是什么?最后一步走了吗、**在用户可见面验了吗**?」 +⛔ **四条"自我安慰"一条都不算交付**:本机改完了 / build 通过了 / 本地打包完成 / 已 commit 了。 + +### D 收尾五件(缺一不算完成) + +🔴 **首选=一行入口**(只合并 ①+清单提醒,2026-10-01 起):`"$PY" "$WS/scripts/dsh.py" close "<会话名>"` + ⇒ 入口内部:`--release` → `--release-exec "<会话名>"`,并打出 ②③④ 的清单提醒(⚠️ 提醒 ≠ 代做:**②~⑤ 仍须本会话逐条做完**) + +① **反序释放锁**(入口内部实现):`--release` → `--release-exec "<会话名>"`;⛔ 不带名 ⇒ 拒释放 ② 台账 / 档案 ③ **提交边界**(未明确要求 ⇒ 不 commit/push;**只 add 自己改的**;⛔ `tmp/`/中间产物/会话交接单 不入库)④ **收口清本棒 `tmp/`** +⑤ 🔴 **【收尾自判】**(2026-09-29 加,**同日按用户意见修正 —— 去掉了自造的去抖协议**):本棒做完后,**顺手**做这一步(⛔ **不是"去叫一个监管会话"**,不产生任何新会话): + 1. **抢域锁当单例**(**既有工具,⛔ 不要自造 `mkdir`/flag**):`--claim-exec "<会话名>" --domains ai1net-dsh-server/` + ⇒ **抢不到 ⇒ 什么都不做**(说明已有人在推进)—— **这就是天然去抖**。 + 2. 抢到则:**读** `automation_runs`(宿主已把各线结论写好)+ `tmp/supervise-inbox/advance.md` ⇒ **判「我这条线」还有没有缺口** ⇒ **有则写下一行 `automations`**(= 派下一棒)⇒ **释放锁**。 + 🔴 **为什么这样才对**:**你(这个会话)本来就在跑**,多花 30 秒判定即可 —— **零额外角色、零额外会话**。 + 旧设计"每线收尾各叫一次监管"会起 N 个会话互抢锁(2026-09-29 实测到"抢锁失败停手"),**已作废**。 + 🔴 全局收敛(V1–V7 全过)/ 线停滞重派 ⇒ 交给**心跳**(时钟由**常驻投递**提供)判 —— ⛔ **不再用宿主排期当钟**(2026-10-01 废弃);⛔ 不必在这里做。 + 🔴 为什么不能靠钩子:**钩子开不了新会话**(自动化才是唯一通道)。派活模板 ⇒ `交付物/多会话协同机制-定稿-20260929.md §4.1`|顶层设计 ⇒ `交付物/多会话协同-顶层设计-20260929.md` + +🔴 **结束语必须对锁状态负责**(写明"已释放",或点名锁在谁手上 + 原因 + 下一步)。 + +### E 常驻的精确边界(⛔ 别一刀切) + +- 🔴 **先记定案**:**「协作与投递一直运行(常驻)」**(09-29 定案,**2026-10-01 用户再确认**;理由「**可能不是所有队列都是钩子产生的**」) + ⇒ ⛔ **不得拿"进程数 0→1"去否它**;⛔ **「自动任务当闹钟」方案 2026-10-01 已废弃**。载体 ⇒ 见末条。 +- ⛔ **禁的是「会干活的会话自己起后台任务」**:输出会**反复唤醒宿主会话** ⇒ 会话永不空闲 ⇒ 用户看到"卡死"(已复现 6 次); + 且它**会压制该会话的 idle 钩子**(实测压 6h20m)⇒ 一切"钩子驱动的唤醒/监管"被无声掐死。 +- ✅ **独立进程不在禁令内**:在**独立窗口 / 计划任务**里跑、**输出不接回任何会话** ⇒ 不唤醒宿主 ⇒ 是项目明列的**正确用法**。 +- 🔴 **但独立进程有一道过不去的坎**:它**不是 WorkBuddy 的后代 ⇒ 读不到 `CODEBUDDY_GATEWAY_PASSWORD`** ⇒ **不能用网关派活**(同 G-C)。 + ⇒ 独立进程**只能做只读判定 / 告警**;**派活仍须由能拿口令的一方(会话/自动化)完成**。 +- ⇒ 🔴 **想要"能投递的常驻",本机唯一可行 = 专用容器会话 + 宿主后台任务 + `stdout` 全重定向到文件** + (`detached` 活不过工具调用边界;`schtasks` 被黑名单硬拦)。要加**别的**常驻,仍先过 + `交付物/多会话协同-顶层设计-20260929.md §7` 判据(是"更早发现"还是"更早推进"?**后者它做不到**)。 + +### F 🔴 新建自动化 = **白名单 + 确认制**(2026-09-29 用户明令) + +**只有两类可以不经确认直接建**: +1. **接续会话** —— 把某条链/某一线的**下一棒接上**(含 §1.5 D⑤ 收尾自判、把断掉的链续上) +2. **给其他会话安排任务** —— 派活 + +🆕 **第 3 类(2026-10-01 用户明令):开工建齐「唤醒 + 按分工类别的协作 + 跟进」三类会话** +> 用户原话:「**开始会话完成需求的时候,主会话需要创建 唤醒会话 以及根据分工类别 创建 协作会话 +> 和 跟进会话呢 不然整个机制跑不起来**」 + +- **它属于"用户当场点名的动作"** ⇒ ⛔ 不算 §F 要防的「自行创建」。 +- 🔴 **三条硬约束**(细则 ⇒ 技能 `session-mechanism` 的 `architecture.md §2.3.0d`): + ① **只有自动化能开新会话** ⇒ "建会话"=**登记一条自动化**,⛔ 不是自己 spawn; + ② 标题必须 **`[角色]-[类别]-<具体>`**,**第 2 级带方括号**、值取 `goal.json` 的 **`topics`** + —— ⛔ 用 `short` ⇒ `parse_session_name()` 认不回 ⇒ 会话**静默漏管**(§2.3.2 表 ④ 实测踩过); + ③ **"还没建"是正常态**(唤醒会话随需求确定时才建),但**一旦开工就必须建齐**,看板上如实显示成灰 ○。 +- ⚠️ **只建这三类** —— ⛔ 别顺手再加监管轮/巡检/定期体检(§F 底部那条反面教材就是这么来的)。 +- ⚠️ **建齐 ≠ 机制跑起来**:投递链(常驻投递)与主会话可响应另算(见 §1 常驻定案)。 + +🆕 **第 4 类(2026-10-02 用户明令):缺会话 ⇒ 自动拉起** +> 用户原话(逐字):「**是用户说 使用协作会话方式 完成目标 或 继续完成目标**」(触发面) +> + 前一句:「应该是…**如果没有 相关会话就自动拉起**」 + +- **触发**:用户说上面那两句(等价说法「继续执行」也认)**或**队列堵住(投递报 `follow-not-live`/`no-follow-session`)。 +- **动作**:先查齐备度 ⇒ **缺 ⇒ 就建排期把它拉起来**,⛔ **不许**把"你去开一条会话"甩给用户 + (旧行为=写 `NEED-USER.md` 喊人开会话,**本条取代它**)。 +- **判据 + 现成参数**:`python <技能包>/scripts/collabd.py --gap [--json]`(**只读**)。硬缺=**跟进/协作**会话没有活着的; + **唤醒**会话算"软缺"(用户定性「随需求确定时才创建」⇒ ⛔ 不算硬缺,否则红多必聋)。 +- 🔴 **建排期只能由「会话」做**(脚本写 `automations` 表=双红线)⇒ "自动"的全部通路= + 钩子把缺口**注入会话**(`wb-result-hook.py::maybe_inject_session_gap()`,`UserPromptSubmit` 零 token)⇒ 会话用 `automation_update` 建。 +- ⚠️ **只建"缺的那三类会话"**,⛔ 别顺手加监管轮/巡检/体检。细则 ⇒ 技能 `session-mechanism` 的 `architecture.md §2.3.0g`。 +- 🔴🔴 **跟进会话=全局唯一席位,⛔ 不按类别各建一条**(2026-10-02 13:5x 用户订正,逐字:「**跟进会话只创建一个, + 跟进的内容来自 协作会话执行完成 后 把 待核对状态 写入 协作队列,上报给那个 固定的 跟进会话处理**」) + ⇒ 一个工作区从头到尾**只应有 1 条**跟进会话;协作/唤醒仍按类别各一条。协作会话收尾=把**待核对状态** + 写进协作队列 ⇒ 交给那固定的一条跟进会话核对并上报,⛔ 不是再开一条跟进会话。 + +⇒ **其余一切用途**(监管轮/巡检/检查点/定期体检/观测/清理/日报…)**必须先取得用户确认**,⛔ **不得自行创建**。 + +🔴 **执行方法**:建之前先自问「**这属于「接续会话」或「派活」吗?**」 +- **是** ⇒ 直接建(一句话把"为什么属于这两类"写进名字或报告) +- **否** ⇒ **先把方案+理由报给用户,等确认**(⛔ 不许先建后报) + +⚠️ **反面教材(2026-09-29 我犯的)**:为"定期检查"自行加了 5 个密集检查点 + 心跳 + 交代轮 ⇒ 用户:「**啥都用自动任务**」。 +⇒ 判据:**看见自己"用建自动化来回应需求"时 ⇒ 先走本白名单;答不上就报给用户。** +✅ **删冗余/清理不属"新建"** ⇒ 可直接做(但要在报告里说明删了什么)。 --- -## 3. 红线 R1–R11(任一条命中 → **先停手**;效率论证不构成豁免) +### G 🔴 阈值到达 ⇒ **自动开接续会话**(2026-10-01 用户定则 · 固化) -| # | 红线 | 要点 | -|---|---|---| -| **R1** | 不自动升级 dsh | 升级须走独立"测试 → 评估 → 修复"流程 | -| **R2** | 不改官方 dsh 主程序与缓存 | `@deepseek-ai/dsh` **零改动**;扩展只走 profile 层官方插件机制 | -| **R3** | client bundle 禁 `exports.default` | 只导出 `apply` + `inject` | -| **R4** | 不用真实账号测登录 | 用临时 session(`mksess.cjs` 直插),用完即删 | -| **R5** | **权限只准收窄** | 凡**扩大**(新挂载 / 放开遮蔽 / 暴露平台目录或 env / 放宽 nft / 提档位)→ 先出「权限影响评估」并取得确认 | -| **R6** | 先查已有资产再动手 | 可用技能 → 本机 / 项目已有技能与记忆 → "本机已有的能否满足" | -| **R7** | **禁未经确认的批量 / 全仓写入** | **只做被明确要求的事**;额外发现的问题**先报告、后动手**;禁全库遍历改写 / 通配符重写 / 批量 `chmod`·`chown` / **批量换行符转换** / `cp -r` 整目录覆盖 / `git add -A`;**可能影响 >10 文件 → 先出清单 + 确认**;先单点验证;**本机不是沙箱**(会经 scp 传导到生产) | -| **R7-边界** | **R7 只适用于「不是我的 lane」—— 两个方向都别套错**(2026-09-13 用户两处明令) | ① **不适用于「我 lane 内的执行细节」**:部署 / 上线(换包、传产物、改静态页、候选池投放)、重启服务、改配置、跑自己的脚本、改自己的插件源码与产物 ⇒ **别拿 R7 当挡箭牌去问,直接做**,事后一句「我选了什么(可推翻)」(原话:「为什么要等我确认才部署呢」)。② **适用于「平台级 / 全局 / 别人 lane」**:`/var/lib/**`、全局符号链接、systemd 单元、nginx·nft、别人的 profile / 产物 ⇒ **一律只报告、不动手**,**哪怕改它能让自己流程跑通**(原话:「谁让你去改这个的」+「不是自己负责的任务相关文件不要去改」)。
⚠️ **2026-09-16 收口(消除与 §1 第 27 行 / R8 的直接冲突):② 的适用对象 =「别人的 / 归属不明」的对象,⛔ 不含「我们自己的 47 / 106 / 本工作区」的资源** —— **本平台自己的** `dshs*`·`dsh-*` systemd 单元、`/var/lib/dshs/**`、nginx·nft、端口 **按 §1 + R8 直接做**(用户 2026-09-15/16 两次要求"删除 106 旧控制面"即为此)。原表述把"平台级 / 全局"与"别人 lane"并列,导致会话 `ddea70b7` 把**自己的 106 节点**读成"别人的东西"⇒ 只报告不动手 ⇒ 上抛。**判据看"归属",不看"是不是平台组件"** | -| **R8** | **中断在线用户的生产变更须先知会** | ⚠️ **2026-09-13 用户明令修正:服务器 `47.77.182.89` 是「开发环境服务器」,不用担心中断用户** —— 重启 `dshs` / 停实例 scope / drain / 改实例配额或 env / 改 nginx·nft **均可直接做**,不必再等确认。仅保留两条最低自律:① **动手前一句话说明**在重启/停了什么(便于出问题回溯)② **破坏性且不可逆**的动作(删数据 / 迁 DB / 清目录)仍先报清单。 | -| **R9** | **⛔⛔ 绝对禁止「人工删锁 / 接管」**(用户 2026-09-12 明令:"严格禁止这类操作") | **AI 一律不得**:`rm -rf 交接单/.exec-lock`、删 `交接单/.doing-*`、或以「持有者疑似已死 / 卡住 / 太久没动 / 只在只读分析没产出」为由**单方面接管**。锁**只能由持有者自己释放**(`--release-exec` / `--release`);**`handoff-guard.sh` 输出里的「或确认接管后人工删锁」不构成授权**。抢不到锁时 AI 的**唯一**合规动作 = **停手 + 报告用户** —— **锁的处置权只属于用户本人**(要删也只能用户自己动手)。**理由**:删锁 = 在**无法验证**对方死活的前提下单方面撤销互斥(**无心跳机制,AI 没有任何判据**)→ 一旦对方仍在跑,就**退回「两个会话同时改同一批文件」**,而这正是这把锁存在的理由。 | -| **R10** | **⛔ 绝不以 root(或非该实例 uid)运行 / 触碰用户实例的东西** | **实证(2026-09-14 事故)**:为量内存**用 root 手动起 admin 的 profile** ⇒ 它以 root 写入 `home/storages/workspace.json`(0.1.5 新增的 `@deepseek-ai/dsh-workspace` 状态文件)与 `home/.dsh/mcn-plugin.db` ⇒ **属主变 root** ⇒ 实例进程(uid 114801)`EACCES` ⇒ `plugin tree failed to load` ⇒ **exitCode 1 崩溃循环 ⇒ 页面 404**。**规则**:① 对用户实例的**一切验证 / 冒烟 / 探针必须以该 uid 运行**(`setpriv --reuid --regid --clear-groups`)或**照平台姿势进 bwrap 沙箱**;⛔ **禁止 root 直跑 `dsh --profile`**。② 确需临时以 root 跑(读全局配置等)⇒ **收尾必须** `find -user root` 列出 + `-exec chown : {} +` 修正。③ 实例「起不来」排查**先看属主 / EACCES**,**不要先怀疑 OOM**(本次先后误判为 OOM,绕了 20 分钟)。**修复手法(实测 1 步恢复)**:`find -user root -exec chown : {} +` → 重启平台 → 页面 200。 | -| **R11** | **⛔ 只做正向迭代:命中「劣化风险」→ 立即停下复盘;确实无正向做法 → 立即停止,禁止继续执行**(2026-09-15 用户明令) | **判据(每次决策前过一遍十维)**:这个改动是否让项目**任一维度净变差** —— **目标 / 方向 / 架构 / 功能 / 性能 / 安全 / 交互 / UI / 便利性 / 扩展性**?
**命中 ⇒ 立即停下复盘**(不许"先做着看"、不许将就):① 写清**劣化在哪一维、代价多大**(证据 / 量级)② 找出**能保住正向收益的做法**(改小范围 / 换实现 / 分阶段)③ **拿不出正向做法 ⇒ 立即停止、不再执行,只报告**。
⛔ **三种伪装禁止**:把劣化说成"必要代价"/用"后续再优化"掩盖已知劣化/把劣化项藏进交付不写。
与 **R5**(权限只准收窄)互补 —— R5 管**权限**,R11 管**全维度净收益**;与 **U27**(不将就妥协)同源。 | +> 用户原话:「**到达阈值后 继续对话自动创建接续会话处理**」 +> +> 🔴 **时机(2026-10-01 用户补定 · 与阈值同等硬)**: +> 用户原话:「**在任务处理的最后 在判断创建会话的事, 不允许在任务中途处理**」 +> **一句判据**:**「建会话」是收尾动作,⛔ 不是中途动作** —— 只有**手头这件事做到头** +> (做完,或达成停手条件:撞阈值 / 受阻 / 需用户拍板)时,才判「要不要开接续会话 / 协作会话」; +> ⛔ **任务进行中一律不许碰它**:不许中途建、不许中途改它的排期、⛔ 更不许中途"为它腾时间"。 +> ⚠️ **反面教训(2026-10-01 实测)**:会话开到一半就把接续会话排到 30 分钟之后「给自己腾地方」, +> 结果任务提前做完 + 本会话又撞日志上限 ⇒ 那 30 分钟**纯空等**(用户当场追问「为什么要等这么久」)。 -## 9. 工作区卫生(2026-09-24 立) +**三条阈值,命中任一即触发**: +1. **上下文用量** —— 本会话上下文 **≥ 220,000 token**(🔴 **用户 2026-10-01 改值**,原 36,000 **作废**)。 + 用户原话:「**上下文取值 220K,另一个会话在解决 日志写入过快的问题**」。 + 🔴 **为什么放到 220 K(而不再是 36 K)**:宿主的历史是**追加式全量重发**(一次工具调用的输出**永久留在历史里、每轮重发**) + ⇒ 越晚换会话单轮代价越高,但**换得越勤则重启开销越多** —— 原 36 K 之所以定得极小,是因为**日志先撞** + (日志 ≈375 KB/分 ⇒ 28 分钟撞 10 MiB,比上下文早 19 分钟);🔴 该瓶颈现已由**另一条线专治「日志写入过快」** + ⇒ 本线**不再按日志反推小阈值**,改按"少交接"取向定 220 K。 + ⚠️ 宿主在 **120 K** 会告警 —— 该点**低于** 220 K ⇒ 只当**中途提示**(可开始留意,⛔ 不必交接)。 +2. 🔴 **会话诊断日志(两档 · 主阈值)** —— `logs/<日期>/sdk/conversations/<本会话 id>.log` 的**字节数** + (`os.path.getsize` 即可,⛔ 不 grep 内容 ⇒ 否则判定本身拖慢会话): + **软档 ≥ 5 MiB** ⇒ **开始写交接材料**;**硬档 ≥ 8 MiB** ⇒ **立刻停手 + 按本节建接续会话**。 + 🔴 **2026-10-01 用户拍板升格**(用户原话:**「B方案」**)—— 本档由"安全兜底"**升为主阈值**,⛔ 不再是"日后再议"。 + 🔴 **为什么硬档是 8 MiB 而不是 10**:宿主上限 10 MiB,须留交接余量;**实测一次交接烧 ≈1.9 MiB** + ⇒ 8 MiB 已是**下限**,⛔ **不得再往上调**(调到 9 以上 ⇒ 交接材料写不完就撞顶)。 + 🔴 与 `collabd.py` 的 **9.5 MiB「已哑」拦截线**是两回事:那道只管"哑了不许再投",这道管"**该交接了**"。 + 🔴 宿主对单会话诊断日志有 **10 MiB 硬上限**,撞上即**拒写(EPERM)⇒ 界面静默哑掉**; + 而协作程序过去对它的投递**仍记 `ok:true`(假绿)** ⇒ 通知全落空而无人知晓。 + (2026-10-01 实测坐实 + 已修:`collabd.py` 增 `_deaf_sids()` 两道闸,见 `memory/2026-10-01.md` §十一) + ⚠️ 换算参考(**仅参考 · ⛔ 不作判据**):正常干活会话日志约 2~3 MB / 20 分钟 ⇒ 5 MiB ≈ 35~40 分钟的量。 +3. 🔴 **工具调用次数(两档 · 与日志同源 · 2026-10-01 用户拍板「B方案」一并采纳)** —— + 本会话**累计工具调用**:**软档 ≥ 200 次** ⇒ 开始收尾;**硬档 ≥ 250 次** ⇒ 停手建接续会话。 + 🔴 **为什么它比 token 更准**:**日志量 ∝ 调用次数**(实测 **152 帧 × 244 B ≈ 37 KB/次**)⇒ + `10 MiB ÷ 37 KB ≈ 283 次` = 一个会话的**物理寿命**(实测撞顶会话 264 次 ⇒ 吻合)⇒ 250 次已留余量。 + ⚠️ 与档 2 **互为冗余**:调用的"次数失控"(同一条命令爆几十次)与"单次输出过大"两条路都能被兜住。 + 📂 阈值设计全过程 ⇒ `memory/2026-10-01.md` §二十四 / §二十七 / §二十九;接续包 ⇒ `接续包_日志事前叫停_20261001.md §4`。 -> 起因:工作区 470 MB 中 411 MB 是过程产物(`tmp/` 1504 件、`待清理/` 1084 件、6 份 33.7 M 的 DB 副本)。 +**触发后四步(顺序固定)**: +1. ⛔ **不在原会话里继续硬撑**(硬撑只会触发上下文压缩、或把日志写爆); +2. **先落盘交接材料**:结论 + 遗留 + 下一步 ⇒ `.workbuddy/memory/<日期>.md` 追加一节,或写接续包; +3. **开接续会话**:走 `automation_update`(🔴 **自动化是唯一能开新会话的通道**,钩子开不了); + 属 §F 白名单「接续会话」⇒ **免确认**;prompt 必须**自包含**(新会话看不到本对话),第 0 步写「跑 `state.py`」; +4. **在本会话明确告知用户**"已交接,请到新会话继续",并**释放锁**(⛔ 带锁结束 = 把所有人挡在门外)。 -- 🔴 **收口必清本棒 tmp** —— 「收尾四件套」加第 ⑤ 件:本棒在 `tmp/` 下的过程目录,收口时自清(或确认无残留)。 -- 🔴 **`tmp/` 保留期 = 7 天** —— 超期目录进 `归档/tmp-<日期>/`;可跑 `state.py --gc` 自动判定。 -- 🔴 **不新建「待清理」类中间态目录** —— 二值决策:**归档**(要留)or **删除**(不留)。中间态 = 拖延。 -- 🔴 **工作区已纳入 git**(`dsh_shenxian_workspace`)⇒ **入库只放文档与文件**:运行态、缓存、`*.db*`、`*.tar.gz`、过程目录一律 `.gitignore`。 -- 🔴 **工作区不保留脚本副本** —— 一律**绝对路径**调文档库 `07-scripts/`(`scripts/` 已撤,2026-09-24)。 -- 🔴 **交界单正文落文档库** `05-交接单/`,工作区只放**指针**。 -- ⚠️ **>60 KB 的单文件**:提交前须逐个判「是否文档 / 是否该提交」(2026-09-24 用户令)。 +⚠️ **触发时机**:以**用户下一条消息到达时判定**为准(钩子 `UserPromptSubmit` 本就会注入机械摘要 ⇒ 顺手带上"本会话已到阈值"即可)。 +🔴 **钩子侧自动检测**= ✅ **已落地**(2026-10-01)—— 载体 `07-scripts/session-log-guard.py`, +挂 `PostToolUse` + `UserPromptSubmit`(宿主**支持 `PostToolUse`**,配置在 `CODEBUDDY_CONFIG_DIR/settings.json`); +只判 `os.path.getsize` 的**字节数**,⛔ **不 grep 日志内容**(否则判定本身拖慢会话);同会话同档只报一次 +(状态落 `tmp/`,**临时文件 + `os.replace`**,⛔ 不用文件锁)。⇒ **软/硬档到达时你会收到一条注入提醒** +(`🟡` / `🔴`,出现在工具结果里)—— **看到即照本节第 1–4 步办**,⛔ 别当背景噪音忽略。 +🔴 ⛔ **不许为此建"定时轮询式自动化"** —— 那属 §F 白名单外的用途,须先报用户。 + +### G·1 🔴 压日志增长的四条硬纪律(2026-10-01 实测口径 · 与本节同源) + +> **为什么单列**:日志是**主阈值**,但"知道该交接"≠"知道怎么别涨那么快"。每条都有本机实测支撑。 + +1. **⛔ 分母不许估** —— 报任何"每次 / 每分"系数前,**分子分母都要量出来**。 + (2026-10-01 我拿"估的调用次数"当分母,把一个系数报大了 3 倍 ⇒ 实测 166 次,我估的约 50 次。) +2. 🔴 **压调用次数是唯一直接杠杆** —— 日志 ≈ **调用次数 × 37 KB**(实测 144 帧/次 × 249 B)。 + 本机实测:某会话 **8 分钟跑 166 次(≈20 次/分)就写掉 6.2 MB**(参考会话 10.5 次/分 ⇒ 慢一半)。 + 手法:**合并命令一次取多个读数**|大输出 `> tmp/x.txt` 后只读关键行(⛔ 不 `cat` 大文件)| + ⛔ 不 `ls -laR`|同一文件**一次读完**|让脚本内部聚合、只 `print` 摘要。 +3. ⛔ **别用"挂钟时间"估算余量** —— 实测:活跃 **630 KB/分** ↔ 空闲 34 分钟 **0 行**(日志只跟"工具在跑"走)。 +4. ⛔ **不许动生产日志一字节**(不改 / 不删 / **不重命名**)—— 它由宿主进程持有; + Windows 上重命名会被占用挡住,且**轮转是否发生由宿主决定**(⛔ 不改官方主程序 ⇒ 属上报项)。 + +--- + +## 3. 红线 R1–R11(任一条命中 ⇒ **先停手**;效率论证不构成豁免) + +| # | 禁令 / 判据 | +|---|---| +| **R1** | **不自动升级 dsh** —— 升级须走独立"测试 → 评估 → 修复"流程 | +| **R2** | **不改官方 dsh 主程序与缓存** —— `@deepseek-ai/dsh` **零改动**;扩展只走 profile 层官方插件机制 | +| **R3** | **client bundle 禁 `exports.default`** —— 只导出 `apply` + `inject` | +| **R4** | **不用真实账号测登录** —— 用临时 session(`mksess.cjs` 直插),用完即删 | +| **R5** | **权限只准收窄** —— 凡**扩大**(新挂载 / 放开遮蔽 / 暴露平台目录或 env / 放宽 nft / 提档位)⇒ 先出「权限影响评估」并取得确认 | +| **R6** | **先查已有资产再动手** —— 可用技能 → 本机 / 项目已有技能与记忆 → "本机已有的能否满足" | +| **R7** | **禁未经确认的批量 / 全仓写入** —— 只做被明确要求的事;额外发现**先报告后动手**;禁全库遍历·通配符改写 / 批量权限·换行符改动 / `cp -r` 覆盖 / `git add -A`;**>10 文件 ⇒ 先出清单 + 确认**;**本机不是沙箱**(scp 会传导到生产) | +| **R7-边界** | **R7 只管「不是我的 lane」**:**我 lane 内细节**(部署 / 重启 / 改配置 / 跑自己的脚本 / 改自己的插件)⇒ **直接做,⛔ 别拿 R7 挡箭牌去问**;**别人的 / 归属不明** ⇒ **只报告、不动手**。🔑 判据看**归属**,不看"是不是平台组件" | +| **R8** | **生产变更**:`47.77.182.89` 是**开发环境服务器** ⇒ 重启 / 停 scope / drain / 改配额·env / nginx·nft **直接做**。两条自律:**动手前一句话说明**在动什么 + **不可逆操作**(删数据 / 迁 DB / 清目录)**先报清单** | +| **R9** | **⛔⛔ 绝对禁止「人工删锁 / 接管」** —— 不得删 `交接单/.exec-lock` / `.doing-*`、不得以「持有者疑似已死 / 卡住」为由**单方面接管**;锁**只能由持有者自己释放**(guard 那句「或确认接管后人工删锁」**不构成授权**)。**抢不到锁唯一合规 = 停手 + 报告用户**(**处置权只属用户本人**) | +| **R10** | **⛔ 绝不以 root(或非该实例 uid)运行 / 触碰用户实例的东西** —— ① 验证 / 冒烟 / 探针**必须以该 uid 运行**或进 bwrap 沙箱,⛔ **禁 root 直跑 profile** ② 确需 root ⇒ **收尾必** `find -user root -exec chown : {} +` ③ 实例起不来**先看属主 / EACCES,别先怀疑 OOM** | +| **R11** | **⛔ 只做正向迭代** —— 判据(十维:目标 / 方向 / 架构 / 功能 / 性能 / 安全 / 交互 / UI / 便利性 / 扩展性):是否让**任一维净变差**?**命中 ⇒ 立即停下复盘**:写清劣化在哪维、代价多大 → 找**保住正向收益**的做法 → **拿不出 ⇒ 立即停止、只报告**。⛔ 禁三种伪装:说成"必要代价" / "后续再优化" / 藏进交付不写 | + +> **R5 / R7-边界 / R8 / R9 / R10 / R11 的解释段 · 事故全过程 · 用户原话 ⇒ 【详】§B-1…§B-5。** --- ## 4. 提交边界 -**未明确要求 → 不 commit / 不 push / 不同步仓库。** 用户说"提交 / 推送 / 同步"时才做,且**只 add 自己改的文件**。 - -**⛔ 三类内容禁止入库(2026-09-21 用户明令:「tmp 交接单 中间产物 禁止提交」)** - -| 类别 | 涵盖 | `.gitignore` 兜法 | -|---|---|---| -| **tmp** | `tmp/`(临时目录) | `/tmp/` | -| **中间产物** | `_tmp_seq*/`、`_中间产物_待清理/` 等 | `/_tmp*/`、`/_中间产物*/` | -| **交接单** | `dsh-server-docs/05-交接单/` 下的会话交接单 | `dsh-server-docs/05-交接单/` | - -- 🔴 **不要用 `git status` 判"交接单要不要提交"** —— 被 ignore 之后它们**根本不会出现**在 status 里; - 这是本条禁令的**预期行为**,不是"没生成"。确需入库只能显式 `git add -f`,且**先说明理由**。 -- ✅ **落点与入库解耦**:交接单**照旧写到 `dsh-server-docs/05-交接单/`**(§5 的 8 段模板不变,全平台仍在同一路径找得到), - 只是**不进 Git**、以本地未跟踪文件形态保留。中间产物则**一律留在 `tmp/` 内**,不要散到仓根。 -- ⚠️ 仓内**已被跟踪**的 `dsh-server-docs/05-交接单/README.md`、`archive/**` **不受影响**(gitignore 不改已跟踪文件),仍可正常更新与提交。 -- 🔴 **提交前自查**:`git diff --cached --name-only` 里出现以上三类 ⇒ **立即 `git reset` 撤出**,不要提交。 +**未明确要求 ⇒ 不 commit / 不 push / 不同步仓库**;用户说了才做,且**只 add 自己改的文件**。⛔ **三类禁止入库**(2026-09-21 用户明令):`tmp/` |**中间产物**(`_tmp_seq*/`、`_中间产物*/`)|`$DOC\05-交接单/` 下的**会话交接单**。 +- ⛔ **别用 `git status` 判"交接单要不要提交"**(被 ignore ⇒ 不出现,属**预期行为**);确需入库只能显式 `git add -f` 并说明理由。⚠️ 已跟踪的 `05-交接单/README.md`、`archive/**` 不受影响。 +- ✅ **落点与入库解耦**:交接单**照旧写** `05-交接单/`(8 段模板不变)只是**不进 Git**;中间产物**一律留 `tmp/` 内**。🔴 **提交前自查** `git diff --cached --name-only` 出现三类中任一 ⇒ **立即 `git reset`**。 ## 5. 规划与执行分离 -规划会话**只产出交接单**(`D:\github\dsh_shenxian\dsh-server-docs\05-交接单/`,8 段必填:目标 / 只读前置 / 范围 / 决策点 / 步骤 / 验收 / 回滚 / 回报格式),**不 ssh、不改码、不重启、不 scp**;落地交另一个执行会话(**不读规划会话的上下文**)。 +规划会话**只产出交接单**(`$DOC\05-交接单/`,8 段必填:目标 / 只读前置 / 范围 / 决策点 / 步骤 / 验收 / 回滚 / 回报格式),**不 ssh、不改码、不重启、不 scp**;落地交另一个执行会话(**不读其上下文**)。 -## 6. 并发纪律(本库多会话并行是常态) +## 6. 并发纪律(多会话并行是常态 · 命令与实证全文【详】§C) -- 共享文件**只用 Edit 做精确片段替换**(失败 = 天然冲突检测),**禁整文件 Write 覆盖**。 -- **并行度按「冲突域是否重叠」定**(**2026-09-22 起,域锁取代"全局只放一个"**):域不重叠 ⇒ **可以真并行**;域重叠 ⇒ 串行。 - ⚠️ **底层框架 / 组件 / 机制层(`config`·`crypto`·`isolation`·`index`·`scripts`·`CODEBUDDY.md`·锁与钩子本身)仍然必须独占** —— 这类改动会打穿所有模块,不参与域并行。 -- **🔒 开工门禁(2026-09-22 用户令:「会话执行任务前先判断,当前任务涉及范围是否都可锁定,确认锁定后开始处理」)**: - 动手前**先判定本次任务涉及的文件是否都能锁定**,再抢锁开工。判定命令: - ```bash - bash D:\github\dsh_shenxian\dsh-server-docs\07-scripts\preflight-lock.sh "<会话名>" <目标文件...> - ``` - 输出【A】可独立锁定 /【B】秒级独占 /【E】机制层(须全局独占)/【C】共享资源 /【D】未归类。 - ⛔ **【D】或【E】非空 ⇒ 脚本 `rc=1` 拒开工**(2026-09-22 实测确认:**【E】也拒**,不是"提示可继续"):【D】= 有文件不在判据管辖内,先归类再动;【E】= 任务是机制层,**全平台共用 ⇒ 不得与其他会话并行**,仅当确认无其他会话在跑时才允许独占开工。 -- **三把锁,顺序固定**(本平台多会话并行的唯一防线): - | 序 | 锁 | 命令 | 管什么 | - |---|---|---|---| - | ① | **域锁**(默认) | `bash …/handoff-guard.sh --claim-exec "<会话名>" --domains <域...>` | **声明我占哪些资源**;域不重叠即可并行 | - | ①' | 全局执行锁(旧行为,兜底) | `bash …/handoff-guard.sh --claim-exec "<会话名>"`(**不带 `--domains`**) | 无法声明域时退化为**独占**;机制层改动必须走这条 | - | ② | 单级占用锁(细) | `bash …/handoff-guard.sh --claim <单号> "<会话名>"` | 这个单归谁做(供台账与占用声明) | - | ③ | 服务器侧操作锁 | `bash scripts/op-lock.sh claim <操作名> "<影响面:谁会被断、断多久>"` | 谁正在动**生产**(重启 / drain / 改 env·配额 / 铺插件 / 改 nginx·nft) | - **域键格式** = `<锚点段>/<下一段>`(如 `src/im`、`dsh-server-docs/scripts`);多个域用**逗号**一次给: - ```bash - --claim-exec "B线-IM开发" --domains "src/im,src/net" - ``` - 🔴 **域键判据在 shell 与钩子两侧必须逐字一致**(锚点表见 `handoff-guard.sh` 的 `_ANCHOR_SEGS` 与 `lock-guard-hook.py` 的 `_DOMAIN_SEGS`)—— ⛔ 改一侧必须同步改另一侧,否则**域锁静默失效(假绿)**。 - 📋 查当前全部锁:`bash …/handoff-guard.sh --locks`。 - **完工反序释放**:先 `--release <单>` / `op-lock.sh release`,最后 `--release-exec`。 -- **秒级独占(`--claim-skeleton` / `--claim-publish`)**:动机制层 / 领取迁移号 / 挂载点等**极短操作**,不必全程持全局锁 —— 抢一把**秒级**锁、做完立刻放。 - | 锁 | 命令 | 用途 | - |---|---|---| - | 骨架锁 | `--claim-skeleton <资源名>` / `--release-skeleton` | 改机制层文件、领迁移号、改挂载点(**秒级**) | - | 发布锁 | `--claim-publish` / `--release-publish` | `commit` / `push` / `scp` / `build` 这类**全局串行**动作(**秒级**) | -- 🔍 **抢锁必须"校验结果",不能"看输出"**(2026-09-15 实证事故):把 `handoff-guard.sh --claim-exec` 的输出**用管道截尾**(`| grep` / `| tail`)时,**失败提示里也含关键词**(如"…必须 `--release-exec` 才算完成")⇒ `grep -q` 会**假命中** ⇒ 于是"以为抢到了"而**在无锁状态下改库**。 - ✅ **正确判据(二选一,缺一不可)**:① **检查退出码**(`if bash "D:\github\dsh_shenxian\dsh-server-docs\07-scripts\handoff-guard.sh" --claim-exec "X"; then … ; fi`,不要接管道);② **复读 `交接单/.locks/<会话名>/DOMAINS`**(域锁)或 `交接单/.exec-lock/OWNER`(全局锁)**并断言是自己的**。 - ⚠️ **`rc=1` 的两种成因要分清**:域冲突(要停手)vs `.gate` 临界区占用(重试即可)—— 看报错正文,⛔ 不要一律当"已完成"。 - ⛔ 事故版形态:`OUT=$(… --claim-exec … | grep 已持…)` —— **grep 吃掉了退出码,也吃掉了"抢不到"这个事实**。 -- ⚠️ **「无锁」的正确读法 =「你快去抢」,不是「可以开工」** —— 2026-09-12 实证:两个会话把 guard 输出的「✓ 无全局锁」读成"环境干净"→ **同时改了本库**(无实际损害,属流程失效)。**看到"无锁" ⇒ 下一个动作就是 `--claim-exec`(带 `--domains`)**;**抢到才是开工许可**。 -- 强制层(可选启用):`scripts/lock-guard-hook.py` + `~/.workbuddy/settings.json` 的 hooks —— **无锁时直接拒写**(见 `调整方案/73`)。 -- ⛔ **抢不到锁就是终点,不是待办**:**不得人工删锁、不得接管**(见 **R9**,2026-09-12 用户明令)。唯一合规动作 = 等持有者自己释放,或**报告用户、由用户本人处置**;AI 不得以任何理由替用户判断"那把锁已经可以删"。 -- ✅ **锁只约束「写」,不约束「读」**(2026-09-12 用户问清):读文档 / 读代码 / 只读命令(`git status|log|diff`、`journalctl`、`ls`、`grep`、只读 ssh)**随时可做,不需要锁** —— 被挡期间照样可以查清事实再报告。 - ⚠️ 但**会改本地状态的命令不算"读"**:`git fetch` / `checkout` / `stash` / `reset` / `switch` 等一律要持锁(它们会写 `.git/refs` 或工作树)。 -- 🔓 **释放时机 = 整个交付闭环走完,不是"改完文件就放"**:`回填台账 → 四件套校验 → commit → 推送 + 对账 → 归档` 全部结束后才 `--release-exec`(**反序**:先 `--release`,最后 `--release-exec`)。 - 理由:中间放锁 = 别的会话可能在你 commit 前挤进来,让**你的半成品被它的提交带走**(本库实证过这类事故)。 -- ⏸️ **持锁期间若需要等用户拍板(等窗口 / 等选择)→ 先释放锁,再等**:锁是"**正在动手**"的凭证,不是"先占着"。挂着锁空转会把所有会话挡在门外;确认完再重新 `--claim-exec`。 -- 🔒 **锁的生命周期 = 任务的生命周期**(2026-09-14 用户明令):**抢到锁的任务,只有"执行完成 → 反序释放"才算完成**;⛔ **禁止"抢到锁、做一半、不解锁就结束回合 / 结束会话"** —— 锁是**独占资源**,带锁结束 = 把其他所有会话挡在门外,而本库**无心跳机制**、别人**没有任何判据**能确认你已停 ⇒ 会被迫空等,或被诱去违规接管(R9)。 - 三条硬性配套:① **抢锁前先把收口步骤列出来**(落地 → 校验 → 推送/对账 → 收尾),别做到一半才发现收不完;② **中途必须停**(等用户拍板 / 等外部窗口)⇒ **先释放锁再停**(上一条);③ **结束语必须对锁状态负责** —— 要么写明"**已释放**",要么**显式点名"锁仍在 ``、未释放、原因、下一步"**(仅限"释放通道不可用"这类极端情形);⛔ **"忘了 / 做不完就走"一律不允许**。 -- 推送前复跑对账:**「仅本地」里若有不在你清单里的文件 → 立刻停手**(幽灵文件);**只推自己本次改的文件**。 -- 单子里的基线数字**必须带取数时间 + 复核命令**,不写死绝对值(会被并行改动打穿)。 -- **`~/.workbuddy/settings.json` 的 `hooks` 段 = 多会话共享配置** —— 多个会话各自加钩子时**只能 Edit 增删条目,禁止整段覆盖**(JSON 顶层键被覆盖会**静默**抹掉别人的钩子)。 - 2026-09-12 实证:两处独立钩子(提问闸门 / 锁闸门)各写一份配置文档,若各自按文档落盘 → **互相覆盖**;已合并为一段(`PreToolUse` 两条 + `SessionStart` 一条)。 - **同一条也适用于用户级 `~/.workbuddy/MEMORY.md`。** -- ⚠️ **钩子命令「会话启动时快照」** —— 改 `~/.workbuddy/settings.json` 里的 hook,**对已在跑的会话无效**,必须**「完全重启」(彻底退出——关窗 ≠ 退出)或新开会话**才加载。**2026-09-13 实测定论**(本会话 06:47 启动 → 06:55 改配置 → 07:05 拆掉临时目录联接后,写操作报的**仍是旧路径**;此前那版「每次调用现读」是被那支临时联接掩盖的**误判**)。 -- ⚠️ **脚本路径失配 = fail-closed**:hook 打不开脚本 → 报错 → 该机**所有会话**的 Write/Edit 全被拒(09-13 实际发生,连改 `settings.json` 本身都被拦)⇒ **迁移 / 改名后第一件事 = 核对 hooks 里的绝对路径**。 -- **应急兜底(本钩子有意的安全阀)**:钩子**不拦 Bash** ⇒ 路径失配期间可用 shell 写文件过渡(09-13 实际走通)。 +- 共享文件**只用 Edit 精确片段替换**(失败 = 天然冲突检测),**禁整文件 Write 覆盖**。**并行度按「冲突域是否重叠」定**:域不重叠 ⇒ **可真并行**;⚠️ **机制层(`config`·`crypto`·`isolation`·`index`·`scripts`·`CODEBUDDY.md`·锁与钩子本身)必须独占**。 +- **🔒 开工门禁**:先跑 `bash $DOC\07-scripts\preflight-lock.sh "<会话名>" <目标文件...>` 判可锁定范围。【A】可独立锁 /【B】秒级独占 /【E】机制层 /【C】共享 /【D】未归类。⛔ **【D】或【E】非空 ⇒ `rc=1` 拒开工**(D = 先归类再动;**E = 机制层全平台共用 ⇒ 仅当确认无其他会话在跑才可独占开工**)。 +- **三把锁 + 两把秒级锁**(全文【详】§C):**域锁**(默认)`--claim-exec "<会话名>" --domains <域键>`,域键 = `<锚点段>/<下一段>`、多个用逗号|**不带 `--domains` ⇒ 退化独占**(**机制层必走**)|`--claim`|服务器侧 `op-lock.sh claim`;秒级 `--claim-skeleton`(改机制层 / 领迁移号)、`--claim-publish`(commit / push / scp / build)。**完工一律反序释放**(先 `--release`,最后 `--release-exec "<会话名>"` —— ⛔ 不带会话名 ⇒ **拒绝释放**)。🔴 **域键判据 shell 与 hook 两侧必须逐字一致**(`_ANCHOR_SEGS` ⇄ `_DOMAIN_SEGS`),⛔ 改一侧 ⇒ **域锁静默失效(假绿)**。 +- 🔍 **抢锁必须"校验结果"不能"看输出"**:① **检查退出码**(不接管道)② 复读 `.locks/<会话名>/DOMAINS` 或 `.exec-lock/OWNER` **并断言是自己的**。⛔ 用 `| grep` 截尾 = **grep 吃掉退出码,也吃掉"抢不到"**。⚠️ `rc=1` 两因分清:域冲突(**停手**)vs `.gate` 占用(**重试**)。⚠️ **「无锁」=「你快去抢」**;⛔ **抢不到就是终点** ⇒ 处置见 **R9**(等释放或报告用户)。 +- ✅ **锁只约束「写」不约束「读」**(读文档 / 读码 / 只读命令随时可做);⚠️ **会改本地状态的命令不算"读"**(`git fetch`/`checkout`/`stash`/`reset`/`switch`)⇒ **要持锁**。🔓 **释放时机 = 交付闭环走完**(台账 → 四件套 → commit → 推送 + 对账 → 归档),**不是"改完就放"**(释放一律带会话名:`--release-exec "<会话名>"`,⛔ 不带名 ⇒ 拒绝释放);**要等用户拍板 ⇒ 先释放再等**。🔒 **锁的生命周期 = 任务的生命周期**,⛔ **禁"抢到锁、做一半、不解锁就结束回合"**。**结束语必须对锁状态负责**(写明"已释放",或点名锁仍在谁手上 + 原因 + 下一步)。 +- 推送前复跑对账:**「仅本地」里有不在你清单的文件 ⇒ 立刻停手**(幽灵文件);基线数字**必须带取数时间 + 复核命令**。 +- **`settings.json` 的 `hooks` 段 = 多会话共享** ⇒ **只能 Edit 增删条目,禁整段覆盖**(覆盖顶层键 = 静默抹掉别人的钩子);**用户级 `MEMORY.md` 同理**。⚠️ **hook =「会话启动时快照」**(对在跑的会话无效 ⇒ 须**完全重启**,**关窗 ≠ 退出**);⚠️ **脚本路径失配 = fail-closed**(Write/Edit 全被拒)⇒ 迁移 / 改名后**第一件事 = 核对 hooks 绝对路径**;兜底 **钩子不拦 Bash**。 -## 7. 环境要点(反复踩过 —— 条数不写死,加完就删旧的) +## 7. 环境要点(反复踩过 —— 加完就删旧的 · 全文【详】§D) -- **本机 bash 的 PATH 常丢**(`dirname`/`grep`/`ls` not found)→ 每条命令显式: - `export PATH="/e/ProgramData/.workbuddy/binaries/PortableGit/versions/1.2.0/usr/bin:/e/ProgramData/.workbuddy/binaries/PortableGit/versions/1.2.0/mingw64/bin:/c/Windows/System32:$PATH"` - (⚠️ **`mingw64/bin` 不能少 —— `git.exe` 只在那里**;2026-09-12 实测:只加 `usr/bin` 时 `git` 仍不可用。`usr/bin` 提供 `ls/grep/dirname/md5sum`。该 Git 安装目录**存在**,此前"路径已失效"的判断有误。) -- **插件安装 / 卸载 / 清理一律走 pnpm**(`pnpm add file:` / `pnpm remove ` / `pnpm install` 自愈;**lockfile 才是账本**,手放 `node_modules` 无效)。 - ⛔ **禁 `rm -rf node_modules/`** —— 手删**不会连带删它的依赖树**,会留下**孤儿包**并让 `node_modules` 与 pnpm 记录不一致。 - (2026-09-12 实测:手删 `dsh-plugin-mcn-suite` 留下了 `xlsx` 的 7 个子依赖,最后靠 `pnpm install` 清掉 **15 个**包才复原。遇到 `pnpm remove` 报 `CANNOT_REMOVE_MISSING`(package.json 里已无该依赖)时,**正确动作是 `pnpm install`**,不是手删。) -- 同名同版本 tgz 改了内容**必须升版本号**。 -- ⚠️ **语法检查别用 `python -m py_compile`**(2026-09-15 实证):它**必然**在脚本旁落 `__pycache__/*.pyc`,而文档库的 `docs-sync-check.sh` 会把它算成「**仅本地(待推送)**」⇒ 污染对账。 - ✅ 用**不落盘**的写法:`python -c "import ast,sys; ast.parse(open(p,encoding='utf-8').read()); print('OK')"`;若已经落了,收尾时 `find <库> -name __pycache__ -type d -exec rm -rf {} +` 并**复跑对账清零**。 -- **会话取证**:dsh 实例会话 = **多帧 zstd**(按 magic `28 b5 2f fd` 切帧);WorkBuddy 自己的会话 = `~/.workbuddy/projects/<目录名>/*.jsonl`。 -- **实例内禁 loopback**(`127.0.0.1` 不可达);取实例页 HTML 需 `curl -L --compressed -c jar -b jar -H "Accept: text/html"`(三条缺一注入就会被 gzip 挡掉)。 -- ⚠️ **本机 → 服务器传文件必须先转 LF**(2026-09-12 实测,差点把 CRLF 带进生产):本机 `D:\github\dsh_shenxian` 的 `core.autocrlf=true` ⇒ **工作树是 CRLF**,而服务器 `/opt/dshs` 是 **LF**。直接 `scp` 会污染生产仓库(脚本照跑,但对账/三方对齐被破坏)。正解:`tr -d '\r' < 源文件 > /tmp/x` 再 `scp /tmp/x`,**只转本次要传的那一个文件**(R7 禁批量换行符转换)。判据用 `od -c` 看行尾(`\n` vs `\r\n`)—— **别用 `grep -c $'\r'`**,在 git bash 里会误报。 -- ⚠️ **本机 Node 是 24,但项目原生模块(`better-sqlite3`)是按 Node 22 编译的** —— 用默认 `node`/`npm` 跑 `npm test` / `npm run verify` 会全线报 `ERR_DLOPEN_FAILED`(`NODE_MODULE_VERSION 127` vs `137`),**看起来像"我改坏了",其实是环境**(2026-09-13 实测)。跑单测/验收一律用 **Node 22**:`E:\ProgramData\.workbuddy\binaries\node\versions\22.22.2-3\node.exe`(服务器本来就是 22.23.2)。 -- ⚠️ **核验"推送是否到位"要用 `git ls-remote origin refs/heads/`**(与本地 `git rev-parse --short HEAD` 对比)。本机两个仓库**都没有 remote-tracking ref** ⇒ `git log origin/main..HEAD` 直接报 `unknown revision`,**别把它的空输出当成"已全部推送"**(2026-09-13 实测)。 +> ⚠️ **本机 bash 的 PATH 常丢**(`ls`/`grep`/`dirname` not found、rc=127)⇒ **每条命令都要前置** PATH 导出(`PortableGit/versions/1.2.0` 下的 `usr/bin` + `mingw64/bin` —— **`mingw64/bin` 不能少,`git.exe` 只在那里**;⛔ 别把 `/c/Windows/System32` 放前面);🔴 **调用间不共享 shell 状态**(命令全文 ⇒ 【详】§D)。 +> ⚠️ 其余六条(转 LF / Node 22 / 禁 `py_compile` / pnpm / 会话取证 / 推送核验)⇒ **全文【详】§D**。 -## 8. 会导致事故的实测事实(**常驻,不许只给指针**) +## 8. 会导致事故的实测事实(6 条速查 · 详解 ⇒ 【详】§E) -| 事实 | 不知道会怎样 | -|---|---| -| **实例权限档位是「会话创建时播种」的** —— 既有会话**不跟随平台默认**(平台默认 `danger-full-access`) | 会误判"平台坏了";更危险的是可能**自动去改档位** —— 那等于把受限会话静默提升为完全权限,**安全语义变更必须用户知情** → 只提示 + 建议新开会话 | -| **功能插件「禁用」= `pnpm remove`(真卸载)**,不是"保留包 + disabled" | 任何**硬绑定 provider** 的覆写段在用户禁用后会指向不存在的 provider;`web.searchProvider` **单选且无回落**,多 provider 又未显式配置 → `WEB_PROVIDER_AMBIGUOUS` 报错(平台已用 `syncWebProviderPatch()` 按当前 bundles 重算解决) | -| **实例配额** = `MemoryMax 384 MiB` / `CPUQuota 150%` / `TasksMax 128`;但 **V8 堆上限按宿主物理内存算(960 MiB)而非 cgroup** | ① `systemd` 的 `MemoryCurrent`/`MemoryMax` **单位是字节、不是 KB**(按 KB 算会放大 1024 倍)② 不注入 `NODE_OPTIONS=--max-old-space-size=256` → 实例会先撞**内核 SIGKILL**(无优雅退出、无日志,排查时无从下手) | -| **业务功能插件(第三方 + 自研)只有一种投放方式** = 「**admin 在门户导入候选池 → 用户在实例「功能管理」里自己启用/禁用**」(用户 2026-09-12 明确:三方插件**一律**按这个处理,**不需要铺什么**) | 若图省事改"直铺"(直接往用户 profile 装包 + 写 provider 覆写段),后果有三:① **绕过用户自决** —— 用户看不到、也关不掉;② 直铺覆写段与档案 65 的平台托管段**同属"整体替换 config"语义** → 两段并存**互相覆盖**,产生**难察觉的配置漂移**;③ 与托管段机制重复建设。**无例外** —— 连 AnySearch 最初走的直铺,也已由用户拍板改回候选池(档案 64 §8.3 修正)。⚠️ **例外只限平台基础设施插件**(`portal-entry` / `business-plugins` / `workspace-scoped-picker`):它们仍是平台级直铺、用户无感,见档案 16「插件三层归属」 | -| 🔴 **实例 home 写文件一律走 `UserFs`**;其文件名**白名单** `HOME_FILE_NAMES`(`src/fs/user-fs.ts`)是「**控制面 + worker agent 两端共用**」 | ① 绕过 `UserFs` 直接 `fs` 写 = **静默空操作**(读回空串、不报错)⇒ 以为"已落盘",实际什么都没有(档案 138 §五)。② 改白名单后**只重启一端**(如只 `systemctl restart dshs`)⇒ 另一端仍判非法,真跑报 `reason=…-unreadable:… bad_path`,**看起来像"功能没生效"**。⇒ 凡改该白名单,收尾清单必含**两个单元**:`dshs` + `dshs-worker` | -| 🔴 **PG 控制面库的 `users` 表身份键是 `id`(text uuid)** —— 同表另有一个 `uid`(**bigint** 序号);`audit_log` 的用户身份列名是 **`actor`** | 写"按 uid 查/删用户"的脚本会命中 `operator does not exist: bigint = text`,或**删不掉还当成功**(本坑 2026-09-20 序47 实测踩到一次)。⇒ 判身份一律用 `users.id` | +> 🔴 **速查(详情 + 后果 ⇒ 【详】§E)**:① 权限档位只在开会话时播种 ② 插件「禁用」= 真卸载 ③ 配额 384 MiB / V8 堆按宿主算 ④ 业务插件只走门户候选池 ⑤ home 写文件走 `UserFs` ⑥ PG 身份键 = `id`。 -> 细节与当时实测:`调整方案/33`(权限档位)· `04-16`/`04-64`/`04-65`(插件与 provider)· `04-58`(内存与配额)。 +--- -## 9. 工作区目录规范(2026-09-19 立 · 详版 = 根 `README.md`) +## 2. 触发词 ⇒ 去查(`$DOC\…`;**全表【详】§F**) -- 🔴 **根目录白名单** —— ⛔ 不许移走,⛔ 不许在根新增散落文件: - `CODEBUDDY.md` · `state.py` · **`接续入口_*.md`** · `README.md` · `scripts/` · `.workbuddy/` · `.codebuddy/` · `.wbapp_*.genie` · 五大结构目录(`docs/` `交接单/` `tmp/` `待清理/` `归档/`)。 - ⛔ **线目录不进根** —— 一条工作线的多份配套方案放 `docs/<线名>/`(同级引用天然有效),冷却后整体移入 `归档/`。 - ⚠️ **`接续入口_*.md` 必须在根** —— `state.py` 用 `os.listdir(工作区根)` 扫描它;移走 ⇒ 新会话读到的**第一个信号就是错的**(事故级)。 -- **落点**:正式文档 → `docs/<主题>/`(覆盖网络 · 集群与实例 · 客户端与桌面 · 会话与接续 · 插件与平台 · 外部接入 · 调研与审计)|交接单 / 接续包 → `交接单/`|一次性脚本与命令输出 → `tmp/<任务名>-<日期>/`|过程目录 → `tmp/历史过程目录/`|不再引用但留痕 → `归档/`|疑似可删 → `待清理/`(**列清单等确认才删**,删除不可逆)。 -- **命名**:正式文档 `<主题>_.md`|线入口 `接续入口_<线名>_<日期>.md`|临时物 `_<用途>.`|过程目录 `_tmp_<序号>/`。日期一律 8 位无分隔。 -- ⚠️ **改写文档内引用路径时,映射键必须收敛到「带 `_YYYYMMDD` 日期戳」的文件名** —— 通用名(`README.md`/`INDEX.md`/`architecture.md`)在任何文档里都可能指别处,映射它必然误伤(2026-09-19 实证)。 -- **路径变更查法**:本次规整(119 项)的「旧 → 新」权威对照 = `tmp/本次整理-20260919/移动对照表.md`;一键回滚 = `python tmp/本次整理-20260919/rollback.py`。 +- **现状 / 该读哪篇** → `$DOC\BRIEF.md`;覆盖网络线入口 = 根 `接续入口_覆盖网络线_20260916.md` +- **UI / 前端** → `$DOC\01-规范\06-工作台UI规范.md`(**强制基线**;client bundle 启动时加载 ⇒ **改完必重启**) +- **"我做完了吗"** → `dsh-workflow` → `references/dsh-change-workflow` **阶段 5 §0 交付门禁**(⛔ 改完 / build 过 / 已 commit **都不算交付**) +- **新建档案** → `$DOC\05-交接单/README.md §二`(先**原子占号**)|**功能怎么改的** → `$DOC\调整方案/-<主题>.md`|**部署 / 构建 / 回滚** → `$DOC\DEPLOY-本部署.md` +- **待办** → `$DOC\01-规范\03-路线图与待办.md §二`/`$DOC\05-交接单/README.md §一`|**服务器文件 = 基线?** → `git hash-object` +- **功能 / 方案决策** → 技能 `dsh-decision`(`references/00-决策方法论.md` = 原 `dsh-decision-method`;`references/01-功能优先协作协议.md` = 原 `dsh-feature-first`);⚠️ **点名「决策方法」⇒ 立即 `Skill(...)`**,⛔ 不凭记忆 +- **「是否已实现 / 能不能」** → `dsh-decision` → `references/01-功能优先协作协议.md §5.1 结论骨架`|**落地改造** → `dsh-workflow`(`references/dsh-change-workflow`) +- **排下一棒** → `dsh-workflow` → `references/dsh-auto-handoff-chain §3.1.1`(收口 + **3~4 分钟**;每线一个)|**复盘决策** → `scripts/extract-user-voice.py` + +> ⚠️ **技能加载由模型判相关性,不能保证** ⇒ "动作前必须生效"的规则必须写在本文件里。 + +## 9. 工作区卫生 / 目录规范(全文 ⇒ 【详】§G + §H) + +- 🔴 **收口必清本棒 tmp**(收尾四件套 + **第 ⑤ 件**);`tmp/` 留 **7 天**,超期进 `归档/tmp-<日期>/`;⛔ **不新建「待清理」类目录**(二值:**归档** or **删除**);⛔ 不留脚本副本;入库口径 ⇒ §4。 +- 🔴 **两条硬判据**:① **`接续入口_*.md` 必须在工作区根**(移走 ⇒ 新会话**第一个信号就错** = **事故级**)② **根目录不许新增散落文件、不许移走白名单内的东西**(白名单 / 落点 / 命名 ⇒ 【详】§H)。 + + + +- **骨架**:拆两层 —— `#` 大类别(已完成/待处理任务)→ `##` 具体事项。每件事先写「当前状态」(每条一个圆点,用**一句陈述句**说重点,依据与细节放**句末圆括号**),再写「待处理事项」(用序号 `1、2、3、`,每条可不止一句)。 +- **层级与顺序**:大类标题必须比任务名大一号;**已完成的大类放最前**,待处理放最后;附件写在**所属板块最末一行**;⛔ 不出四级标题。 +- **首屏**:开头 3 行内先给判定(✅/⚠️/❌ + 一句),细节放后面。 +- **三禁**(⛔ 任一命中 = 该条回复**作废、重写后再发**):**表格** / **长散文** / **碎标签堆叠**。 +- **并列内容竖排**:多个候选、多项并列各占一段、逐条编号;⛔ 不横排、⛔ 不挤进一段、⛔ 不塞成表格的列。 +- **待拍板项**:放回复**最后一节**,逐条编号;每条写清「问题 + 说明(影响谁/断多久/花多少钱/有无不可逆)+ 各候选的优点与缺点 + 倾向」。 +- 🔴🔴 **「变相征询」同样禁止**(2026-10-06 用户据实况追认):凡是**要用户拿主意**的事 —— + 包括「**先只报不动**」「**等你发话**」「我倾向 X,你看呢」这类**不带选项的待定清单** —— + **一律按上一条写成「待拍板项」**(问题 + 说明 + **各候选的优点与缺点** + 倾向)。 + ⛔ 不许用一句"倾向"或"等你发话"**顶替**选项;⛔ 也不许把「没写选项的清单」当成"只是汇报"蒙过去。 + 反过来:**已自决**的事写成陈述句(「我选了什么,可推翻」),⛔ 不要混进待拍板清单里充数。 +- ⛔ **不用征询句收尾**(「要我…吗/请确认/你看怎么办」);能自决策的直接做完,只留一句"我选了什么(可推翻)"。 +- ⚠️ 若**本工作区另有更新的定稿**(环境文件里有更细的排版节)⇒ **以那份为准**,本块是通用形态。 +