Files
dsh_shenxian/dsh-server-docs/skills/dsh-opensource-release/SKILL.md
T
admin 5ad755116e chore(docs): 文档库并入代码仓(R4 选 a)+ 索引/台账跟进
1) dsh-server-docs/ 从工作区(原 E:\...\aliyun-dsh-server\dsh-server-docs)**整体并入本仓**,
   保留目录名 ⇒ 仓库内 dsh-server-docs/... 的相对引用天然继续有效;旧目录(含其 .git)已归档到
   工作区 _中间产物_待清理/,未随本提交带入。
2) .gitattributes:新增 `dsh-server-docs/** -text` —— 原文档库是 `* -text` + autocrlf=false,
   必须保持纯 LF,否则会被本仓的 CRLF 规则翻掉。
3) 活引用里的绝对路径已全部改到新位置(docs 的 INDEX / README / scripts / skills + 用户级 skills
   + ~/.workbuddy/settings.json 的 hooks);历史档案(04-调整方案/、archive/)按「只增不改」未动。
   ⚠️ hooks 路径改动需「完全重启会话」才生效(配置是会话启动快照)。
4) 交接单/T08:新增 §16「生产整体切换执行记录」(形态 / 落地动作 / **4 个只有真上线才暴露的真 bug** /
   验收证据 / 回滚命令 / 残留项);台账 T08 行 → 已完成并归档;03-路线图 §二 登记 T08 收尾项。
5) 统一称谓:**「本机」只指跑 WorkBuddy 的开发机**,47 / 106 一律写「远程服务器」。
2026-09-15 18:47:13 +08:00

