Files
dsh_ai1net_server/CODEBUDDY.md
T

233 lines
42 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# DSH 平台项目 — 项目指令(每次会话自动加载)
> **本文件只放两类东西**:① **动作前必须生效的规则**(不常驻就会出事)② **「什么时候去查什么」的指针**。
> 知识型内容一律**不在此复制** —— 按 §2 的触发条件**现查**。
### ⛔ 分层判定标准(防止把该常驻的东西做成指针)
> **这条内容如果不看,会不会导致「违规」或「事故」?**
> - **会 → 必须常驻实体内容**(写在本文件 / MEMORY.md 里,不许只给指针)
> - **只是"更慢、更绕" → 才可以只给指针**
**因此以下内容一律以实体形式常驻,任何"瘦身"都不得把它们降级为指针**:
§1 提问判据 · §3 红线 R1–R10(全表) · §4 提交边界 · §5 规划与执行分离 · §6 并发纪律 · §7 环境要点 · §8 会导致事故的实测事实。
**可以是指针的只有**:平台背景知识、UI 规范细节、档案模板、历史方案 —— 即"需要时才看、看了更准,不看也不违规"的那类。
> 加载顺序:用户级 `~/.codebuddy/CODEBUDDY.md` → 本文件 → `.codebuddy/rules/*.md`(条件规则)→ `D:\github\dsh_shenxian\dsh-server-docs\`(单一来源)。
> **改本文件需重启才会重载。**
---
## 1. 提问判据(唯一一条)
> **只问「超过现有判断方法边界」的问题。**(用户原话)
- **边界内 → 一律自决,不要问**:技术选型 / 实现路径 / 命名与数据结构 / 性能与资源调参 / 部署与同步 / 排查方法 / 版本与依赖 / 兼容与降级 / 方法内的方案取舍 / 文档与档案的技术内容。
- ⚠️ **「部署 / 上线」明确属于上面这一项**(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-边界② 说的"只报告不动手"**只针对"别人的 / 归属不明"的对象**。
⛔ **禁止把"我有倾向"降级成"建议 + 待你拍板"**:候选能排出优劣 ⇒ **直接做完**并写一句「我选了什么(可推翻)」。
📌 **上抛前必答三问**(任一条足以自决,缺一不可全答"否"才允许上抛):① 对象是**我们自己的平台资源**吗?→ 是 ⇒ 自决;② 我**查证过**关键不确定点了吗(如"还有谁在用")?→ 没查 ⇒ 先查,不许把"不确定"当上抛理由;③ 候选排完序,**第一名是否明显更优**?→ 是 ⇒ 自决。
---
## 2. 「什么时候去查什么」—— 动作触发的指针(**不要凭记忆答,先查**)
| 当你准备… | 去查 |
|---|---|
| **想知道"当前什么状态"(锁 / 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-<NN>`) |
| 判断"某功能当时怎么改的" | `D:\github\dsh_shenxian\dsh-server-docs\调整方案/<NN>-<主题>.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/编码影响) |
> ⚠️ **技能的加载由模型判断相关性,不能保证**。所以:**凡"动作前必须生效"的规则,必须写在本文件里(§1/§3/§4/§5/§6);技能只承载"需要时去拿的方法论"。**
---
## 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** | **禁未经确认的批量 / 全仓写入** | **只做被明确要求的事**;额外发现的问题**先报告、后动手**;禁全库遍历改写 / 通配符重写 / 批量 `chmod`·`chown` / **批量换行符转换** / `cp -r` 整目录覆盖 / `git add -A`;**可能影响 >10 文件 → 先出清单 + 确认**;先单点验证;**本机不是沙箱**(会经 scp 传导到生产) |
| **R7-边界** | **R7 只适用于「不是我的 lane」—— 两个方向都别套错**(2026-09-13 用户两处明令) | ① **不适用于「我 lane 内的执行细节」**:部署 / 上线(换包、传产物、改静态页、候选池投放)、重启服务、改配置、跑自己的脚本、改自己的插件源码与产物 ⇒ **别拿 R7 当挡箭牌去问,直接做**,事后一句「我选了什么(可推翻)」(原话:「为什么要等我确认才部署呢」)。② **适用于「平台级 / 全局 / 别人 lane」**:`/var/lib/**`、全局符号链接、systemd 单元、nginx·nft、别人的 profile / 产物 ⇒ **一律只报告、不动手**,**哪怕改它能让自己流程跑通**(原话:「谁让你去改这个的」+「不是自己负责的任务相关文件不要去改」)。<br>⚠️ **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 <uid> --regid <uid> --clear-groups`)或**照平台姿势进 bwrap 沙箱**;⛔ **禁止 root 直跑 `dsh --profile`**。② 确需临时以 root 跑(读全局配置等)⇒ **收尾必须** `find <home> -user root` 列出 + `-exec chown <uid>:<uid> {} +` 修正。③ 实例「起不来」排查**先看属主 / EACCES**,**不要先怀疑 OOM**(本次先后误判为 OOM,绕了 20 分钟)。**修复手法(实测 1 步恢复)**:`find <home> -user root -exec chown <uid>:<uid> {} +` → 重启平台 → 页面 200。 |
| **R11** | **⛔ 只做正向迭代:命中「劣化风险」→ 立即停下复盘;确实无正向做法 → 立即停止,禁止继续执行**(2026-09-15 用户明令) | **判据(每次决策前过一遍十维)**:这个改动是否让项目**任一维度净变差** —— **目标 / 方向 / 架构 / 功能 / 性能 / 安全 / 交互 / UI / 便利性 / 扩展性**?<br>**命中 ⇒ 立即停下复盘**(不许"先做着看"、不许将就):① 写清**劣化在哪一维、代价多大**(证据 / 量级)② 找出**能保住正向收益的做法**(改小范围 / 换实现 / 分阶段)③ **拿不出正向做法 ⇒ 立即停止、不再执行,只报告**。<br>⛔ **三种伪装禁止**:把劣化说成"必要代价"/用"后续再优化"掩盖已知劣化/把劣化项藏进交付不写。<br>与 **R5**(权限只准收窄)互补 —— R5 管**权限**,R11 管**全维度净收益**;与 **U27**(不将就妥协)同源。 |
## 9. 工作区卫生(2026-09-24 立)
> 起因:工作区 470 MB 中 411 MB 是过程产物(`tmp/` 1504 件、`待清理/` 1084 件、6 份 33.7 M 的 DB 副本)。
- 🔴 **收口必清本棒 tmp** —— 「收尾四件套」加第 ⑤ 件:本棒在 `tmp/` 下的过程目录,收口时自清(或确认无残留)。
- 🔴 **`tmp/` 保留期 = 7 天** —— 超期目录进 `归档/tmp-<日期>/`;可跑 `state.py --gc` 自动判定。
- 🔴 **不新建「待清理」类中间态目录** —— 二值决策:**归档**(要留)or **删除**(不留)。中间态 = 拖延。
- 🔴 **工作区已纳入 git**(`dsh_shenxian_workspace`)⇒ **入库只放文档与文件**:运行态、缓存、`*.db*`、`*.tar.gz`、过程目录一律 `.gitignore`。
- 🔴 **工作区不保留脚本副本** —— 一律**绝对路径**调文档库 `07-scripts/<name>`(`scripts/` 已撤,2026-09-24)。
- 🔴 **交界单正文落文档库** `05-交接单/`,工作区只放**指针**。
- ⚠️ **>60 KB 的单文件**:提交前须逐个判「是否文档 / 是否该提交」(2026-09-24 用户令)。
---
## 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` 撤出**,不要提交。
## 5. 规划与执行分离
规划会话**只产出交接单**(`D:\github\dsh_shenxian\dsh-server-docs\05-交接单/`,8 段必填:目标 / 只读前置 / 范围 / 决策点 / 步骤 / 验收 / 回滚 / 回报格式),**不 ssh、不改码、不重启、不 scp**;落地交另一个执行会话(**不读规划会话的上下文**)。
## 6. 并发纪律(本库多会话并行是常态)
- 共享文件**只用 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)。
三条硬性配套:① **抢锁前先把收口步骤列出来**(落地 → 校验 → 推送/对账 → 收尾),别做到一半才发现收不完;② **中途必须停**(等用户拍板 / 等外部窗口)⇒ **先释放锁再停**(上一条);③ **结束语必须对锁状态负责** —— 要么写明"**已释放**",要么**显式点名"锁仍在 `<OWNER>`、未释放、原因、下一步"**(仅限"释放通道不可用"这类极端情形);⛔ **"忘了 / 做不完就走"一律不允许**。
- 推送前复跑对账:**「仅本地」里若有不在你清单里的文件 → 立刻停手**(幽灵文件);**只推自己本次改的文件**。
- 单子里的基线数字**必须带取数时间 + 复核命令**,不写死绝对值(会被并行改动打穿)。
- **`~/.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 实际走通)。
## 7. 环境要点(反复踩过 —— 条数不写死,加完就删旧的)
- **本机 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:<tgz>` / `pnpm remove <pkg>` / `pnpm install` 自愈;**lockfile 才是账本**,手放 `node_modules` 无效)。
⛔ **禁 `rm -rf node_modules/<pkg>`** —— 手删**不会连带删它的依赖树**,会留下**孤儿包**并让 `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/<branch>`**(与本地 `git rev-parse --short HEAD` 对比)。本机两个仓库**都没有 remote-tracking ref** ⇒ `git log origin/main..HEAD` 直接报 `unknown revision`,**别把它的空输出当成"已全部推送"**(2026-09-13 实测)。
## 8. 会导致事故的实测事实(**常驻,不许只给指针**)
| 事实 | 不知道会怎样 |
|---|---|
| **实例权限档位是「会话创建时播种」的** —— 既有会话**不跟随平台默认**(平台默认 `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` |
> 细节与当时实测:`调整方案/33`(权限档位)· `04-16`/`04-64`/`04-65`(插件与 provider)· `04-58`(内存与配额)。
## 9. 工作区目录规范(2026-09-19 立 · 详版 = 根 `README.md`)
- 🔴 **根目录白名单** —— ⛔ 不许移走,⛔ 不许在根新增散落文件:
`CODEBUDDY.md` · `state.py` · **`接续入口_*.md`** · `README.md` · `scripts/` · `.workbuddy/` · `.codebuddy/` · `.wbapp_*.genie` · 五大结构目录(`docs/` `交接单/` `tmp/` `待清理/` `归档/`)。
⛔ **线目录不进根** —— 一条工作线的多份配套方案放 `docs/<线名>/`(同级引用天然有效),冷却后整体移入 `归档/`。
⚠️ **`接续入口_*.md` 必须在根** —— `state.py` 用 `os.listdir(工作区根)` 扫描它;移走 ⇒ 新会话读到的**第一个信号就是错的**(事故级)。
- **落点**:正式文档 → `docs/<主题>/`(覆盖网络 · 集群与实例 · 客户端与桌面 · 会话与接续 · 插件与平台 · 外部接入 · 调研与审计)|交接单 / 接续包 → `交接单/`|一次性脚本与命令输出 → `tmp/<任务名>-<日期>/`|过程目录 → `tmp/历史过程目录/`|不再引用但留痕 → `归档/`|疑似可删 → `待清理/`(**列清单等确认才删**,删除不可逆)。
- **命名**:正式文档 `<主题>_<YYYYMMDD>.md`|线入口 `接续入口_<线名>_<日期>.md`|临时物 `_<用途>.<ext>`|过程目录 `_tmp_<序号>/`。日期一律 8 位无分隔。
- ⚠️ **改写文档内引用路径时,映射键必须收敛到「带 `_YYYYMMDD` 日期戳」的文件名** —— 通用名(`README.md`/`INDEX.md`/`architecture.md`)在任何文档里都可能指别处,映射它必然误伤(2026-09-19 实证)。
- **路径变更查法**:本次规整(119 项)的「旧 → 新」权威对照 = `tmp/本次整理-20260919/移动对照表.md`;一键回滚 = `python tmp/本次整理-20260919/rollback.py`。