529 lines
64 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.
---
name: dsh-opensource-release
description: DSH 多租户托管平台的「开源导出与版本迭代」技能 —— 把私有代码仓导出成可公开的开源副本(脱敏 / 去插件 / 分层授权 / 重写说明文档),并在后续版本里安全地重跑导出、登记版本。当用户说「开源一份」「导出到 GitHub」「发新版本」「改一下开源那份的脱敏/授权/说明」「开源那份同步一下」时触发。核心:**源仓库只读** + **阻断性探针 0 命中**才准放行 + **OVERLAY 手工撰写层**不得被重建抹掉。
version: 1.6.0
updated_at: 2026-09-15
last_change: 1.6.0(2026-09-15):用户立 **K8s 长期策略** —— 「后续获取开发项目代码时**跳过** K8s 相关部分,**就用当前清除/修改后的版本**」⇒ 新增 **R-O14**(源仓那侧视为**废弃分支**;判定标准 = 构建 `blocking hits: 0`),并**修掉会误导未来会话的过期事实**:① §2「保留」列表原写 `deploy/`(11) 与 `poc/`(16) **要保留** —— 与 K8s 政策**直接冲突**(会让未来会话把它们加回来)⇒ 改为「K8s 相关一律不带」+ 指向 `_K8s排除台账.md` / `_k8s_scan.py`;② §0 的 `dsh-web-platform` → **`dsh-users-platform`**、中文母本 → **英文为主(`*.zh-CN.md`)**、首发行 → **v1.1.0 / 153 文件 / 历史已重置为单条提交 `a327649`**、补计数现状(OVERLAY 26 · REQUIRED 54 · INCLUDE_DIRS 6 · EXCLUDE_FILES 9 · DROP_SCRIPTS 8);③ 标注 `K8S_SEMANTIC_RULES`(清「不含 `k8s` 字样但语义已不成立」的描述)与 `_apply_k8s_semantics.py`(本机 `--force` 被安全删除层拦下时的**热应用**替代路径)。1.5.2(2026-09-13):`assets/` 漏收录 + 空括号残渣两处真问题。
agent_created: true
---
# dsh-opensource-release — 开源导出与版本迭代
## 何时用
- 要把 `dsh_shenxian`(私有部署版)导出成可公开的仓库副本;
- 要**发新版本**(v1.0.1 / v1.1.0 …)→ 重跑导出 + 登记版本表;
- 要改开源那份的**脱敏口径 / 保留范围 / 授权结构 / 说明文档**;
- 有人问「开源那份怎么维护 / 怎么保证不泄密」。
**不适用**:日常平台改造(用 `dsh-change-workflow`)、知识库维护(用 `dsh-knowledge-upkeep`)。
---
## 0.5 🔴 事故清单(真发生过 —— **开工前 30 秒读完**)
| # | 事故(真实) | 正确做法 | 详见 |
|---|---|---|---|
| 1 | **误放别的会话的全局执行锁** —— 抢锁失败**没看输出** + `claim`/`release` 写在同一条命令 ⇒ 末尾的 `--release-exec` 是 `rm -rf` 语义,删掉了 `R1-注入层-1104` 的锁 | 抢锁**单独一条命令**并**当场看输出**;看到占用者不是自己 ⇒ 停手;放锁前 `cat 交接单/.exec-lock/OWNER` 确认首行是自己;**误放**则按 `handoff-guard.sh:43` 格式原样重建**并告知用户** | §8 坑 16 |
| 2 | **导出基线在漂** —— 导出脚本复制的是**工作树**,而多会话在并行改源码仓 ⇒ 导出混入**别人未提交的改动**,还会冒出新的需脱敏标识(`'dsh-plugin-mcn-suite'` 触发探针) | 发布前**按 commit 取**(`git archive <commit>`)=冻结版本;否则**每次重建都必须重跑探针**并复核 README 描述与实际一致 | §10 |
| 3 | **盲替 `_build_export.py` 自身** —— 脚本里同时有「源模式」与「目标值」,批量替换把**源模式**改掉 ⇒ 改名规则**静默失效** | 改脚本只用**精确 `Edit`**;改完**必须重建 + `grep` 复查** | §8 坑 9 |
| 4 | **`Dockerfile` / `Dockerfile.dsh` 整份跳过脱敏** —— `is_text()` 按扩展名判断 | `is_text()` 已加特判;**新增无扩展名文件**要复核是否进了脱敏 | §8 坑 10 |
| 5 | **`package.json` 手改被重建覆盖** —— 它属「源派生」而不是 OVERLAY | 项目自有字段(version/description/repository/license)必须写成 **GLOBAL 规则** | §8 坑 11 |
| 6 | **废弃的中间候选名静默残留** —— 探针只探最老的名字,`_overlay` 里的中间名(`dsh-hosting`)漏了 72 处 | **所有曾用名**都进 `LEAK_PROBES` | §8 坑 14 |
| 7 | **说明文档文案连改 7 轮**(替别人宣传 / 开头讲基线 / 议论式表述 / 授权在最前 / 把未验证的当可用 / 致谢太长) | 严格照 **R-O9–R-O12** 写;**写完自审一遍**再交付 | R-O9–R-O12 |
| 8 | **脚本里用键名当标题**(`marks[k][0]` 拿到的是键 `"A"` 不是标题)⇒ 结构改写打歪,误删 `PoC` 的一个字母 | 结构改写用**完整标题字符串**做锚点;改完**读回原文复核**关键块 | 本表 #7 的同一节 |
| 9 | **改工作根时漏改脚本常量 ⇒ 旧目录被"重建复活"**(2026-09-13 迁移到 `dsh-laijing-github` 时:先搬目录、后改 `OUT`,中间跑了一次重建 ⇒ 旧位置被重新建出 135 个文件,且**因旧位置没有 `_overlay` 而缺了 6 个手工层文件**;`_overlay` 本身侥幸没被洗掉) | **顺序必须是:① 改脚本常量(`OUT`+`EX`)→ ② 再搬/删目录 → ③ 重建验证**。迁完**必查旧目录没有复活**(`ls`),并核对 `find <repo> -type f \| wc -l` 与手工层 6 个文件是否都在 | §8 坑 17 |
| 10 | **白名单收录静默漏项:`assets/` 没进 `INCLUDE_DIRS`**(2026-09-13 用户问"确认都同步了吗"时查出)—— 导出的仓库缺 `assets/inject/{recovery,assist}.js`:`proxy.ts` 的 `loadInject` 会 **fail-fast 抛错**(平台起不来)、仓库自带 `scripts/verify-inject.cjs` 也会**判失败**;`package.json` 的 `files` 同样缺 `assets`(npm/git 安装也会缺) | 已补 `INCLUDE_DIRS`、`package.json` files 规则,并**新增 `REQUIRED_EXPORT` 清单(**23 项**)**:缺任何一项 ⇒ **构建判失败**(`return 1`)。以后新增"运行时要读的文件"必须同步加进该清单 | §3 · §8 坑 18 |
| **10** | 🔴 **"顺手清理"的正则把 ASCII 标点也吃进去 ⇒ 直接改坏源码**(2026-09-13 去「档案 NN」时:清理规则写成 `[((]\s*[))]` / `[;;,,]\s*[))]`,**字符类里混了 ASCII `(` `)` `,` `;`** ⇒ 源码里所有 `foo()` 被删成 `foo`,`whitelist.ts` / `security-scan.ts` 当场语法错(tsc 报 *Invalid character* / *Unterminated string literal*)。**而当时 leak 探针全过**) | ⛔ **清理类正则只准碰全角标点(()·、,:;),绝不可把 ASCII 语法符号写进字符类**;<br>✅ **改完必跑 `_verify_tsc.mjs`,`exit 0 + no diagnostics` 是"没改坏代码"的唯一证据** —— **探针全过 ≠ 代码还活着**,两者查的是完全不同的东西;<br>另:`(\s*/\s*` 这类"吃掉前导斜杠"的规则会毁掉 `(/api/x)` 路径,一律不要写 | 本表新条目 · 台账 §八 D |
| **11** | 🔴 **同一个名字既当"脱敏目标"又当"公开值" ⇒ 自相矛盾**(2026-09-13 填 `PUBLISH_*` 时:`maogeigei` 同时在 `GLOBAL`(替换为占位符)与 `LEAK_PROBES`(判泄露)里 ⇒ ① 刚填好的公开值被规则**改回占位符**,② 探针报 `blocking hits: 4`。**"占位符残留 0"是假象**) | ⛔ **任何进入 `PUBLISH_*` 的值,先 `grep -n "<该值>" _build_export.py` 确认它不在 `GLOBAL`/`LEAK_PROBES`/`INFO_PROBES` 里**;升格为公开身份后要**同时**从 GLOBAL **与** 探针移除(只删一处 = 另一种错);<br>公开联系方式的兜底应靠**更具体的探针**(如私有仓库域名 `work.alotbuy`),不要靠账号名 | 台账 §八 D2 |
| **12** | ⚠️ **`--dry-run` 报"假成功"**(2026-09-13 真机预演时:`run()` 会把命令加 `[dry-run]` 前缀跳过执行,**但结果提示语是硬编码的** ⇒ 预演满屏 `✓ 构建完成` / `✓ 服务已启动` / `✓ 部署完成`;更糟的是**打印了一个假的初始管理员密码** —— `bootstrap-admin` 根本没跑,用户会照着登录失败。另:未设域名时文案出现空缺「把 与 *. 的 DNS A 记录」) | **dry-run 必须"只读":既不落盘,也不得宣称成功**。所有**结果类提示**(不只是动作)都要有 `DRY_RUN` 分支;**凡是"执行后才产生的值"(密码/ID/路径)在 dry-run 下必须标为"未生成"**;文案里的变量要有 `${VAR:-默认}` 兜底。<br>**验收方式**:`--dry-run` 的输出里**不允许出现任何 `✓`**,只允许 `[dry-run]` | 台账 §八 E |
| **13** | 🔴 **`_build_export.py --force` 在本机跑不动**(2026-09-14 实测:`shutil.rmtree(DST)` 被 WorkBuddy 的「安全删除层」shim 拦下 ⇒ `SAFE_DELETE_FAIL_CLOSED`;⚠️ 而且 `--force` 会**连导出物里的本地 `.git` 一起删** —— 本轮实测提交历史被吃掉后由会话重新 `git init` 建单条提交) | ① 新规则要**抽成具名列表**(如 `K8S_SEMANTIC_RULES`)再 `REGEX_RULES += …`,不要直接内联 —— 具名才能被热应用工具复用;<br>② 本机改规则后用 `<导出根>\_apply_k8s_semantics.py --write` **就地热应用**(不删任何东西,结果与整目录重建**逐字节一致**,且幂等:复跑命中 0);<br>③ 确实要整目录重建:先 `mv dsh-users-platform/.git <导出根>/_keep_git_tmp`,重建完 `mv` 回来 | 台账 §五·补 |
| **14** | 🔴 **「K8s 残留」只按 `k8s` 字面量清 ⇒ 清不干净**(2026-09-14:字面量已清零,仍有 **10 处注释**在讲 `Pod` / `file sidecar` —— 描述的是**已被移除的 K8s 形态**,属悬空描述) | 判据是**「这段描述在单机形态下还成立吗」**,不是「有没有 `k8s` 字样」:`Pod`(K8s Pod ≠ DSH 子进程)· `sidecar`(已移除的 per-user file sidecar)· `a Linux Pod` 都要清;<br>⛔ **噪音不要清**:`--profile headless`(dsh 自己的 profile 名)· `headless-univer`(第三方插件包名)· `manifest`(npm 包清单,不是 K8s manifest)· `egress`(nftables 出网护栏,本项目**保留**能力);<br>复核用 `<导出根>\_k8s_comment_scan.py [--wide]` —— 它按「**注释 / 代码**」分类输出,可直接核对「只清注释」这件事 | 台账 §五·补 |
| **15** | 🔴 **只删「构建步骤」、没删「使用者」⇒ CI 必红**(2026-09-15 实测:撤下 `Dockerfile.dsh` 后,构建 `dsh:ci` 镜像的那条步骤删了,但 **3 条使用者仍在** —— `Smoke — dsh resolves runtime plugin` / `Trivy — dsh` / `Push dsh to ACR` ⇒ 首次推送 CI 必红) | 判据 =「**产物不在,引用它的步骤也不该在**」:删任何产物(镜像 / 文件 / 模块 / 脚本)时,**必须把它的「使用者」一并处理**(本次已把 3 条步骤整块删除 + 把 `dsh:ci` 加进 `LEAK_PROBES`);<br>⚠️ 复核**务必用 `os.walk` 脚本**(`_k8s_comment_scan.py`),`bash grep -r` 会漏隐藏目录 —— 见 #16 | 影响说明 §7 |
| **16** | 🔴 **`bash grep -r` 不遍历隐藏目录 ⇒ 静默漏掉 `.github/`**(2026-09-15 实测:据此一度误判「CI 没问题」,靠 Read 才看到 `dsh:ci` 仍在) | 全仓复核用**自带脚本**(走 `os.walk`)或给**显式路径**;<br>本机另注:bash 的 `PATH` 会被 shim 重置(`dirname`/`grep`/`awk` 全 `command not found`)⇒ 先 `export PATH=<PortableGit>/usr/bin:<…>/mingw64/bin:/c/Windows/System32:/c/Windows:$PATH`,`python` / `node` 一律用**绝对路径** | 影响说明 §8 |
---
## 0. 事实(硬编码,勿猜)
| 项 | 值 |
|---|---|
| **中文名 / English name** | **DSH 用户平台** / **DSH Users Platform**(2026-09-14 由 `dsh-web-platform` 定稿改名) |
| **文档语言(2026-09-14 定稿,覆盖 R-O13)** | **英文为主**:主文档 `*.md` 为英文 + 顶部中文入口;中文全文在 **`*.zh-CN.md`**;**`manual/` 8 篇 ×2 语言**;架构图也分语言(`diagrams/architecture{,.zh-CN}.svg`) |
| **内容来源(用户 2026-09-13 定的重点卖点)** | **全部代码与文档由 AI 生成** —— 模型 **DeepSeek V4 / V4.1 flash**(**不写工具名**)。README 里有专节 `## AI 生成`(**位于「目录」之后、「亮点」之前**),Hero 区还有**一行声明 + 2 个徽章**(`code & docs-AI-generated`、`DeepSeek V4 / V4.1 flash`)—— **这三处一个都不能少** |
| ~~**文档语言**~~ | ⛔ **本行已作废** —— 2026-09-14 起改为**英文为主**,见上表「文档语言(2026-09-14 定稿)」行;<br>**R-O13 的「中文母本 + `*.en.md`」口径同时作废**,现状是 `*.zh-CN.md` 副本 |
| **技术标识(仓库·包·服务·env 前缀)** | **`dsh-users-platform`** | `DSH_USERS_PLATFORM_*` | `/var/lib/dsh-users-platform`(中文名「DSH 用户平台」/ English「DSH Users Platform」) |
| **曾用名(全部必须在 `LEAK_PROBES` 里)** | `dshs` · `dsh-multitenant` · `dsh-hosting` · **`dsh-web-platform`** · `DSH_WEB_PLATFORM_*` · `/var/lib/dsh-web-platform`;`taimiao` 未落地也一并加入 |
| **前名(已废弃,现为阻断探针项)** | `dsh-multitenant` / `DSH_MULTITENANT_*` / `/var/lib/dsh-multitenant` —— 2026-09-13 20:2x 由用户定名 `dsh-web-platform` 取代;`LEAK_PROBES` 已收录,**出现即判泄露** |
| **源仓库(只读!)** | `D:\github\dsh_shenxian` |
| **工作根(GitHub 开源专用文件夹)** | `E:\ProgramData\AI技能\dsh-laijing-github\`(2026-09-13 由 `aliyun-dsh-server\_开源导出_20260913\` 整体迁入;**本项目的独立开源工作区**,不再是 `aliyun-dsh-server` 的子目录) |
| **仓库根(可直接 git init)** | `<导出根>\dsh-web-platform\` |
| 构建脚本 | `<导出根>\_build_export.py`(**默认拒跑**,须 `--force`) |
| 手工撰写层快照 | `<导出根>\_overlay\`(脚本自动维护) |
| 类型检查 | `<导出根>\_verify_tsc.mjs`(建 junction → tsc → `rmdirSync` 拆) |
| 给人看的台账 | `<导出根>\_导出说明与脱敏台账.md`(**不随仓库上传**;§七 = 改名记录) |
| 手工撰写层(`OVERLAY`:重建时自动快照→恢复,**不得被抹掉**) | **现为 3 份**:`README.md` · **`PLUGIN-PORTING.md`** · `install.sh`(原另有 `LICENSE` / `LICENSE-UPSTREAM-MIT.txt` / `THIRD-PARTY-NOTICES.md`,**2026-09-13 已按用户要求移除** —— 见 §5) |
| 当前版本 | **v1.1.0 / 2026-09-14**,**153 文件**;历史已按用户要求**重置为单条提交**(`a327649`,强推覆盖)| 首发 v1.0.0 / 2026-09-13 |
| 计数现状(2026-09-15) | `OVERLAY` **26** | `REQUIRED_EXPORT` **54** | `INCLUDE_DIRS` **6** | `EXCLUDE_FILES` **9** | `DROP_SCRIPTS` **8** |
| 上游基线(三方) | `上游骨架仓库(已按要求不再具名)` → **MIT**(GitHub 仓库 + DSH 插件目录已收录) |
| 运行期上游 | `@deepseek-ai/dsh`(DeepSeek Harness)→ **MIT** |
| 本机 Python | `/e/ProgramData/.workbuddy/binaries/python/versions/3.13.12/python.exe` |
> ⚠️ **工作根 = `E:\ProgramData\AI技能\dsh-laijing-github\`**(独立 GitHub 开源文件夹)。**不要再改名或迁移** —— 这个名字被 `_build_export.py` 的 `OUT`、`_verify_tsc.mjs` 的 `EX`、本技能、项目 MEMORY 同时引用。真要迁:先改这几处,再**复跑重建 + tsc**,并检查旧目录**没有被重建脚本重新建出来**(迁完曾因漏改 `OUT` 而复活一次)。
---
## 1. 硬规则 R-O1–R-O13(= 用户原始要求 + 实际踩过的坑,任何会话**不得放宽**)
| # | 规则 | 判据 |
|---|---|---|
| **R-O1** | **源仓库只读** | 所有改写只落在导出副本。任何 `git`/写操作碰 `D:\github\dsh_shenxian` = 违规 |
| **R-O2** | **去掉本机 / 服务器信息** | 域名 / IP / 账号 / 绝对路径 / 私有仓库地址 → **占位符或通用化**;收尾探针 **0 命中** |
| **R-O3** | **去掉所有已投放的插件** | 插件包目录 + 插件投放脚本 + 构建产物(`*.tgz`)全部不带;**代码注释里的插件名也要中性化** —— ⚠️ **唯一例外见下表**:`dsh-univer-office` 经用户 2026-09-13 特许保留 |
| **R-O4** | **不带任何项目文档与 skill** | `docs/`、档案类 md、`skills/`、`.workbuddy/`、`lib/`、**`.git`** 一律不带;只留 4 个新写文档(见 §6) |
| **R-O5** | ~~授权:个人/非商业免费 + 商业收费~~ → **2026-09-13 用户定:授权类内容全部移除、不再处理、不再上抛** | 见 §5;**但「上游 MIT 不得被附加限制」是事实,保留** |
外加**操作纪律与文档口径(R-O6–R-O12)**,每条都对应 §0.5 里的真实事故:
- **R-O6** 未经明确要求 **不做 `git init` / commit / push / scp**(同项目 §4)。
- **R-O7** 导出的**手工撰写层**(README / LICENSE / NOTICE / PLUGIN-PORTING / install.sh)是资产,**不得被重建抹掉** —— 靠 `_overlay` 自动快照恢复。
- **R-O8 · 命名** —— **绝不沿用三方项目名**(详下)。
- **R-O9 · 能力宣称** —— **只写「已验证的」**:有代码 + 有单测 + 有 PoC **都不等于可用**;未验证的标「实验性 · 未验证」(详下)。
- **R-O10 · 出处** —— 只放**文末**,且**只写一行致谢**;不写骨架枚举、不写自我表扬、不写改名理由(详下)。
- **R-O11 · README 内容口径** —— 开头写**重心**不写门槛 · **面向读者**不写议论 · 每条亮点**带机制与数值** · 亮点**先读码再写**(详下)。
- **R-O12 · README 结构顺序** —— 认知漏斗 Hook→Onboarding→Content→Trust→Meta · **授权压轴** · Hero 前 50 行有可视块 · TOC 锚点机检(详下)。
- **R-O13 · 双语文档** —— **中文是母本**;英文版**在同步 GitHub 之前**才生成(`*.en.md`),两份顶部再加语言切换行;法律文本不翻译(详下)。
### R-O8 · 命名规则(2026-09-13 立,因踩过)
> 上游 `dshs` **是 上游作者(已按要求不再具名) 的三方项目**(GitHub 仓库 + **已被 DSH 插件目录收录**,含 L1–L5 验证记录)。
> 沿用它当发布名 = **冒名 / 指代混淆**,还会让上游作者的工作被误认作本项目产出。
**发布前必须做三件事**:
| # | 动作 | 判据 |
|---|---|---|
| 1 | **起自己的名**,并**实测未被占用**(npm `registry.npmjs.org/<name>` + 插件目录 `dshbase.com/plugins/<name>`,两处都 404 才可用) | 本项目命名口径(用户 2026-09-13 定):中文 **DSH Web 平台** | English **DSH Web Platform** | 技术标识 **`dsh-web-platform`**(短横连写用于仓库/包/服务/env 前缀)。<br>⚠️ **已排除的两个候选**:**`dsh-hive`**(第三方插件 `llluchy/dsh-hive` 已占用);**`dsh-hosting`**(本会话中间候选,已被用户口径取代 ⇒ **仓库里不得残留**,已列入阻断性探针) |
| 2 | **改完全部自指标识**:包名 / bin / cordis plugin id / env 前缀(36 种)/ 数据根 / systemd 单元 / nginx conf / 库文件名 / 镜像与 k8s / 导出目录名 | 全仓 `grep` 旧名,**只剩出处引用**才算干净 |
| 3 | **保留出处并正式致敬 —— 但只放在文末**(用户 2026-09-13 明确:"在最后提一下引用了谁就行,不要上来就重点讲用了谁") | ① `LICENSE-UPSTREAM-MIT.txt` **逐字节不动**(MIT 强制);② README **不得在开头单独设「名称与渊源 / 基于 XX」章节** —— 出处只出现在**文末**的「第三方组件与致谢」里,**一句话带过**;③ 法律归属由 `LICENSE` 第一层说明 + `THIRD-PARTY-NOTICES.md` §4 承载(这两处必须完整,不受"轻描淡写"影响) |
⚠️ **改名时最容易误伤的两处**:① 把「致敬上游」的引用一起改掉(= 抹掉出处,法律与道义双重问题);② 把脚本里作**源模式**的旧名改掉(见 §8 坑 9)。
### R-O9 · 只写「已验证的」能力
> 用户原话:「模式 B · Kubernetes **去掉这个,根本没验证**,应该是**待开发验证**」。
| 规则 | 做法 |
|---|---|
| **宣称必须可复现** | 只把**真正跑过端到端验收**的路径写成「可用」。**有代码 + 有单元测试 + 有 PoC 记录,都不等于可用** |
| **未验证的照实标注,不删代码** | 保留代码与清单,但标注 **「实验性 · 未验证」**,并与可用路径**在同一张表里用状态行对照**,不要让读者自己猜 |
| **删掉「已完整落地 / Phase 0–4」式表述** | 这类词最容易把"写完了"说成"验证过了"(2026-09-13 从「部署形态」章节删掉的就是它) |
| **配置表 / 脚本文案同步标注** | 未验证路径的 env 变量统一加「(模式 X · 未验证)」前缀;`install.sh` 的报错/提示文案要与 README 口径一致 |
| **FAQ 给一句直答** | 加一条「X 现在能用吗?」→ 直接回答「**还不能**」,并说清**缺哪一步**(如「从未做过端到端部署验收」) |
| **归属措辞也要改** | 出处/致谢里描述对方贡献时,把「双部署**形态**」降级为「双部署**框架**」,避免暗示未验证的路径是可选项 |
| **落点清单(照抄)** | README:部署形态表 + 快速开始 + 配置表 + 功能详解 + 安全模型 + 亮点 + 目录结构 + FAQ;`install.sh` 文案;`LICENSE` 与 `THIRD-PARTY-NOTICES.md` 的归属措辞 |
---
---
### R-O10 · 出处的位置与措辞
> 用户原话:「在最后提一下引用了谁就行,**不要上来就重点讲用了谁**,感觉是在宣传别人的项目,已经很多地方深度改造过了。」
| 规则 | 说明 |
|---|---|
| **位置** | 只在**文末**(README 的「第三方组件与致谢」+ `LICENSE` 第一层 + `THIRD-PARTY-NOTICES.md` §4)。**不得**在开头/前部单设「名称与渊源」「基于 XX」章节 |
| **主语** | 正文一律**以本项目为主语**。写「本项目做了…」,**不要**写成「上游提供了骨架,我们在此基础上…」这种自我降格的框架 |
| **措辞** | 用「**起步时参考了** X(作者,许可证)—— **感谢作者开源**」;**不要**用「仅仅接着往前走了一步」「离开它就没有这个项目」这类过度抬举,**也不要**补「我们做了大量深度改造」这类**自我表扬**(见下表「致谢只写一行」) |
| **边界** | 「轻描淡写」**只作用于 README 正文**;MIT 要求的版权与许可声明(`LICENSE-UPSTREAM-MIT.txt`)与授权分层说明**一处都不能省** |
| **JS/注释里的名字** | 代码注释里也**不主动**出现上游名(它们已经被 R-O8 第 2 步改成新名;只剩出处语境才允许) |
| **致谢只写一行**(2026-09-13 第七次纠正) | 出处行 = **「起步时参考了 X(作者,许可证)—— 感谢作者开源」**,一行结束。**不要**写:① 骨架范围的枚举(那是 `LICENSE` 第一层与 `THIRD-PARTY-NOTICES.md` §4 的职责,**重复即冗余**);② 「此后我们做了大量深度改造」(**自我表扬**,致谢不是讲功绩的地方);③ 「项目名从它的 X 换成我们的 Y(避免指代混淆…)」(**内部事务**,读者不关心,改名说明留在 NOTICE 的「更名说明」行即可)。用户原话:「**有必要讲这么多吗,好好想想**」 |
| **别解释我们为什么这么写** | 「—— 这是 MIT 的硬性要求,必须保留」「因此 README 与 install.sh 一律以模式 A 为准」这类**关于文档自身**的话术删掉;正文只陈述事实与规则本体 |
| **指针行不重复** | 「机制见亮点」这类导航指针**全文留 1–2 处**(Hero + 功能详解)即可,Content 各节末尾各来一句 = 冗余 |
---
### R-O11 · README 内容口径
> 🧭 **体例 = 说明书,不是技术文档**(2026-09-13 20:4x 用户定):「readme 是说明不是技术文档 用语需要简明扼要 排版要方便观看」。
> ⇒ 只写**怎么用 / 有什么 / 限制**;机制级细节(链名、flag、常量、函数语义)留给代码注释与 `PLUGIN-PORTING.md`;
> ⇒ **不写内部变更日志式长表**(如「v1.0.0 的六组改造」);表格单元格要短、可扫读;多用列表少用长段落。
> 用户原话:「部署到一台服务器,多个用户注册并经管理员审核,各自获得一套相互隔离 —— **这个只是项目的基础不是重点**,重点是多租户各自进程的安全隔离、状态恢复、插件和技能的管理。**多看看项目找出这个项目的亮点**」「对了多想想 **不要弄了半天亮点都体现不出来**」。
| 规则 | 做法 |
|---|---|
| **开头 = 亮点,不是门槛** | 开篇**不要**写「部署到一台服务器,多个用户注册…」这类「能跑」描述(那是基线)。改成**一句定位 + 三条主线速览**:① 进程级安全隔离 ② 故障自愈与状态恢复 ③ 插件与技能的受控管理 |
| **亮点必须先于功能清单** | README 的第一个正文章节就是**亮点**(`## 亮点`),且**要早于**「快速开始 / 功能详解」。功能清单只做**索引**,并在开头声明「机制见亮点」 |
| **每条亮点必须带机制** | 禁止「真隔离」「会自愈」这类空词。每条要写**具体机制 + 关键数值/常量 + 为什么这么设计**(例:端口守卫要在 OUTPUT 链按**客户端 uid** 匹配、且 `-I OUTPUT 1` 插链首,否则 ufw/conntrack 的 ACCEPT 会先吃包;宿主不支持就 fail loud) |
| **先读代码再写亮点** | 亮点**必须来自实际读码**(`src/**` 的头注与关键函数就写得很好),不要凭 README 旧文或印象复述。读法:`find src -name '*.ts' \| xargs wc -l \| sort -rn` 找最重的模块 → 读头注 → 再挑 3-5 个「反直觉且踩过坑」的细节 |
| **「难在哪」心里有数(但别写进 README)** | 每条主线要能回答「为什么这不容易」(租户之间**真的**隔得住 / 崩溃后**无感**恢复 / 装了**不出事**)—— 用于**决定亮点写什么**;⚠️ 但「多租户演示十分钟就能写出来」这类**对比式议论本身不许出现在 README**(见下表「面向读者」) |
| **补一节工程纵深** | 用「双后端同接口 / 关键决策是纯函数 / `npm test` 覆盖 + 注入脚本运行时校验」这类事实回答「凭什么信这些机制」 |
| **面向读者,不是面向作者**(2026-09-13 第三次纠正) | README 是给**使用者 / 贡献者**看的。禁止出现「**重点不是 X,而是 Y**」「X **十分钟就能写出来**」这类**议论式 / 对比式**表述 —— 那是在跟作者对话,读者不关心。直接陈述「**项目做什么 + 重心在哪**」即可 |
| **小节标题别加议论** | 标题只写名词短语(`### 一、进程级安全隔离`),**不要**写成 `(不是「同机不同目录」)` `(「能装」和「装了不出事」是两件事)` 这种反问/抖机灵。对比与理由放到**表格单元格**里当技术说明 |
| **别重复定位句** | 标题下的一句 tagline 与 badges 之后的描述段**不要重复同一个句式**(「把 DSH 变成…」说了两遍)。tagline 一句话,描述段讲**用户视角的事实 + 三条重心** |
---
---
### R-O12 · README 结构顺序
**依据**:`readme-craft`(SkillHub 技能,蒸馏 awesome-readme / Standard README / Art of README / Make a README / GitHub Docs / thoughtbot)+ `standard-readme`。核心是**认知漏斗**:**Hook → Onboarding → Content → Trust → Meta**,**不得倒序**。
**本项目定稿顺序(Hero + 16 节)**
```
标题 → 一行描述 → 徽章(**5 个**:Node · Version · built on DSH · AI-generated · DeepSeek V4/V4.1 flash;License 徽章已随授权类内容一同移除)
→ Hero(三条主线 + **一行「全部代码与文档由 AI 生成」** + 拓扑图)→ 快速开始片段
目录 → **AI 生成** → 亮点 → 快速开始 → 功能详解 → 架构 → 安全模型 → 部署形态 → 配置
→ 控制面 API → 插件移植指南 → 开发 → 常见问题 → 目录结构 → 版本与迭代 → 贡献
→ 第三方组件与致谢 → 授权与商业使用(必须最后)→ 授权摘要收尾(全文唯一一处摘要)
```
> ⚠️ `## AI 生成` 是**第 1 个正文章节**(用户 2026-09-13 明确「还有个重点要加载前面」)—— 它是这个项目的**核心差异化**:整站代码与文档由 AI 写成。**别把它挪到后面,也别只留徽章不留正文**。
| # | 硬规则 | 理由 |
|---|---|---|
| 1 | **授权章节放最后** | 6 个权威来源共识 + readme-craft 的 Trust 评分项就是「License 在最后?」 |
| 2 | **授权信息不要出现在前排正文**(2026-09-13 用户纠正):靠**顶部 License 徽章**承载(本项目 = `license-personal free \| commercial paid`,并链接到末节)+ **文末**「授权与商业使用」章节尾的一行摘要。**不要在徽章下方另加一句授权 blockquote** —— 用户明确「说明文档还是在前面」不接受 |
| 3 | **Hero 区(前 50 行)必须有可视块** | 审计项 H3 占 7 分(拓扑图 / 截图 / GIF 任选) |
| 4 | 一行描述 **< 120 字符**,且与 `package.json` 的 `description` **开头一致** | 审计项 H2 占 8 分,单项最高 |
| 5 | 超 100 行**必须有 TOC**,且**锚点逐条可解析** | 自查法:把标题按 GitHub 规则转锚点(小写 / 去标点 / 空格→`-`)后与 TOC 比对 |
| 6 | 功能用**表格/列表**不写散文;**清单型章节要声明「机制见亮点」** | 避免 C1 扣分与 S3 信息重复 |
| 7 | **API 概览必须从代码抓真实路由**(`grep -oE "app\.(get\|post\|put\|delete)\("`),别凭印象写 | 审计项 C3;写错路由比不写更糟 |
| 8 | 章节重排要用**脚本按标题切分再拼装**,重排后**复跑 TOC 校验** | 手工搬章节极易丢内容 |
| 9 | **表格单元格里不要写裸 `\|`**(如 `GET\|POST`)—— 会截断表格,改写「(`POST` 同形)」 | 格式错误扣 P2 |
| 10 | 发布后补 **CI 徽章** 与**真实联系方式**;上线前不要加 CI 徽章(占位符 = 死链) | T3 / T4 是唯二「因尚未发布」而扣分的项 |
**审计口径**(readme-craft 22 项 / 100 分):Hook 25 · Onboarding 25 · Content 20 · Trust 15 · Structure 10 · Polish 5;等级 **S**≥90 / **A**≥80 / **B**≥70 / **C**≥60 / **D**<60。
**本项目首轮自审 = 93/100(S)**,扣分仅:CI 徽章 0/3 · 维护者信息 0/3(`<CONTACT_EMAIL>` 未填)· S3 信息重复 2/3。
---
### R-O13 · 双语文档(2026-09-13 用户定:**中文优先,英文在同步 GitHub 时生成**)
> 用户原话:「文档别忘了**中英文双语**,后续**优先写中文**,需要同步到 GitHub 时再更新英语。」
| 项 | 口径 |
|---|---|
| **母本** | **中文**(`README.md` / `PLUGIN-PORTING.md`)。**日常只维护中文**;英文文件**不要求随时同步**,避免双份维护成本 |
| **英文文件名** | `README.en.md`、`PLUGIN-PORTING.en.md`(与中文同目录,后缀 `.en.md`) |
| **生成时机** | **发布 / 推送 GitHub 之前那一步**(SOP 的 5.5)—— 也就是 §7 验证之后、§6 交付之前 |
| **语言切换行** | 两份文件**顶部都要有**:中文版写 `[中文](README.md) · [English](README.en.md)`;英文版写 `[English](README.en.md) · [中文](README.md)`。⚠️ **英文文件存在之前不要加**(否则是死链,扣 P1) |
| **翻译时严格保持** | 代码块、命令、路径、env 名、包名、URL、图(ASCII/Mermaid)、表格结构与「版本与迭代」表内容 —— **逐字保留**;占位符(`<YOUR_GITHUB_ACCOUNT>` 等)**同步替换** |
| **不翻译(保持原文)** | `LICENSE-UPSTREAM-MIT.txt`(MIT 英文原文,**逐字节**);`LICENSE` 保持**中文正文 + 英文摘要**(现状即如此);`THIRD-PARTY-NOTICES.md` 中文即可(包名与许可标识本就是英文) |
| **校验** | 英文版同样要跑 **TOC 锚点校验**(按英文标题算锚点)与**相对链接存在性**;两版的「版本与迭代」表**行数与版本号必须一致** |
| **别忘** | 这是**发布前唯一会因为"没做"而显得半成品**的项 —— 写进 SOP 与 §10 待办,接手先看 |
---
### R-O14 · 🚫 K8s 相关内容:**源仓那侧视为废弃分支**(2026-09-14 定稿;2026-09-15 用户升级为**长期策略**)
> 用户原话(2026-09-15):「记住这部分和 K8S 相关的代码 **后续获取开发项目代码时 跳过就用当前清除或修改后的版本**」
| 规则 | 判据 |
|---|---|
| **① 不回流** | 源仓的 K8s 资源 / 文档 / 注释 / 语义描述 / K8s 专属配置,**不得**因源仓更新而重新进入导出物 |
| **② 不重复实现** | 导出层已有的清除与改写就是**唯一口径**,不要每次重新发明 |
| **③ 改了源仓也不跟** | 源仓若再改 K8s 那段代码,**以导出层版本为准**(源仓那侧视为**废弃分支**) |
| **判定标准** | 构建输出 **`blocking hits: 0`**;不为 0 = 回流了 ⇒ 按台账 §四 处置 |
| **权威清单** | **`_K8s排除台账.md`**(本工作根)—— 含「下次取新版本怎么做」的步骤 + 判读表 + 噪音清单 |
| **扫描器** | **`_k8s_scan.py`**(`--source` 扫源仓 / 无参数扫导出物;**EXIT=1 = 有未覆盖项**) |
**三重强制手段**(缺一会静默失效):
`INCLUDE_DIRS`/`INCLUDE_FILES` 白名单 → `EXCLUDE_FILES`/`DROP_SCRIPTS` → `REGEX_RULES` ⑦⑧⑨ + **`K8S_SEMANTIC_RULES`** + `LEAK_PROBES`。
**已清到 0(源码与注释)**:62 → 0;含 K8s 专属配置项(`config.ts` 7 字段 + 3 常量 + `parseCidrs()`)、
`DeployMode` 收窄为 `'local'`、全部注释与**语义描述**(`Pod`/`file sidecar`/`ConfigMap`)。
⚠️ **连带必做**(否则 `tsc` 报错):`proxy.ts` 的 `deployMode === 'k8s'` 实参改常量 `false`;`web/server.ts` 的 fail-loud 守卫删除。
⏳ **唯一剩项**:`@kubernetes/client-node` 依赖 + lock(8 处)—— 导出层删不了(须重生成 lock,⛔ 不手改 lock)。
⚠️ **但这不等于「去源仓 `npm uninstall` 就行」**(2026-09-15 更正):**导出物**里它 0 import(3 个 import 它的模块被 `EXCLUDE_FILES` 剔了),可**源仓仍保留 K8s 后端**(`src/supervisor/{k8s-spawner,leader,reconcile}.ts` 都 import 它)⇒ **在源仓直接删会打断源仓 `tsc`**。
**正确顺序**:源仓先下线 K8s 后端 → 再 `npm uninstall` 重生成 lock → 最后重跑导出。三条路线见 `_K8s清理影响评估与回归说明_20260915.md §9`。
---
## 1.5 🔴🔴🔴 用户当场纠正过的硬口径(2026-09-14 凌晨:连纠 8 次)!!!
> **这不是"建议",是硬口径 —— 一条不遵守就要返工。** 全部来自真实纠正,逐条附用户原话。
| !!! | 规则 | 判据 / 用户原话 |
|---|---|---|
| **!!! 1 !!!** | **没验证的能力:不写(不是标注「未验证」,是整条删)** | 「**没验证的不要写意思就是不要写 !!!**」,点名删「双部署后端同一接口(K8s 后端未验证)」。⇒ README **不得出现**「未验证 / 实验性 / 模式 B / K8s 后端 / Postgres」等字样 —— 已**全清**(代码可留,**文档不提**)|
| **!!! 2 !!!** | **不写废话**:括号里的体贴话、推销话 | 「(想自己掌控每一步)**不要写这种没用的废话**」;「商业授权…联系 xxx **不用写这个废话**」。**判据:括号里若没有机制 / 数值 / 真实行为 / UI 提示 = 废话,一律删**。已删:`手动部署(想自己掌控每一步)`、`(建议先审一遍)`、整段「商业授权」+ 邮箱、章节名「授权与商业使用」→「**授权**」|
| **!!! 3 !!!** | **归类必须准:DeepSeek Harness 是「基座」,不是「第三方组件」** | 「**这个不叫三方组件…所有开发都是基于这个来的**」⇒ ① 删掉「第三方组件与致谢」整节;② 在「架构」里讲清 **DSH = DeepSeek AI 开源的 agent harness**(everything-is-a-plugin / Cordis 驱动 / 默认 `npx @deepseek-ai/dsh web` → `127.0.0.1:3080` **本机单用户** Web UI);③ 补上 **developer preview ⇒ 官方明说会破坏性变更**(它才是"为什么要有兼容性预检 + 版本冻结"的钩子)|
| **!!! 4 !!!** | **读码找亮点:亮点必须来自实际读码,且区分「已验证」** | 「在分析一遍项目看看还有哪些亮点」⇒ 按 R-O11 逐个读 `src/**` 头注;**没验证过的不写**(本次挖到的"模型管理两层""迁移账本同形"等因**未实测**而**未采纳**)|
| **!!! 5 !!!** | **未持锁 ≠ 不能改导出物;但「重建」会删掉本地 `.git`** | ① 本工作根**不在**锁保护范围(钩子只护「文档库 / 代码仓」)—— **别再拿锁当理由拒改导出物**(本次被纠正);② ⚠️ **`_build_export.py --force` 的 `rmtree(DST)` 会连仓库里本地 `.git` 一起删**(本次实测)⇒ 顺序必须 **重建 → 六件套 → `git init`/commit → 推送**,**提交后不得再重建** |
| **!!! 6 !!!** | **安装成功才准提交 / 推送(用户定的闸门)** | 「**必须按照说明文档 安装成功才能提交**」⇒ **平台本体跑通**(install 成功 + 登录 200 + 管理台 / 桌面 API 200)是**下限**;`--dry-run` 通过、组件级验证、探针全绿 **都不算通过** |
| **!!! 7 !!!** | **敏感信息一律不进仓库**(测试 IP / 账号密码 / 实例 ID / 服务器信息) | 本次把**真实测试 IP** 写进 README 示例被当场抓到 ⇒ 改用 **RFC 5737 文档保留地址**(如 `203.0.113.10.nip.io`);并把 `106.54.21.172` / `ins-3q6k1p8t` / 测试账号密码**四项加进 `LEAK_PROBES`** |
| **!!! 8 !!!** | **提「注意事项」前先对齐本技能 §1 的 R-O1–R-O13** | 「感觉都不是我最早说的注意事项,**你看看 skill**」⇒ 用户要的是**他原始定的硬规则**,**不是**实现坑;计划/清单按 R-O 组织 |
| **!!! 9 !!!** | **章节顺序服务于读者;参考性章节别放太后;排版要整体看** | 「目录结构 是不是放的太靠后了,**在整体看看你的排版呢**」⇒ ①「**目录结构**」从 Meta 区**上移到「架构」之后**(逻辑结构 → 物理结构,读者顺着一路读);② 排版检查要**整体过**:分隔线用法、标题层级、表格/列表一致性、长句拆分、括号废话(见 !!! 2 !!!)。⚠️ 与 R-O12 的社区标准顺序冲突时,**以用户当场指令为准**并说明理由 |
### 改文档时的**固定动作**(本次反复用到,照抄)
1. **两份同步**:`_overlay\<文件>` 与 `dsh-users-platform\<文件>` **必须逐字节一致**(改一份 = 改两份,改完 `md5` 比一次);
2. **改完必验**:`md5` 相同 + **TOC 锚点机检无悬空** + **全篇关键词扫描**(`模式 B / K8s / 未验证 / 实验性 / WorkBuddy / 旧名 / 敏感串` 应**全 0**);
3. **提交**:`git -c core.autocrlf=false add -A` → `commit --amend --no-edit`(**首发阶段**保持单条发布提交);**提交后不得再重建**;
4. **改本技能**:两副本(活跃 + 文档库归档)**md5 必须一致**,同步前**先抢全局执行锁**。
## 2. 保留 / 移除清单(delta · 改口径先改这里)
### 保留
```
assets/ 注入脚本(2:inject/recovery.js · inject/assist.js)—— ⚠️ **proxy.ts 的 loadInject 按包根解析 ⇒ 缺了平台启动即抛错**,必须随包
src/ 控制面 TS 源码(**已剔除 6 个 K8s 后端模块**,见下)
web/ 静态页
scripts/ 运维与冒烟脚本(**已剔除 5 个 K8s / 已排除插件相关脚本**)
test/ 单测(**已剔除 2 个 K8s 模块的测试**)
.github/workflows/build.yml
package.json package-lock.json tsconfig.json cordis.patch.yml
Dockerfile .dockerignore .gitignore(重建)
mksess.cjs ensure-role-profile-patch.cjs(临时会话 / profile 补丁)
```
> 🔴 **K8s 相关一律不带**(2026-09-14 用户定稿,2026-09-15 升级为**长期策略**):
> ⛔ **不再保留** `deploy/`(11 个 K8s 清单)· `poc/`(4 个 K8s 可行性验证件)· `Dockerfile.dsh`(每用户 Pod 镜像)·
> 6 个 K8s 后端模块(`k8s-spawner` `k8s-user-fs` `leader` `reconcile` `tcp-bridge` `web/file-service`)·
> 2 个 K8s 测试 · `smoke-file-service.mjs` · CI 里的 dsh 镜像步骤 · `config.ts` 的 K8s 专属配置项 ·
> 以及**全部 k8s 注释与语义描述**。
> ✅ **保留** `src/db/pg.ts`(Postgres = 共享 HA,**不是** K8s 专属)。
> 📌 **权威清单与处置办法**:**`_K8s排除台账.md`**(本工作根下)—— 含「下次取新版本怎么做」的步骤与判读表;
> 扫描器 **`_k8s_scan.py`**(`--source` 扫源仓 / 不给参数扫导出物);判定标准 = 构建 **`blocking hits: 0`**。
### 移除
| 路径 | 理由 |
|---|---|
| `poc/business-plugins/` | **已投放插件**(功能管理分区插件,含全部 `.tgz`) |
| `poc/workspace-scoped-picker/` | **已投放插件**(目录选择器) |
| `scripts/ensure-anysearch-admin.cjs`<br>`scripts/ensure-anysearch-pool.mjs` | 特定插件投放脚本 |
| `scripts/ensure-biz-plugins.cjs`<br>`scripts/ensure-portal-entry.cjs`<br>`scripts/install-workspace-picker.sh`<br>`scripts/provision-new-users.sh` | 插件 / 本平台专属投放与开通脚本 |
| `docs/`(7 篇) | R-O4:项目文档 |
| `STANDARD.md` · `poc/README.md` · `poc/*/README.md` | R-O4:文档 |
| `.workbuddy/` · `lib/` · `node_modules/` · `data/` | 本地状态与构建产物 |
| **`.git`** | ⛔ 历史里含全部内部信息 ⇒ **必须全新仓库** |
| 全部 `*.tgz` | 构建产物 |
### 本就不在代码仓(别去找)
**MCN 工作台(`dsh-plugin-mcn-suite`)、douyin-accounts、vision-router、各 skill 本体(含 MCN 相关 skill)** 都不在 `dsh_shenxian` 里 —— 它们在其他仓库 / 实例 profile。收尾探针会复核 `mcn` / `douyin` / `抖音` / `vision-router` **0 命中**。
### ⚠️ 特许保留项(**不要在脱敏里动它**)
| 项 | 处置 | 依据 |
|---|---|---|
| **`dsh-univer-office`**(插件名 + 实例侧 unix-socket 适配) | **保留原名、保留代码**:`src/supervisor/orchestrator.ts` 的 `DSH_INSTANCE_UNIVER_SOCKET` / `UNIVER_DSH_GATEWAY_SOCKET` 段**不得删**;`src/web/routes/whitelist.ts` 注释里点名 `dsh-univer-office`;探针 `LEAK_PROBES` 里**不得**出现 `dsh-univer` / `UNIVER` | 用户 2026-09-13:「univer 可以保留这个插件 专门讲讲如何把开源插件改造为可在多租户平台运行」 |
| ↳ 为什么值得保留 | 它是 `PLUGIN-PORTING.md`(插件移植指南)的**唯一实证样本** —— 一个「静态预检通过但在托管平台完全不可用」的真实案例,含两层根因、3 条设计决策、2 个平台侧机制坑 | 同上 |
### 待决项(用户没拍板前维持现状)
- `poc/business-plugins` + `poc/workspace-scoped-picker`:现按**严格口径移除**(「所有已投放的插件」)。若判定它们属「平台自带界面」而非投放插件 → 加回,并同步改本节 + 探针。
---
## 3. 脱敏映射表(**唯一口径**,改只改这里 + 脚本里的 `GLOBAL`)
| 类别 | 原值 | 替换为 | 命中 |
|---|---|---|---|
| 平台域名 | `dsh.alotbuy.com` / `*.alotbuy.com` / `alotbuy` | `<baseDomain>` | 3 文件 |
| ↳ **特例** | `web/wake.html` 的 `safeNext()` 正则 | 改成**运行时从 `location.hostname` 推导注册域**(`split('.')` 去最左一段),彻底去硬编码 | — |
| 服务器公网 IP | `47.77.182.89` | `<SERVER_PUBLIC_IP>` | 1 |
| 宿主内网 IP | `172.18.16.212` | `<HOST_LAN_IP>` | 1 |
| 服务器代码路径 | `/opt/dshs` | `<INSTALL_DIR>` | 8 |
| ↳ **特例** | `require('/opt/dshs/node_modules/better-sqlite3')` | `require('better-sqlite3')`(等价且更规范) | — |
| 服务器平台目录 | `/opt/dsh/{state,artifacts,backups}` | `/var/lib/dsh-web-platform/{state,artifacts,backups}`(与上游文档一致的数据根) | 6 |
| **内部档案号** | 全角/ASCII 括号里的 `档案 NN`(含 `档案 81 · R1`、`(档案 20)`、`backoff (档案 20)`) | **整块删**(号无对外意义);残渣(空括号 `()` / ` ().`)由后续规则清掉 | 183 |
| **悬空文档引用** | `docs/k8s.md §5.2` / `docs/blueprint.md` / `docs/domain-config.md` | 映射到 README 真实小节(`README「部署形态」` / `README「架构」` / `README「配置」`);`§号` 一并去掉 | 42 |
| ↳ **实现位置** | 以上两项由 `_build_export.py` 的 **`REGEX_RULES`** 做(不是 `GLOBAL`);⛔ **清理规则只准碰全角标点**,唯一放开的两条 ASCII 规则:① `(档案 NN)`(模式里必须有档案号 ⇒ 不会误伤 `foo()`)② ` ().`+句末标点(`(): void` 后面是 `:` ⇒ 不命中)。**每次改 REGEX_RULES 必须重建 + `tsc` 复核**(第一次踩过:ASCII 括号进字符类把 `foo()` 删了,tsc 报 Invalid character) | — |
| ↳ tar 相对写法 | `opt/dsh*`(无前导斜杠) | `var/lib/dsh-web-platform*` | — |
| 账号 / 私有仓库 | `maogeigei`、`[email protected]:...dsh_shenxian_doc.git` | `<YOUR_GITHUB_ACCOUNT>`;文档库引用随 README 重写整体移除 | 1 |
| 云厂商标识 | 「阿里云内网 DNS」「BT-Panel 58888/8765」「sshd 22/32022」 | 「云厂商内网 DNS」「面板」「sshd」 | 1 |
| 插件具体名 | `anysearch` / `@anysearch/anysearch-dsh` / `@liustack/modlens` / `mcn-workstation` | 「该插件」/「某第三方插件」→ 再**整行重写**成中性描述 | 6 |
| ↳ **例外(保留原名)** | **`dsh-univer-office`** 与其实例侧适配(`DSH_INSTANCE_UNIVER_SOCKET` / `UNIVER_DSH_GATEWAY_SOCKET`) | **不脱敏、不删代码**(用户 2026-09-13 明确「可以保留这个插件」);它同时是 `PLUGIN-PORTING.md` 的实证样本 ⇒ 其 env 名、注释、`DELETE_LINES` 条目**都不得再动** | 3 |
| **项目改名** | `dshs` 全部自指标识 | `dsh-web-platform`(规则顺序:`dshs.db` → `dshs.db` → `/var/lib/dshs` → `DSHS_` → `dshs`) | 149 |
| ↳ **必须保留** | `上游骨架仓库(已按要求不再具名)`、`上游 \`dshs\`` | **原样不动**(出处/致谢);`LICENSE-UPSTREAM-MIT.txt` 逐字节不动 | — |
**发布前必须替换的占位符**(写在台账里):
| 占位符 | 出现位置 |
|---|---|
| `<YOUR_GITHUB_ACCOUNT>` | `README.md`、`package.json` → repository |
| ~~`<CONTACT_EMAIL>`~~ | 原为 `README.md` 授权章节 / `LICENSE` —— **授权章节与 LICENSE 已于 2026-09-13 移除**,该占位符随之消失(见 §5) |
| `<COPYRIGHT_HOLDER>` | `LICENSE` |
| `<SERVER_PUBLIC_IP>` / `<HOST_LAN_IP>` | `scripts/install-egress-guard.sh`(部署时按自己服务器填) |
| `<INSTALL_DIR>` / `<baseDomain>` | 仅注释,可留 |
> `install.sh` 里的 `dsh.example.com` / `you@example.com` 是文档示例,符合惯例,**不改**。
---
## 4. 保留未动(刻意)
- **183 处「档案 NN」内部编号引用**(仅代码注释):属内部档案号,**不是**本机/服务器信息。已列入「可选后续」;要清需用户点头(做一次纯注释替换)。
- 上游 `docs/` 内容虽为 MIT 可带走,但 R-O4「不带任何文档」⇒ 不带。
---
## 5. 授权结构(**2026-09-13 15:1x 已变更:授权类文件全部移除**)
> 🔴 **现状(以本条为准,覆盖下文所有旧描述)**:用户 2026-09-13 明确 —— **「你的任务是改造和优化,这些内容全部删除」** ⇒ 导出仓里的
> **`LICENSE` / `LICENSE-UPSTREAM-MIT.txt` / `THIRD-PARTY-NOTICES.md` 三份已全部移除**(同时从 `OVERLAY` 与 `REQUIRED_EXPORT` 摘除、`_overlay/` 快照同步删除、README 的 License 徽章 / 目录项 / 「授权与商业使用」整节删掉、`package.json` 的 `license` 回到上游原值 `MIT`)。
> **原文备份在 `../_授权归档_发布时再放回/`**,恢复步骤见 `_导出说明与脱敏台账.md` §F。
> ⛔ **不要再把授权/许可当议题去问用户**(不属本技能范围,不列为待办、不上抛)。
> ⛔ **不要再把授权/许可当议题去问用户**(不属本技能范围,不列为待办、不上抛)。**下文凡提到这三份文件的段落一律以此条为准**(保留原文只为记录历史,不是要求)。
> ⚠️ **但下面这条「MIT 硬约束」是客观事实,任何会话都不许删**(删了会误导后人写出违规声明)。
> ⚠️ **硬法律前提(事实,必须记住)**:**上游 `dshs`(上游作者(已按要求不再具名))是 MIT。MIT 不允许对上游代码附加限制** ⇒ 若日后要恢复「个人免费 / 商用收费」,**只能分层**:上游留 MIT,只对**本项目新增部分**收费;把整个仓库标成「商用需授权」= **违反 MIT**。
**若日后要对外发布**(当前仓库里**没有**任何授权文件),必须先放回那三份并恢复 `OVERLAY` / `REQUIRED_EXPORT`。理由:
1. 无 `LICENSE` ⇒ 默认「保留所有权利」⇒ **任何人都不能合法使用**;
2. 缺上游 MIT 声明 ⇒ **侵犯 上游作者(已按要求不再具名) 的著作权**(MIT 明文要求保留版权与许可声明);
3. 缺第三方 NOTICES ⇒ 多款依赖(MIT/Apache-2.0 等)同样要求保留声明。
⚠️ **不要再把「授权结构」当议题去问用户**(2026-09-13 用户明确:*"你的任务是改造和优化,这些内容全部删除"*)—— **不属本项目范围,不列为待办、不上抛**。保持**现状分层**即可(保守合规:上游永远 MIT、只对本项目新增部分声明)。
> 即便日后发现 `上游作者(已按要求不再具名)` 就是用户本人(可整体简化为单一授权),也**不是本技能要推进的事** —— 只在用户**主动**提出时按本表处理。**但上一条「MIT 不允许对上游代码附加限制」是客观事实,任何会话都不许删掉它**(删了就会误导后人写出违规声明)。
每次发版要动的地方:`README.md` 授权章节(若条款变)、`LICENSE`(版本/日期)、`THIRD-PARTY-NOTICES.md`(依赖版本与分布)。
---
## 6. 迭代发布 SOP
```sh
# 0) 先抢全局执行锁(会写文档库/或要动导出物时)
cd "/d/github/dsh_shenxian/dsh-server-docs"
ME="<会话名>" bash scripts/handoff-guard.sh --claim-exec "<会话名>"
# 1) 源仓库确认基线(只读)
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"
git -C "D:/github/dsh_shenxian" status --short --branch
git -C "D:/github/dsh_shenxian" log --oneline -10
# 2) 若本次要改脱敏/保留口径 → 先改 _build_export.py 的 GLOBAL / INCLUDE_* / DROP_SCRIPTS / OVERLAY
# 3) 重建(手工撰写层会自动快照→恢复)
cd "/e/ProgramData/AI技能/dsh-laijing-github"
"/e/ProgramData/.workbuddy/binaries/python/versions/3.13.12/python.exe" _build_export.py --force
# 4) 改版本(三个地方一起改,否则不一致)
# package.json 的 version | README.md「版本与迭代」表新增一行 | LICENSE 的版本/日期(条款没变就不用)
# 5) 六项验证(§7)——**全绿才准交付**
# 5.5) 【**发布到 GitHub 前必做**】补齐英文版(见 R-O13)——中文是母本,英文此刻才生成
# README.en.md + PLUGIN-PORTING.en.md + 两份 README 顶部加语言切换行
# 6) 交付/推送(**R-O6:未经明确要求不做**)
```
**版本号约定**:遵循语义化版本。写 README 版本表时**同时写「类型」列**(首个公开发布 / 特性 / 修复 / 内部迭代)。
**登记格式**(README「版本与迭代」表):
```
| **v1.1.0** | 2026-10-xx | 特性 | 一句话摘要(对外能看懂,不写内部档案号)。 |
```
---
## 7. 验证六件套(缺一不可)
| # | 验证 | 命令 / 判据 |
|---|---|---|
| ① | **阻断性探针 0 命中** | 脚本内置 `LEAK_PROBES` 与 `INFO_PROBES` 两档;**`blocking hits: 0`** 才放行。`INFO_PROBES`(`档案` / `交接单`)是**有意保留**的注释引用 |
| ② | 未改动文件**逐字节一致** | 抽样 `cmp -s <源> <导出>` → `IDENTICAL` |
| ③ | **行尾符不被改写** | `grep -c $'\r'` 两边相等(CRLF 源必须仍是 CRLF) |
| ④ | **能编译** | 见下「tsc 验证法」→ `tsc -p tsconfig.json --noEmit` 退出码 **0** |
| ⑤ | `install.sh` 语法 | `bash -n install.sh` + `bash install.sh --help` |
| ⑥ | **已移除项核对** | 逐一 `[ -e ]` 确认 `docs` / 两个插件目录 / `STANDARD.md` / `lib` / `node_modules` / `.workbuddy` 均不存在 |
**tsc 验证法**(导出物没有 `node_modules`,靠**临时联接**;用脚本而不是手敲,**并且绝不能用 recursive 删除**):
```sh
# 脚本已备好:<导出根>\_verify_tsc.mjs(建 junction → 跑 tsc → rmdirSync 拆联接)
"/e/ProgramData/.workbuddy/binaries/node/versions/22.22.2-3/node.exe" "<导出根>\_verify_tsc.mjs"
# 判据:tsc exit = 0 | junction removed = true
# 收尾再确认「导出物无 node_modules 残留」+「源仓库 node_modules 条目数未变」
```
⚠️ **绝不要用 `rm -rf` / `rmSync(…, {recursive:true})` 删这个联接** —— 在 Windows 上会**顺着联接删掉源仓库的 `node_modules`**。只用 `rmdirSync`(只摘链、不进目标)。
---
## 8. 实测坑(全部踩过,别再踩)
1. **`text` 被重新赋值导致替换未落盘** —— 脱敏函数里 `text = text.replace(...)` 之后再用 `if text2 != text` 判「是否需要写盘」⇒ 全局替换**永远不落盘**(只落行级改动)。**必须留 `orig = text` 做比较基准。**
2. **行级重写的 key 必须写「全局替换之后的文本」** —— 例:原行 `(档案 70 的 anysearch 事故…)`,key 要写 `(档案 70 的 该插件 事故…)`。用替换前的原文当 key ⇒ 静默不命中,留下半吊子句子。
3. **行级重写会丢缩进** —— 按 `strip()` 匹配就必须把**原行的 indent 补回去**(多行替换值还要给后续行也加 indent),否则 TS 缩进错乱。
4. **全局替换顺序敏感** —— `wake.html` 的专用规则必须在笼统的 `alotbuy → <baseDomain>` **之前**,否则专用规则永远不命中(会被先替换成 `<baseDomain>.com` 这种半成品)。
5. **重建会抹掉手写文件** —— 所以有 `OVERLAY`(先快照 `_overlay/` 后恢复)+ `--force` 安全闸。**验证过一次**:重建后 5/5 个 overlay 文件逐字节一致。
6. **`.git` 绝不能带过去** —— 历史里含全部内部信息;必须是**全新仓库**。
7. **Windows 下 `install.sh` 没有执行位** —— README 一律写 `sudo bash install.sh`;要执行位就 `chmod +x` 后再提交。
8. **别信「探针没命中」的直觉** —— 探针必须**在重建之后、还原 overlay 之后**跑(overlay 里也可能带泄漏)。
9. ⛔ **绝不对 `_build_export.py` 本身做批量替换** —— 脚本里同时存在「源模式」与「目标值」(例如规则 `("dshs", "dsh-web-platform")`),盲替会把**源模式**一起改掉 ⇒ **改名规则静默失效**(2026-09-13 实际误伤 4 处)。改脚本只用精确 `Edit`,改完**必须重建一次并用 grep 复查**。
10. **`Dockerfile` / `Dockerfile.dsh` 曾被整份跳过脱敏** —— `is_text()` 按扩展名判断,二者无扩展名(或 `.dsh` 不在白名单)⇒ 漏掉。已在 `is_text()` 里加特判。
11. **`package.json` 属「源派生」而非 OVERLAY** —— 手改 version / description / repository / license 会在下次 `--force` **被覆盖**。这类项目自有字段必须写成 **GLOBAL 规则**。
12. **`dshs.db` 是无前缀写法** —— `src/config.ts` 里默认库文件名不带 `dsh-` 前缀,只替换 `dshs` 会漏掉它;要单独一条规则。
13. **改名要连导出目录名一起改**,并同步两个辅助脚本里的绝对路径(`_build_export.py` 的 `DST`、`_verify_tsc.mjs` 的 `EX`),改完**重跑一次 tsc** 验证路径仍对。
14. **废弃的旧候选名要进阻断性探针** —— 改名改了两轮时(`dshs` → 中间候选 → 终选),若只探最老的那个名,**中间名会静默残留**(`_overlay` 里最容易中招)。做法:把**所有曾用名**都加进 `LEAK_PROBES`。
15. **改 overlay 的自指标识可以做批量替换**(overlay 里只有本项目自己的标识,上游出处名是另一个字符串)—— 但**必须先用 grep 确认「旧名出现处全是自指」**再替换;`_build_export.py` 本身**绝不**能这么干(见坑 9)。
16. 🔴 **`--claim-exec` 的输出必须当场看;claim 与 release 绝不能写进同一条命令**(2026-09-13 **实际闯祸**)—— 把 claim 结果重定向到文件、只显示 `OWNER` 首行 ⇒ **没发现抢锁失败**;命令末尾的 `--release-exec` 是 `rm -rf` 语义,于是**把另一个会话(`R1-注入层-1104`)的锁删掉了**。正确姿势:
- 抢锁**单独一条命令**,且**当场看它的输出**(`✓ 已持全局执行锁` vs `✗ 抢锁失败`);
- 看到「占用者」不是自己 ⇒ **立即停手**,不碰文档库/代码库;
- **放锁前再 `cat 交接单/.exec-lock/OWNER` 确认首行是自己**;
- 万一误放:脚本实现是 `rm -rf "$LOCKEXEC"`、**不留档** ⇒ 按 `handoff-guard.sh` 第 43 行的格式原样重建(`OWNER` / `开始:MM-DD HH:MM` / `在做:…`,值取自抢锁失败时的输出),再用 `bash scripts/handoff-guard.sh` 的信息模式验证「占用者」已回到原会话,并**在回复里明确告知用户**;若该会话其实已结束,请用户自行撤锁(处置权只属于用户)。
17. 🔴 **迁移工作根:先改脚本常量,再搬目录**(2026-09-13 实际踩)—— 顺序颠倒(先 `mv` 后改 `OUT`)时,任何一次重建都会**在旧位置把整个仓库重新建出来**(那次建出 135 个文件),而且因为旧位置**没有 `_overlay`**,**6 个手工层文件(README / LICENSE / NOTICE / PLUGIN-PORTING / install.sh / LICENSE-UPSTREAM-MIT)全部缺席** —— 若此时旧目录被当成交付物,就是一次「文档凭空消失」的事故。
**正确顺序**:① 改 `_build_export.py` 的 `OUT` **和** `_verify_tsc.mjs` 的 `EX` → ② 复制/搬运目录 → ③ 在新位置重建 → ④ 核对 `文件数` 与**手工层 6/6** + 跑 `tsc` → ⑤ **`ls` 确认旧位置不存在**(没被重建复活)→ ⑥ 同步本技能与项目 MEMORY 里的路径引用。
⚠️ 附带教训:`_build_export.py` 的安全闸与 OVERLAY 机制**都依赖 `OUT` 指向真实工作根**;`OUT` 指错时它**不会报错**,而是**默默在别处新建一套**。
18. 🔴 **`INCLUDE_DIRS` / `INCLUDE_FILES` 是白名单:漏了不报错,只是少文件**(2026-09-13 实际踩:`assets/` 漏收录 ⇒ 导出的仓库缺注入脚本,平台会 fail-fast、自带校验脚本也会失败)。**两条防线**:
- **`REQUIRED_EXPORT` 清单**(脚本内,26 项):构建时逐个 `os.path.exists`,**缺一即 `return 1`**;新增"运行时要读的文件"必须加进去。
- **用被导出仓库自带的校验脚本验收** —— `node scripts/verify-inject.cjs`(读 `assets/inject/*.js` + 检查 `proxy.ts` 走 `loadInject`)。这比"我看了一眼目录"可靠得多。
⚠️ 同类风险点:`package.json` 的 `files`(npm/git 安装按它过滤,与 `INCLUDE_*` 是**两套名单**,都要补)。
---
## 9. 命令速查
```sh
# 重建
cd "/e/ProgramData/AI技能/dsh-laijing-github"
"/e/ProgramData/.workbuddy/binaries/python/versions/3.13.12/python.exe" _build_export.py --force
# 只跑探针/看结构(不重建)
grep -rIlF "alotbuy" dsh-web-platform/ ; find dsh-web-platform -type f | wc -l
# 看某文件与源的差异
diff -u "D:/github/dsh_shenxian/<f>" "dsh-web-platform/<f>"
# 行尾对照
grep -c $'\r' "<源 f>" ; grep -c $'\r' "dsh-web-platform/<f>"
```
---
## 10. 已知待办 / 漂移(接手先看)
> ✅ **2026-09-13 15:1x 全部清零** —— 以下原待办均已处理:
> · `install.sh` 已在服务器隔离目录跑通 `--dry-run`(并修 4 个缺陷)| · 悬空 `docs/*.md` 42→0| · 「档案 NN」197→0| · 7 处占位符已按 `PUBLISH_*` 填| · `poc/` 两个插件的加回结论 = **保持移除**。
> ⛔ **「授权条款 / 法律意见 / `上游作者(已按要求不再具名)` 是否本人」已从本项目移除** —— 用户明确「你的任务是改造和优化,这些内容全部删除」⇒ **不再列为待办、不再上抛**(事实层面的 MIT 约束见 §5,那条不许删)。
**当前仍需注意的(非待办,是风险提示)**:
- ⚠️ **首装从未在干净机器上真跑过**:`--dry-run` 已验证全流程,但**真实安装会写 `/etc`、建 systemd unit、起服务** ⇒ 首次真装请在一台干净机器上做。
- ⚠️ **导出基线会漂**:导出脚本复制的是**工作树**(多会话并行在改源码仓)⇒ 发布前按 `git archive <commit>` 冻结,或**每次重建必重跑探针 + `_verify_tsc.mjs`**。
- ✅ **项目名已定稿**(用户 2026-09-13):中文 **DSH Web 平台** | English **DSH Web Platform** | 技术标识 **`dsh-web-platform`**(已在 npm 与插件目录实测未占用)。若日后要换名,按 **R-O8** 重走「查占用 → 改全量标识 → 保留出处致敬」,并把**新废弃的旧名加进探针**。
- ✅ **本技能的归档副本 + `INDEX.md` 登记已补**(2026-09-13):`dsh-server-docs/skills/dsh-opensource-release/SKILL.md`,两副本 md5 一致;INDEX §一/§二 已登记;四件套全绿。
⚠️ **本技能每次改动后都要同步归档副本**(md5 必须一致),同步前**先抢全局执行锁**;纪律 = 先单独抢锁**当场看输出** → 验 `OWNER` 是自己 → `cp` → 两副本 md5 一致 → 复跑四件套 → **再验 `OWNER`** → `--release-exec`。
- ⏳ **英文版待生成(发布前必做,见 R-O13)**:`README.en.md` + `PLUGIN-PORTING.en.md` + 两份顶部的语言切换行。**当前刻意不做**(用户口径:中文优先,同步 GitHub 时再更新英语)—— ⚠️ 但**切换行要等英文文件存在后再加**,否则是死链。
- 🔴 **导出基线会漂(2026-09-13 11:2x 实测)**:导出脚本复制的是**工作树**,而**多个会话在并行改源码仓** —— 当时 `D:\github\dsh_shenxian` 的 HEAD 仍是 `da3e0f9`(⚠️ 2026-09-13 20:2x 实测已变为 **`43976fe`「初始提交」**,且**仅 1 条提交**),但**工作树有 20 个未提交文件**(`orchestrator.ts` / `crash-policy.ts` / `proxy.ts` / `spawner.ts` / `config.ts` / `web/*.html` / `test/*` …),导出里因此混入了**别人未完成的改动**;同时新增了需脱敏的标识(`'dsh-plugin-mcn-suite'`)触发探针。**发布前必须二选一**:① 把导出改成**按 commit 取**(`git archive <commit>`,才是"冻结版本");② 或明确接受"含未提交工作"并**每次重建后重跑探针**(新增插件名/内部名会随别人提交冒出来)。
- ⚠️(知识库侧,非本技能职责):本机 `.workbuddy/skills/dsh-instance-diagnose/` **尚无归档副本**;`INDEX.md` 只登记了 3 个 skill(`dsh-knowledge-upkeep` 未登记)。属既有漂移,**未擅自修**。
---
## 相关
- 台账(唯一事实源):`_导出说明与脱敏台账.md`(在本工作根下)
- 构建脚本:`_build_export.py` | 类型检查:`_verify_tsc.mjs` | 手工层快照:`_overlay\`(三者都在本工作根下)
- **插件移植指南**(随仓库发布):`dsh-web-platform\PLUGIN-PORTING.md` —— 讲「把开源插件改造成多租户平台可用」,含六个失败模式 H1–H6 与五条规范 R-a~R-e,样本 = `dsh-univer-office`
- 内部权威源(**只读参考,不外带**):`dsh-server-docs/04-调整方案/75-*.md`(托管友好性 + 资源成本两维度)· `76-*.md`(univer 改造全过程)· `71-*.md`(兼容性预检)
- 技能:`dsh-change-workflow`(改动流程与红线)· `dsh-knowledge-upkeep`(文档库维护)· `dsh-instance-diagnose`(实例故障)
- 项目事实:`dsh-server-docs/BRIEF.md`(现行事实)· `dsh-server-docs/CODEBUDDY.md`(动作前规则)