Files

534 lines
110 KiB
Markdown
Raw Permalink Normal View History

---
name: dsh-opensource-release
description: DSH 多租户托管平台的「开源导出与版本迭代」技能 —— 把私有代码仓导出成可公开的开源副本(脱敏 / 去插件 / 分层授权 / 重写说明文档),并在后续版本里安全地重跑导出、登记版本。当用户说「开源一份」「导出到 GitHub」「发新版本」「改一下开源那份的脱敏/授权/说明」「开源那份同步一下」时触发。核心:**源仓库只读** + **阻断性探针 0 命中**才准放行 + **OVERLAY 手工撰写层**不得被重建抹掉。
version: 1.0.0
updated_at: 2026-09-20
last_change: 【2026-09-22 按要求统一版本号】frontmatter `version` → `1.0.0`(原 v2.10.8);正文与历史中的版本号为当时记录,未改动。此前 2.10.8(2026-09-20):**登记「提交并推送双仓」的写法与一条换行陷阱**(用户指令:「**提交到仓库**」)—— ①✅ 本轮以 commit **`4a870dc`** 推送双仓(8 files · 146+/152−),**推前三方同 hash 复核**:`git ls-remote origin/main` = `git ls-remote cnb/main` = 本地 `HEAD` = `4a870dc27de183486f255e9aaeaf511228f2d540`。②🔑 **推送写法(两仓两套,⛔ 别互换)**:`origin` = GitHub 走 SSH,**必须显式** `GIT_SSH_COMMAND="ssh -i C:/Users/Administrator/.ssh/id_ed25519_ai1net -o IdentitiesOnly=yes"`;`cnb` = `https://cnb.cool/…` 走凭据钩子 `~/.cnb/git-cred.sh`(**⛔ 不给它套 SSH 密钥**)。③✅ 提交信息经 **`-F <文件>`**(⛔ 不用 heredoc — 非 ASCII 会被改写)· 身份**内联** `-c user.name/email`(⛔ 不改全局)· `git add` **显式路径**(⛔ 不用 `-A`)。④🔴🔴 **新增换行陷阱(写进坑 36)**:`dsh_ai1net/.gitattributes` **只钉了 `LICENSE` / `COMMERCIAL-LICENSE.*` 三行 `-text`,`*.md` 不在其内** ⇒ 走 `core.autocrlf=true` ⇒ **导出仓 `*.md` 一旦被 git 重新检出就变 CRLF,而 `_overlay` 恒为 LF ⇒ `_sync_overlay.py --check` 会假报「有差异」**(`git status` 反倒干净)⇒ ⚠️ **此时反向拷贝就是事故 #27 的反向版**;正确处置 = 直接跑 `_sync_overlay.py`(LF 覆盖回去),且 **git blob 一直是 LF**(`git cat-file -p HEAD:README.md` 实测无 `\r`)。2.10.7(2026-09-20):**新增坑 36 —— 「巡检表」是自己的草稿、⛔ 不是事实源;并给出可复用的「精确串替换器」落地法**(用户:「**按照你的方案优化,要确保中文英文内容一致**」)—— ①🔴🔴 **差一步按草稿改错数**:第 9 轮巡检 P1-4 建议「改成**七个**失败模式」,落地前回源文档核对 ⇒ `PLUGIN-PORTING.{md,zh-CN.md}` **早已是 H1~H8 八个失败模式 + 六条规范(R-a~R-f)** ⇒ 按**源文档**对齐成 8 行(补 H7 静默挂死 / H8 原生绑定 glibc),收尾指针的「五条规范」一并去计数。🔑 **判据:README 是二手转述、定义在源文档 —— 凡动「几个 / 几条 / 第几条」必须回定义处核对**,否则修完仍与文档打架(正是 P1-4 要修的毛病)。②✅ **落地法定型:精确串替换器**(`(old, new)` + **先数命中次数要求恰为 1**,任一不中即**整体中止不落盘**;行级删除按行首前缀断言 1 次)—— 比逐次 `Edit` 可靠、比 `sed`/heredoc 安全(非 ASCII 不被改写);跑完 `grep -c` 复核旧串 = 0 再比字节/行数。③🔴 **索引不写死计数**:文档地图 3 处 + 收尾指针 1 处;⚠️ **括号里的枚举保留**(那是指针的坐标不是计数)。④🔴 **搬内容优先于删内容**:版本历史里 5 条讲「文档自己怎么改的」⇒ 选**搬到 `manual/contributing.*`**(判据 = 该文档地图描述本就写着「版本号与发布历史」),README 只留用户可感的行为变更(v1.4.1 只剩 `503` 冷启动 + 整理)。⑤🔴 **历史条目里的旧名 ⛔ 不要改**(`project.*` 3 处「功能管理」是当时改名事件的历史条目名;要改的是 README/FAQ 的现名)。⑥🔴 **地图加一行会动 parity `tableRows`**:75 → **78**(地图 +1、PLUGIN-PORTING +2),**中英须同增**。⑦🔴 **中英同批执行**(6 文件一次脚本写完),彻底修掉 2.10.2 记的「英文滞后一轮」问题。2.10.6(2026-09-20):**确立「两语言各用各的工具」并跑完中文侧实测**(用户:「**中文用中文版本测 给你两个语言各用各的**」)—— ①🔴🔴 **分工定案:中文 → `humanizer-zh`(24 模式 + 5 维 50 分表);英文 → `humanizer`(55 模式 + CLI)**,⛔ 不交叉用;**原待办「给中文侧补分词器」据
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 05-交接单/.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` 本身侥幸没被洗掉) | **顺序必须是:① 改脚本常量 → ② 再搬/删目录 → ③ 重建验证**。迁完**必查旧目录没有复活**(`ls`),并核对 `find <repo> -type f \| wc -l` 与手工层文件是否都在。<br>🔴 **改名前先 `grep -rn "<旧路径>" --include="*.py" --include="*.mjs" --include="*.sh"` 把全部引用点扫出来**:2026-09-16 由 `dsh-laijing-github` 改名时实测:要同步的**不是 2 处而是 5 处**(`OUT` · `EX` · `_sop_check.mjs` / `_upload_audit.mjs` / `_verify_all.mjs` 各自的 `W`) | §8 坑 17 |
| 10 | **白名单收录静默漏项:`assets/` 没进 `INCLUDE_DIRS`**(2026-09-13 用户问"确认都同步了吗"时查出)—— 导出的仓库缺 `assets/inject/{recovery,assist}.js`:`proxy.ts` 的 `loadInject` 会 **fail-fast 抛错**(平台起不来)、仓库自带 `07-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 |
| **17** | 🔴🔴 **只清「新导出物」、从不巡检「已公开仓库」⇒ 泄漏在公网上长期挂着**(2026-09-19 用户当场指出)—— 实测 `dsh-users-platform`(已公开多日)里躺着 **7 个 `verify-cluster-*.mjs` + `start-cluster-manager.sh`**,逐个含**内网主机号 `w-106`(14 处)/ 内部隧道端口 `15432`·`19000`·`19001` / PG 连接串带口令**;另有 7 个 smoke 脚本硬编码 `adminpass123` 之类的**像真口令**的字面量 | **硬规则 R-O15**(见 §1)=「**每次整理/发版,必须对*所有已公开仓库*跑一遍 `_check_public.py`**」;发现命中就**三件一起做**:① 补成 `_build_export.py` 的**规则 + 探针**(否则下版回潮)② **就地清理已公开仓库** ③ 提交推送。<br>判据(正名):**「换一个部署者,这个值会不会一样?」** 会 ⇒ 通用事实、允许(云厂商元数据端点 `100.100.100.200`、docker 网桥 `172.17.0.1`、RFC1918/5737 段);不会 ⇒ **泄漏**(我们的主机号、隧道端口、测试机 IP、口令) | 本表 #17 |
| **18** | 🔴 **drop 一个脚本前只查「文档引用」、不查「代码引用」⇒ 误删 12 个被测脚本**(2026-09-19 实测)—— 把 12 个 `overlay-*.cjs` 当「内部工具」drop,结果 `test/overlay-join.test.mjs` 用 **`readFileSync(join(repo,'07-scripts','overlay-node-admit.cjs'))`** 真的去读它、`test/overlay-content.test.mjs` 读 `overlay-probe.cjs` ⇒ 这些测试**必然跑挂**;`jitter.ts` 头注还写着「与 `overlay-jitter.cjs` 逐字同口径」⇒ 变悬空引用 | **判据 ① 必须是「无任何引用」,文档与代码都要查**:`grep` 脚本名 + **搜 `readFileSync` / `join(` / `import` / `require` / `spawn`** 等**代码级**依赖;<br>删除后**必跑悬空引用复查**(对所有 drop 名单逐个在保留文件里搜),并用 `npm run verify` / `npm test` 的入口清单反查 `package.json` | 本表 #18 |
| **19** | 🔴 **纯数字的「内部端口」直接全局替换 ⇒ 可能命中 base64 哈希**(2026-09-19 防住)—— `15432` / `19000` / `19001` / `32022` 都是裸数字串,而 `package-lock.json` 的 `integrity` 与**图示内嵌字体的 base64** 里全是 base64 字符 | 加这类规则**前先扫上下文**:① 确认命中处都是 URL/端口语境;② 逐处查 `package-lock.json` 与 `diagrams/*.html`(字体 base64)是否 0 命中;③ 同理,`w106`/`w47` 这种**纯字母数字**串**绝不能裸替**,只替「**带引号 / 对象键 / `network/hostId` 复合键 / 标识符**」等**可区分形态**(或给正则加 base64 安全边界 `(?<![A-Za-z0-9+/\-_=])…(?![…])`)。误伤的典型症状是 **`npm ci` 校验失败**,且与本次改动毫无关联、极难排查 | 本表 #19 |
| **20** | 🔴🔴 **drop 文件前只查「文档引用」⇒ 误删 12 个被测脚本**(2026-09-19 实测,同 #18 的第二个实例)—— 把 12 个 `overlay-*.cjs` 当内部工具 drop;实际 `test/overlay-join.test.mjs` 用 **`readFileSync(join(repo,'07-scripts','overlay-node-admit.cjs'))`** 真的去读它,`src/net/relay/jitter.ts` 头注写着「与 `overlay-jitter.cjs` 逐字同口径」 | 判据 =「**无任何引用**」必须**文档与代码双向查**:文件名 `grep` **+** `readFileSync` / `fs.readFile` / `join(` / `import` / `require` / `spawn` / `exec`;删后**必跑悬空引用复查**(对 drop 名单逐个在保留文件里搜)。<br>🔑 **反面对照**:同族的 `verify-*.mjs\|cjs` **不能**按「看着像测试」删,它们被 `src/web/i18n.js` / `src/web/model-landing.ts` / `AGENTS.md` / 安装流程引用,是**产品自带校验** | 本表 #20 |
| **21** | 🔴 **删脚本后 `package.json` 留下悬空入口**(2026-09-19 实测)—— ① `"check:layering": "node 07-scripts/check-layering.mjs"`(脚本已 drop)② **同一条命令还内联在 `verify` 里** ⇒ 上一版只删了独立入口、**漏了内联那处**,公开仓 `npm run verify` **必失败**;③ 删完 `test`/`smoke*` 后 `verify` 成了**最后一项** ⇒ **尾逗号让 JSON 非法** | 删任何文件时把它的**每一处引用**都清掉:`package.json`(`scripts` / `files`)· CI `.github/workflows/*.yml` · `Dockerfile` · `.dockerignore` · `tsconfig.json`;改完 **`json.load` 校验 + 打印入口清单**。判据 =「**产物不在,引用它的步骤也不该在**」(= #15 的同一条,本轮在 `package.json` 上第二次踩) | 本表 #21 |
| **22** | 🔴 **行级替换的 key 写了「脱敏前」的文本 ⇒ 静默失配**(2026-09-19 再犯)—— `LINE_REWRITE` 改 `07-scripts/ci.sh` 标题,key 带 `(档案 19 §C3)`;而「档案 NN」正则在**行级替换之前**就把那段吃掉了 ⇒ 不报错、不命中、旧标题留在公开文件里 | ① `sanitize()` 的阶段次序是 **GLOBAL → REGEX → 行级(`DELETE_LINES` / `LINE_REWRITE`)** ⇒ 行级 key 必须写**该阶段实际看到的文本**;② 改完**读回原文复核**;③ 整块删除**用 GLOBAL 多行字面量**(纯 `str.replace`,支持跨行),别用「行首前缀」删(删完会留**空壳行**,而 REGEX 抓不到删完后的形态);④ 源文是 **CRLF** 时字面量必须带 `\r\n`(先 `open(p,'rb').read()` 数 `\r\n` 确认) | 本表 #22(同 §8 坑 2) |
| **23** | 🔴 **范围收缩时漏改「清单」与「文档宣称」**(2026-09-19 实测三处)—— ① `REQUIRED_EXPORT` 里留着 `test/crash-policy.test.mjs` ⇒ 构建**必然失败**;② 文档仍写「**两层测试**:单测 + 9 个 `smoke:*` 冒烟」⇒ 读者照文档跑 `npm test` / `npm run smoke` **必报错**;③ 忘了同步 `_check_parity.mjs` 的 `PAIRS`(**白名单:没登记就静默跳过对等检查**) | 收缩范围 = **四处一起改**:`INCLUDE_*` / `REQUIRED_EXPORT` / `PAIRS` / **文档的能力宣称**(OVERLAY 手工层 ⇒ 要**手工逐处改**且 `_overlay/` 与导出仓两处同步)。凡「清单式」机制,改完**回读计数**(AST 实读) | 本表 #23 |
| **24** | 🔴 **本机 PowerShell 下 CNB 推送「假死」**(2026-09-19:`exit 128` 且**零输出**,`GIT_TRACE` 停在调用凭据助手那一行)—— 而 `git ls-remote cnb`(公开读**不需要认证**)一切正常 ⇒ **极易误判成「令牌失效 / 仓库不存在 / 网络问题」**。真因:CNB 凭据助手是**本地 sh 脚本**(依赖 `tr`/`sed`),git 经 `sh` 调用时 **PATH 被宿主 shim 重置** ⇒ 失败(早期还打印 `tr: command not found`,加过 PATH 后错误反而**静默**) | ✅ **用 Bash 工具 + 显式 PATH 推**(同机同凭据,一次成功):<br>`export PATH="/e/…/PortableGit/versions/1.2.0/usr/bin:/e/…/mingw64/bin:/c/Windows/System32:/c/Windows:$PATH"` 然后 `GIT_TERMINAL_PROMPT=0 git push --force cnb main`。<br>⚠️ 别在 PowerShell 里把 `usr/bin` **前置**(会把 `git` 解析到不完整版本 ⇒ 报 `git: 'remote-https' is not a git command`) | 本表 #24 |
| **25** | 🔴 **范围收缩的判据用错 ⇒ 要么白删、要么删坏**(2026-09-19)—— ① 把「没人引用」当判据:`web/i18n.js` 只在**注释**里被提到,但它是**运行时静态资源**,删了页面就坏;`src/web/semver-shim.d.ts` 在「从入口不可达」单子里,但删了 **`tsc` 报 TS7016**;② 漏查「**删完谁还会指向它**」:`install-python-runtime.sh` 等 4 个脚本的名字**出现在管理台 API 返回体**里(`/api/admin/runtime` 的 `installScripts`、`/api/capabilities` 的降级提示)⇒ 只删脚本 = **界面指向不存在的文件**;③ 顺手发现两个「白留」的:`07-scripts/build-web.mjs` 是**空操作占位**(源码自称 *intentionally does nothing yet*)、`.github/workflows/build.yml` **监听 `master` 而导出仓分支是 `main` ⇒ CI 永不触发** | 判据(用户 2026-09-19 定)= **「缺少也不影响项目运行」**,且**四步都要走**:① 真实 import 图 + 从 `package.json` 的 `main`/`bin` 算**可达性**;② 算**移除闭包**(移除后保留文件还有没有边指向它);③ `grep` 文件名在 **`.ts`** 里的位置,看是否出现在**返回给前端的字符串**;④ 查**文档/`package.json`** 引用。<br>✅ 工具:**`_check_unused.py`**(只报告不删) | 本表 #25 · R-O17 |
| **26** | 🔴🔴🔴 **把「从代码入口不可达」当成「不影响运行」⇒ 差点删掉生产正在跑的服务端进程入口**(2026-09-19 用户真机验证推翻)—— 我据「从 `package.json` 的 main/bin 出发不可达 + 移除闭包 0」判定 **relay 服务端族 7 文件(214 KB,含 `server.ts` 112 KB)** 可删,并已写进 `EXCLUDE_FILES` + 探针 + 文案改写规则。<br>🔴 **真相**:它们是 **`dshs-relay.service` 的进程入口**,**两台机器上都在跑**(`active running`,监听 `127.0.0.1:20080`)。控制面**从不 import 服务端族**是**设计使然**:**relay 是另一个进程**,入口是 `main.ts`,由 **systemd `ExecStart`** 拉起。<br>🔑 **为什么静态分析必然错**:**systemd 单元文件不在代码仓里**(实测:源仓 + 导出物 **0 个 `.service`**)⇒ **在仓内做可达性,永远看不到这条接线**。<br>🔑 **用户原话(判据正名)**:**「若某份分析以 dshs 控制面为根做可达性,"零引用"是根进程选错了,不是事实。」** | ⛔ **「从代码入口不可达」不再作为任何删除依据**(见 R-O17 修正)。<br>✅ 必须做**接线核实**:问「**这个子系统的进程入口是哪个?由谁拉起?**」:**systemd / cron / docker / 一键安装脚本 / 手动**,并**去真机看状态**(`systemctl status` / `active running`)。<br>✅ **接线线索极可能不在代码仓** ⇒ **仓内查不到接线时,默认「它在别处被接线」,而不是「它没人用」**。<br>⚠️ **连带教训**:我当时还漏查了**源仓内部文档**:`dsh-server-docs/01-规范/02-运维手册.md` 与 `04-调整方案/28-用户数据清理策略.md` 里对这些运维脚本有**成文流程**(我只扫了导出物)。<br>⚠️ 同批被误判的还有「能力已备、门面未接」的两个:`join.ts`(一键入网,只有 barrel + 测试)· `placement.ts`(选点算法,零调用点)—— 用户判定**保留**:**删掉 = 删掉后续自助入网/自动选点的唯一判据来源** | 本表 #26 · R-O17 |
| **27** | 🔴🔴🔴 **改手工层时只改 `_overlay/`、没改导出仓 ⇒ 改动在重建时被「静默回滚」**(2026-09-19 实测,白做一整轮)—— 我按规范改完 8 个手工层文件(README ×2 / install ×2 / manual/project ×2 / manual/architecture ×2,全部只是删引用、无新内容),直接 `--force` 重建 ⇒ 回执报 **`blocking hits: 36`**,逐条指向**我刚删掉的那些引用**,改动**全部消失**。 | 🔑 **机制**:`stash_overlay()` 的方向是 **导出仓 → `_overlay/`**(重建前快照、重建后还原)⇒ **导出仓里的旧版会覆盖你刚改好的 `_overlay` 新版**。<br>✅ **纪律**:改手工层 = **两处都要落**(`_overlay/<f>` **与** 导出仓 `<f>`,逐字节一致)。<br>✅ **工具**:改完立刻跑 **`_sync_overlay.py`**(`--check` 只报告,不加参数则同步并报 SHA-256)—— 输出 `两处逐字节一致 —— 可以安全重建` 才准重建。<br>⚠️ 口诀仍然有效但**用途不同**:**「`_overlay` 是未来版、导出仓是当前版」** 说的是 **⛔ 绝不允许整目录 `cp` 导出仓 → `_overlay`**(那会把未来版打回当前版);**不是**「只改 `_overlay` 就够了」。 | 本表 #27 · §12.11 |
| **28** | 🔴🔴 **规则用了「跨行锚」⇒ 在按行处理 + 纯 CRLF 的文件上静默空转 ⇒ 关键字段丢失**(2026-09-19)—— 我把版本号规则拆成 `('"version": "0.1.0",\n "description":', …)`,想「先具体后笼统」只在 `package.json` 插入 `license`。重建**成功且全部门禁为绿**,但 `package.json` 的 **`license` 字段整个消失**(此前每一版都有 ⇒ 这是**回退**)。 | 🔑 **两个前提同时不成立**:`sanitize()` **按行处理**(跨行锚永远匹配不上)+ 源仓 `package.json` 是**纯 CRLF**(实测 CRLF=85 / LF=85)⇒ 我的锚里是 `\n`,**必然空转**。<br>✅ **规则里不许出现跨行锚**;要「只对一个文件生效」就用**该文件独有字段的单行锚**(此处改成 `'"description": "面向公网的多租户 DSH 托管平台'`:实测在 `package.json` 命中 1 次、`package-lock.json` **0 次**)。<br>✅ 且必须**核对构建回执里那条规则的 `sanitized` 计数**(空转的话它是 0,一眼可见)。<br>🔴 **根本教训**:**探针只管「有没有不该有的」,不管「该有的还在不在」** ⇒ 凡「由规则生成的关键字段」(`name` / `version` / `license` / `repository`)都要在 `_check_dst.py` 里加**存在性断言**。 | 本表 #28 · §12.12 |
| **29** | 🔴🔴 **首推 `--force` 前没和远端比对 ⇒ 差点删掉「只在远端存在」的授权合规件**(2026-09-19)—— 推送前顺手查远端 `main`(`8dd8071`)的文件树,发现它有 **`.gitattributes`**(173 B:`LICENSE -text` / `COMMERCIAL-LICENSE*.md -text`),而**导出物里没有**(它从未进过构建产物)。若不查,`push --force` 会把它一并抹掉 ⇒ **三份法律文本的逐字节保护静默失效**(`LICENSE` = AGPL-3.0 官方全文 34,523 B)。 | ✅ **首推/强推前必做**:`git ls-tree`(或 GitHub API)**列出远端根文件与本地比对**,把「只在远端存在」的**逐个判定**:该留 ⇒ 做成**构建产出**(内容与远端**逐字节一致**,用 `git hash-object` 对 blob sha 核)+ 进 `REQUIRED_EXPORT`;该删 ⇒ 明确记录。<br>✅ 已把 `.gitattributes` 做成 `_build_export.py` 的 `GITATTRIBUTES` 常量并在 `.gitignore` 之后写出。<br>🔑 通用句式:**「远端有、本地没有」的文件,在下一次强推时等价于「主动删除」**。 | 本表 #29 · §12.12 |
| **30** | 🔴🔴🔴 **源仓工作树不干净时重建 ⇒ 把「别的会话正在写、尚未提交」的中间状态带进公开仓库**(2026-09-19 实测)—— 我为「覆盖网络架构图」重建导出物,回执 `blocking hits: 0`、`required 52/52`,随后 `_check_dst` / `_check_links` / `_check_parity` / `tsc` / `_check_public` **全部为绿**。`git status` 却显示导出物多了 13 个源派生文件被改(`src/db/*`×6 · `src/web/routes/*`×2 · `src/web/server.ts` · `web/*`×4,**+234 行**)。<br>🔴 **真因**:**源仓是「多会话共用」的工作区**,那一刻另一个会话**正在源仓就地改这些文件(未提交)**;而 `_build_export.py` 是**按文件系统当前状态 copy**(不是 `git archive <commit>`)⇒ **工作树脏 = 导出物脏**。<br>🔑 **时间线取证**(决定「不是我干的」):我 12:32 核验时 `git status` **只有 3 行 `??`**(干净);那 19 个文件的 mtime 是 **12:37–12:49**(连续递增),而我那时正在跑 archify 出图;源仓 `HEAD` 始终 `971ccc3`、`git diff --cached` 为空、`.git` 下 **11:27 之后零写入**、无新 ref ⇒ **我零 commit / 零 add / 零写入**。<br>⚠️ **最危险的一点**:这类污染**任何探针都查不出来**:探针只回答「**有没有不该有的**」,**不回答「内容是否已定稿」**。 | ✅ **新增前置闸门**(`_build_export.py#dirty_src_files()`,事故后立即落地):重建前跑 `git -C <源仓> status --porcelain`,**只要出现已跟踪文件的 `M`/`A`/`D`/`R` 就默认拒跑(return 4)**,列出全部文件名,并提示「等那个会话提交(推荐)/确认过才加 `--allow-dirty-src`」。<br>⚠️ 🔴 **作用域必须收窄到「会进导出物的路径」**(2026-09-19 实测后修):第一版把**任何**已跟踪改动都判脏,立刻在源仓的 `.gitignore` 上**误报**并拦下构建 ⇒ 按案例库 C5「**假阳性过多 = 等于没有工具**」收窄为「路径落在 `INCLUDE_DIRS` 下或与 `INCLUDE_FILES` 同名」才拦,其余只提示。<br>🔴 **且 `??`(未跟踪)必须一起拦**:第一版注释写「白名单取文件,未跟踪天然进不来」,该理由**对 `INCLUDE_DIRS` 内部不成立**:`copy_item()` 是 `os.walk` **整目录复制**,会连未跟踪的新文件一起复制(实测抓到 `src/platform-paths.ts`,它本会被静默发布)。<br>✅ 源仓脏但必须重建时:`--allow-dirty-src` 重建后,**在导出仓**用 `_revert_src_derived.py` 退回(已跟踪用 `git checkout --`,未跟踪直接删)—— ⛔ 绝不碰源仓。<br>⚠️ **⛔ 绝不为了「让闸门通过」去动源仓**(不 stash、不 checkout、不 commit 别人的工作树)—— 那是别人的任务边界。<br>✅ **发现已污染时的处置**:在**导出仓**(不是源仓)用 `git checkout -- <显式路径>` 把这批源派生文件退回上一次干净提交,**保留本次真正要发布的改动**(文档 / 图),然后**暂停推送**并报告。 | 本表 #30 · §14 |
| **31** | 🔴🔴 **用 `bash heredoc` 写一次性脚本 ⇒ 反斜杠与非 ASCII 被静默损坏**(2026-09-19 实测两次)—— ① 用 heredoc 传 Python 时,字符串里的 `\n` **被吞掉反斜杠**,写成**字面 `/n`** ⇒ README 里两处该有的空行消失(肉眼极难发现,因为渲染出来只差一个空行);② 同一原因,**em-dash `—` 被改写** ⇒ `old_string` 匹配 **0 次**,脚本报「命中 0 次(应为 1)」而真正原因与「文本不对」无关。<br>🔑 **本机 bash 环境特有**:`heredoc` 会解释/吞掉内容里的反斜杠与部分非 ASCII 序列。 | ✅ **一次性脚本一律用 Write 落盘、再执行**(不要 heredoc、不要内联 `python -c "..."` 传含反斜杠/非 ASCII 的长文本)。<br>✅ 同理:**内联 `-c` 里不要出现反引号**:会被 shell 当命令替换(实测 `<code>overlay-architecture</code>` 被当命令执行报 `command not found`)。<br>✅ 写入后用**探针式自查**收尾:搜 `code:/n` 这类**不该存在的字面串**,比人眼可靠。 | 本表 #31 |
| **31** | 🔴🔴 **用 `bash heredoc` 写一次性脚本 ⇒ 反斜杠与非 ASCII 被静默损坏**(2026-09-19 实测两次)—— ① 用 heredoc 传 Python 时,字符串里的 `\n` **被吞掉反斜杠**,写成**字面 `/n`** ⇒ README 里两处该有的空行消失(肉眼极难发现,因为渲染出来只差一个空行);② 同一原因,**em-dash `—` 被改写** ⇒ `old_string` 匹配 **0 次**,脚本报「命中 0 次(应为 1)」而真正原因与「文本不对」无关。<br>🔑 **本机 bash 环境特有**:`heredoc` 会解释/吞掉内容里的反斜杠与部分非 ASCII 序列。 | ✅ **一次性脚本一律用 Write 落盘、再执行**(不要 heredoc、不要内联 `python -c "..."` 传含反斜杠/非 ASCII 的长文本)。<br>✅ 同理:**内联 `-c` 里不要出现反引号**:会被 shell 当命令替换(实测 `<code>overlay-architecture</code>` 被当命令执行报 `command not found`)。<br>✅ 写入后用**探针式自查**收尾:搜 `code:/n` 这类**不该存在的字面串**,比人眼可靠。 | 本表 #31 |
---
## 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-17 定稿口径)** | **「人负责规划与关键判断,AI 负责实施」**(英文 `Human planning and key judgment; implementation by AI`)—— 模型 `DeepSeek V4 / V4.1 flash`(**不写工具名**)。⛔ 不要写成「全部由 AI 生成」(法律风险:中国版权保护中心 2026 新规「纯 AI 生成的软件不予登记」⇒ 商业授权缺标的物);⛔ 也不要照 2026-09-13 删掉「人的角色」。落点见下方「本项目定稿顺序」 |
| ~~**文档语言**~~ | ⛔ **本行已作废**: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」) |
| 🔴 **新名称(2026-09-17 用户定,🟡 执行中)** | **`dsh_ai1net`**:中文名 **「能力网络」** | 英文名 **`DSH AI1NET`** | 技术标识 `dsh_ai1net` / `DSH_AI1NET_*`。用户原话:「是 `E:\ProgramData\AI技能\aliyun-dsh-server` 对应**开源项目的新名称**」。**旧仓 `maogeigei/dsh-users-platform` 保留不动**(用户 09-16「之前的不动」),新仓 `maogeigei/dsh_ai1net` 已建(空仓),密钥 `id_ed25519_ai1net` 已绑。<br>**口径 = 方案 A(用户 09-17 08:18 选定)**:env 前缀 → `DSH_AI1NET_*` · 数据根 → `/var/lib/dsh-ai1net` · 库文件 → `dsh_ai1net.db`(与源仓在此项上**永久分叉**,映射规则在导出层)。<br>✅ **已完成(09-17 08:3x)**:`_build_export.py` **46 行规则** + 3 条旧名探针 · 6 个工具脚本 · `_overlay/` **13 文件 139 处**:均静态验证通过。<br>⏳ **待做**:源仓提交出冻结基线 → 重建 → `.git` 迁移 → 全量验证 → 推送新仓。<br>📄 作业书:`_改名执行清单_dsh_ai1net_20260917.md`(§零 进度 / §三 规则清单 / §四 步骤) |
| **曾用名(全部必须在 `LEAK_PROBES` 里)** | `dshs` · `dsh-multitenant` · `dsh-hosting` · **`dsh-web-platform`** · `DSH_WEB_PLATFORM_*` · `/var/lib/dsh-web-platform`;`taimiao` 未落地也一并加入。**⚠️ 改名 `dsh_ai1net` 执行后,须把 `dsh-users-platform` / `DSH_USERS_PLATFORM_*` / `/var/lib/dsh-users-platform` 补进 `LEAK_PROBES`** |
| **前名(已废弃,现为阻断探针项)** | `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-ai1net-github\`(2026-09-13 由 `aliyun-dsh-server\_开源导出_20260913\` 整体迁入;**2026-09-16 由 `dsh-laijing-github` 改名**;**本项目的独立开源工作区**,不再是 `aliyun-dsh-server` 的子目录) |
| **仓库根(可直接 git init)** | ✅ **现行 = `<导出根>\dsh_ai1net\`(255 文件,2026-09-19 重建)**;旧 `<导出根>\dsh-users-platform\`(198 tracked)**已冻结,仅供对照**。⚠️ `_build_export.py` 的 `DST` 已指向 `dsh_ai1net` ⇒ **重建只会写新目录** |
| 构建脚本 | `<导出根>\_build_export.py`(**默认拒跑**,须 `--force`) |
| 手工撰写层快照 | `<导出根>\_overlay\`(脚本自动维护) |
| 类型检查 | `<导出根>\_verify_tsc.mjs`(建 junction → tsc → `rmdirSync` 拆) |
| 文档校验 | `<导出根>\_check_links.mjs <repoDir>`(链接/锚点/配图)· `<导出根>\_check_parity.mjs <repoDir>`(中英对等 + 列出英文里的汉字;**英文文档汉字数应恒为 4**) |
| 🔴 **已公开仓库巡检**(R-O15) | `<导出根>\_check_public.py [<dir> …]`:**形态级**审计(内网 IP / 主机号 / 内部端口 / 凭据字面量 / 内部绝对路径 / 内部单据名)。默认扫 `dsh_ai1net` + `dsh-users-platform`;**exit≠0 = 有命中**。与构建期的 `LEAK_PROBES`(字符串级)**互补**,缺一不可 |
| 🔴 **「不用开源」审计**(R-O17) | `<导出根>\_check_unused.py [<dir>]`:判据「**缺少也不影响项目运行**」。给三张单子:① 死文件(无任何引用)② 从 `package.json` 的 `main`/`bin` 经真实 import 图**不可达**的 src 模块 ③ 名字像运维/辅助且不在 `package.json` 里。**exit≠0 = 有候选**。⚠️ **只报告不删**:范围收缩影响对外可见面 ⇒ 必须人工确认 |
| 🔴🔴 **手工层同步器**(2026-09-19 新增) | `<导出根>\_sync_overlay.py [--check]`:把 `_overlay/<f>`(**权威源**)复制到导出仓。**改完任何手工层文件就立刻跑它**:报 `两处逐字节一致 —— 可以安全重建` 才准重建。<br>🔑 起因见事故 **#27**:`stash_overlay()` 方向是 **导出仓 → `_overlay/`** ⇒ 只改 `_overlay` 会在重建时被旧版**静默回滚**(实测 8 个文件的改动全丢、`blocking hits: 36`) |
| 给人看的台账 | `<导出根>\_导出说明与脱敏台账.md`(**不随仓库上传**;§七 = 改名记录) |
| 手工撰写层(`OVERLAY`:重建时自动快照→恢复,**不得被抹掉**) | **现为 30 份**:`README.md` · `LICENSE`(**AGPL-3.0,2026-09-14 按用户选定 B 方案加回**)· **`COMMERCIAL-LICENSE.md` + `.zh-CN.md`(2026-09-18 新增 · 双轨的轨 2)** · `PLUGIN-PORTING.md` · `install.sh` · `install.md` · `AGENTS.md` + 5 个 `*.zh-CN.md` + `manual/` **9 对 18 份**(2026-09-17 新增 `decisions.{md,zh-CN.md}`) |
| 当前版本 | **v1.2.0 / 2026-09-15**,已发布至远端 `main` = **`25f930f`**(普通推送累计;`332afff` 后追加商业轨文件)| 首发 v1.0.0 / 2026-09-13 | v1.1.0 / 2026-09-14 |
| 计数现状(2026-09-18 AST 实读) | `OVERLAY` **30** | `REQUIRED_EXPORT` **60** | `INCLUDE_DIRS` **6** | `INCLUDE_FILES` **8** | `EXCLUDE_FILES` **9** | `DROP_SCRIPTS` **28** | `LEAK_PROBES` **75** | `INFO_PROBES` **2** | `REGEX_RULES` **73** | `GLOBAL` **71** | `K8S_SEMANTIC_RULES` **14** | `TEXT_EXT` **16** | `EXTRA_TREES` **3** |
| 上游基线(三方) | `上游骨架仓库(已按要求不再具名)` → **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-ai1net-github\`**(独立 GitHub 开源文件夹)。**不要再改名或迁移**:这个名字被 **5 个工具脚本** + 本技能 + 项目 MEMORY 同时引用。真要迁:① 先改**全部 5 处脚本常量**(`_build_export.py:13` `OUT` · `_verify_tsc.mjs:5` `EX` · `_sop_check.mjs:5` / `_upload_audit.mjs:5` / `_verify_all.mjs:5` `W`)→ ② 再搬/删目录 → ③ 复跑重建 + tsc,并检查旧目录**没有被重建脚本重新建出来**(2026-09-13 曾因漏改 `OUT` 而复活一次;**2026-09-16 由 `dsh-laijing-github` 改成现名时,已于 09-17 按此顺序把 5 处全部同步完**)。
---
## 1. 硬规则 R-O1–R-O17(含 2026-09-19 真机验证修正)= 用户原始要求 + 实际踩过的坑,任何会话**不得放宽**)
> 🔴 **R-O15(已公开仓库巡检与清除)是 2026-09-19 用户点名的「重点」规则**:见 R-O14 之后。
> 🚫 **R-O16(测试用例不开源)· R-O17(范围收缩判据「缺少也不影响运行」)** 同上,见 R-O15 之后。
| # | 规则 | 判据 |
|---|---|---|
| **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、`08-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-17 定,🟡 执行中)**:中文 **「能力网络」** | English **`DSH AI1NET`** | 技术标识 **`dsh_ai1net`**(详见 §0 表「新名称」行)。<br>**同口径历史**:中文 **DSH 用户平台** | English **DSH Users Platform** | 技术标识 **`dsh-users-platform`**(2026-09-14 定,线上旧仓仍用此名,**冻结不动**)。<br>⚠️ **已排除的候选**:**`dsh-hive`**(第三方插件 `llluchy/dsh-hive` 已占用);**`dsh-hosting`**(中间候选,已被取代 ⇒ **仓库里不得残留**,已列入阻断性探针);**`taimiao`**(仅候选,未落地)。 |
| 2 | **改完全部自指标识**:包名 / bin / cordis plugin id / env 前缀(36 种)/ 数据根 / systemd 单元 / nginx conf / 库文件名 / 镜像与 k8s / 导出目录名 | 全仓 `grep` 旧名,**只剩出处引用**才算干净 |
| 3 | **保留出处并正式致敬,但只放在文末**(用户 2026-09-13 明确:"在最后提一下引用了谁就行,不要上来就重点讲用了谁") | ① 上游 = **DeepSeek Harness(MIT)**,**本项目不包含、不分发其代码** ⇒ 无需单独 MIT 声明文件,README 文末保留**一行道义致敬**(`dshs`,MIT,⛔ 不许删);② README **不得在开头单独设「名称与渊源 / 基于 XX」章节**:出处只出现在**文末**的「第三方组件与致谢」里,**一句话带过**;③ 本项目的法律归属由 **`LICENSE`(AGPL-3.0 官方全文)** + README 末节「授权与商业使用」承载 |
⚠️ **改名时最容易误伤的两处**:① 把「致敬上游」的引用一起改掉(= 抹掉出处,法律与道义双重问题);② 把脚本里作**源模式**的旧名改掉(见 §8 坑 9)。
### R-O9 · 只写「已验证的」能力
> 用户原话:「模式 B · Kubernetes **去掉这个,根本没验证**,应该是**待开发验证**」。
| 规则 | 做法 |
|---|---|
| **宣称必须可复现** | 只把**真正跑过端到端验收**的路径写成「可用」。**有代码 + 有单元测试 + 有 PoC 记录,都不等于可用** |
| **未验证的照实标注,不删代码** | 保留代码与清单,但标注 **「实验性 · 未验证」**,并与可用路径**在同一张表里用状态行对照**,不要让读者自己猜 |
| **删掉「已完整落地 / Phase 0–4」式表述** | 这类词最容易把"写完了"说成"验证过了"(2026-09-13 从「部署形态」章节删掉的就是它) |
| **配置表 / 脚本文案同步标注** | 未验证路径的 env 变量统一加「(模式 X · 未验证)」前缀;`install.sh` 的报错/提示文案要与 README 口径一致 |
| **FAQ 给一句直答** | 加一条「X 现在能用吗?」→ 直接回答「**还不能**」,并说清**缺哪一步**(如「从未做过端到端部署验收」) |
| **归属措辞也要改** | 出处/致谢里描述对方贡献时,把「双部署**形态**」降级为「双部署**框架**」,避免暗示未验证的路径是可选项 |
| **落点清单(照抄)** | README:部署形态表 + 快速开始 + 配置表 + 功能详解 + 安全模型 + 亮点 + 目录结构 + FAQ;`install.sh` 文案;README **末节「授权与商业使用」**与**文末致敬行**的措辞 |
---
---
### R-O10 · 出处的位置与措辞
> 用户原话:「在最后提一下引用了谁就行,**不要上来就重点讲用了谁**,感觉是在宣传别人的项目,已经很多地方深度改造过了。」
| 规则 | 说明 |
|---|---|
| **位置** | 只在**文末**(README 的「第三方组件与致谢」+ 末节「授权与商业使用」)。**不得**在开头/前部单设「名称与渊源」「基于 XX」章节 |
| **主语** | 正文一律**以本项目为主语**。写「本项目做了…」,**不要**写成「上游提供了骨架,我们在此基础上…」这种自我降格的框架 |
| **措辞** | 用「**起步时参考了** X(作者,许可证),**感谢作者开源**」;**不要**用「仅仅接着往前走了一步」「离开它就没有这个项目」这类过度抬举,**也不要**补「我们做了大量深度改造」这类**自我表扬**(见下表「致谢只写一行」) |
| **边界** | 「轻描淡写」**只作用于 README 正文**;**`LICENSE`(AGPL-3.0 官方全文,逐字节)与 README 末节的授权说明一处都不能省** |
| **JS/注释里的名字** | 代码注释里也**不主动**出现上游名(它们已经被 R-O8 第 2 步改成新名;只剩出处语境才允许) |
| **致谢只写一行**(2026-09-13 第七次纠正) | 出处行 = **「起步时参考了 X(作者,许可证),感谢作者开源」**,一行结束。**不要**写:① 骨架范围的枚举(那是 `LICENSE` 与 README 末节授权说明的职责,**重复即冗余**);② 「此后我们做了大量深度改造」(**自我表扬**,致谢不是讲功绩的地方);③ 「项目名从它的 X 换成我们的 Y(避免指代混淆…)」(**内部事务**,读者不关心 ⇒ 不进公开文档)。用户原话:「**有必要讲这么多吗,好好想想**」 |
| **别解释我们为什么这么写** | 「—— 这是 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 · **code & docs-human-planned, AI-implemented** · DeepSeek V4/V4.1 flash;**无 License 徽章**)
→ Hero(三条主线 + **一行「人负责规划与关键判断,AI 负责实施」** + 拓扑图)→ 快速开始片段
目录 → 亮点 → 快速开始 → 功能详解 → 架构 → 安全模型 → 部署形态 → 配置
→ 控制面 API → 插件移植指南 → 开发 → 常见问题 → 目录结构 → 版本与迭代 → 贡献
→ 第三方组件与致谢 → 授权与商业使用(必须最后)→ 授权摘要收尾(全文唯一一处摘要)
```
> 🔴 **「本项目由谁建成」= 现行唯一口径(2026-09-17 11:48 用户定):「人负责规划与关键判断,AI 负责实施」**(英文 `Human planning and key judgment; implementation by AI`)。
> · **落点**:README Hero 的 `code & docs-human-planned, AI-implemented` 徽章 + 一行声明 · `manual/project.{md,zh-CN.md}` 的 **`## How this project is built` / `## 项目如何建成`**(一行声明 + 2 行「环节 → 由谁负责」表:人 = 方向/范围/架构决策/评审验收;AI = 代码/测试/文档/插件改造示例)。
> · **为什么必须这样写**:中国版权保护中心 **2026 新规「纯 AI 生成的软件不予登记」**,判据 = **人类独创性智力投入**。旧口径「全部代码与文档由 AI 生成」= **自认纯 AI** ⇒ 商业授权**缺标的物**;新口径**恰恰确立人类投入** ⇒ 是**法律加固**,⚠️ 不要简化回「AI 生成」(详见 §5 与 `<工作根>\_授权决策与法律依据_20260917.md`)。
> ⛔ **不要再写回「全部由 AI 生成」**(用户 09-17 **11:44 曾短暂要求全部删除,11:48 改为本条**);⛔ **也不要照 2026-09-13 的「删掉人的角色」**,两者均已被推翻。
> ✅ 一并保留:`DeepSeek V4 / V4.1 flash` 模型标注徽章 · README 的 `**能力网络** · **DSH AI1NET**` 行。
| # | 硬规则 | 理由 |
|---|---|---|
| 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`:**AGPL-3.0 官方英文全文,逐字节不许翻译或改写** |
| **校验** | 英文版同样要跑 **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`。
---
### R-O15 · 🔴🔴 **已公开仓库巡检与清除**(2026-09-19 用户点名升级为**重点硬规则**)
> 用户原话:「**顺带修掉两处既有公开泄漏(不是本轮引入,已随旧仓公开)…!!!严格排查是否还有相同问题!!!,把已经公开的仓库清除相关信息**」,
> 随后:「**然后把这个作为重点!!!强化到规则中**」。
**为什么必须上升到硬规则**:整个流程过去只盯「**新导出物**干不干净」,**从没回头巡检已经躺在公网上的仓库**。
实测代价:`dsh-users-platform` 公开多日,里面**逐个含内部主机号 / 内部隧道端口 / PG 口令**的 7 个 `verify-cluster-*.mjs` + `start-cluster-manager.sh` 一直挂着。
⇒ **公开发布不是终点;只要仓库在公网上,就必须周期性回头查。**
| 项 | 规定 |
|---|---|
| **① 何时查** | **每次整理 / 发版前后各一次**;任何一次「严格排查」都必须**同时覆盖两处**:新导出物 **+ 每一个已公开仓库** |
| **② 用什么查** | `<导出根>\_check_public.py [<dir> …]`(**常驻工具**,默认自动扫 `dsh_ai1net` 与 `dsh-users-platform`;**exit≠0 = 有命中**) |
| **③ 命中后三件一起做** | ⓐ 补成 `_build_export.py` 的**规则 + 探针**(⛔ 只清新仓 = 下版回潮,见 #17)<br>ⓑ **就地清理已公开仓库**(删危险文件 / 替换敏感字面量)<br>ⓒ 提交 + 推送(**注意**:前向提交**不会**从历史里抹掉,要彻底清除须改写历史,属「不可逆 + 影响面」事项,**必须先问用户**) |
| **④ 判据(正名)** | **「换一个部署者,这个值会不会一样?」**:会 ⇒ **通用事实,允许**(云厂商元数据端点 `100.100.100.200`、云内网 DNS、docker 网桥 `172.17.0.1`、RFC1918 `10/172.16–31/192.168`、RFC5737 `192.0.2/198.51.100/203.0.113`、测试夹具 `w-1`/`w-999`/非法 IP);<br>不会 ⇒ **泄漏**(**我们的**主机号、隧道端口、服务器/测试机 IP、口令字面量、内部单据名、内部绝对路径) |
| **⑤ 同一把尺子** | ⛔ **不许「新仓严、旧仓松」**。新旧仓用**同一个** `_check_public.py`、同一份 `LEAK_PROBES`。发现新形态 ⇒ 加进工具的 `PATTERNS` **并**补 `_build_export.py` 的规则+探针(**两处都要动**) |
| **⑥ 与 `LEAK_PROBES` 的分工** | `LEAK_PROBES` = **已知字符串**(构建时自动跑,防回潮);`_check_public.py` = **形态**(IP / 主机号 / 端口 / 凭据 / 内部路径 / 内部单据,用来发现**规则还没覆盖**的新泄漏)。**两者互补,缺一不可** |
**本轮(09-19)实际清出的存量**(全部为**既有公开泄漏**,非本轮引入):
| 类别 | 实例 | 处置 |
|---|---|---|
| 内部集群脚本(8 个,**逐个含主机号+端口+PG 口令**) | `verify-cluster-{agent,cross,domain,fs,lease,live,migrate}.mjs` · `start-cluster-manager.sh` | 旧仓**删除**;新仓规则层 `DROP_SCRIPTS` 同处置 |
| 内部主机号 | `w-106`(14 处)· `w-47` · 无连字符 `w106`/`w47` · 标识符 `w47Secret` | → `<worker-b>` / `<worker-a>` / `node-NN`(**命名体系整体抹掉**,保序号以维持测试区分度) |
| 内部端口 | `15432`(PG 隧道)· `19000`/`19001`(隧道监听)· `32022`(sshd) | → `<PG_PORT>` / `<TUNNEL_PORT_1..2>` / `<SSH_PORT>` |
| 内网地址 | `10.0.1.11`(注释里写作「agent 的内网地址」)· `10.0.0.5` 等夹具 | → `<HOST_LAN_IP>` |
| PG 测试口令 | `dshs_cluster_test` / `dsh-users-platform_cluster_test` | → `_<TEST_DB_PASSWORD>` |
| smoke 口令字面量(**读起来像真口令**) | `adminpass123`(配 `username:'admin'`)· `bob/alice/carol/frank/gina/root` + `pass123` | → 统一 `smoke-test-password`(脚本自建用户再用,改值不影响行为) |
| 内部单据引用 | `交接单_网抽象与地址规划R6 §待办②` · `交接单 T05` · `` `04-调整方案/133-…md` §2.2 `` | → `设计说明` / `` `the design note` ``(**从 INFO 升格为阻断**:公开仓里它既是内部信息又是**悬空指针**) |
**配套两条纪律**(本轮新踩,同样进规则):
- 🔴 **drop 任何文件前,查「谁读它」必须同时查文档与代码**(`readFileSync` / `join(` / `import` / `require` / `spawn`)—— 只看文档会误删被测脚本(事故 #18)。
- 🔴 **纯数字 / 纯字母数字的内部标识(端口 `15432`、主机号 `w106`)不得裸替**:会命中 base64 哈希,症状是 `npm ci` 校验失败且与改动无关(事故 #19)。
---
### R-O16 · 🚫 **测试用例相关文件和代码不开源**(2026-09-19 用户定稿)
> 用户原话:**「测试用例相关文件和代码 不开源」**。
| 项 | 规定 |
|---|---|
| **不带** | ① `test/**`(单测套件)② `07-scripts/smoke*.mjs`(端到端冒烟,**也是测试用例**)③ `07-scripts/fake-*.mjs`(只为冒烟存在的**测试替身**) |
| **仍要带** | `07-scripts/verify-*.mjs\|cjs` 族:它们**不是**测试用例,而是**产品自带校验**(`verify-inject.cjs` 被 `AGENTS.md` 指引、`verify-static.mjs` 被 `web/i18n.js` 调用、`verify-model-landing.mjs` 被 `src/web/model-landing.ts` 引用、`verify-dsh-install.mjs` 是安装自检)。判据 = **「谁读它」**(见事故 #20) |
| **四处连带**(缺一即留断链) | ① `package.json`:删 `test` / `smoke*` 入口、摘掉 `verify` 里内联的 `node --test …`、**并修掉尾逗号**(`verify` 会变成最后一项)② `src/**` 与保留下来的 `07-scripts/**` 里**引用测试文件**的注释(会变悬空引用)→ 中性化 ③ `.github/workflows` 里的测试口令字面量 ④ **文档能力宣称**(OVERLAY 手工层:`README{,.zh-CN}` 能力表 · `AGENTS{,.zh-CN}` · `manual/{architecture,highlights,project}{,.zh-CN}`) |
| **兜底探针** | `LEAK_PROBES += [".test.mjs", "node --test", "07-scripts/smoke", "fake-dsh.mjs", "fake-setpriv.mjs", "ci-test-pass-123"]` ⇒ 任何回潮都会让构建**判失败** |
| ⚠️ 最易漏 | **文档宣称**(`_check_public.py` / `LEAK_PROBES` **扫不出**「文档说我们有 9 个冒烟」这种语义不一致)⇒ 收缩范围时**必须人肉把 README / AGENTS / manual 过一遍** |
> 📖 **配套**:本轮所有泄漏实例(含真实值与「为什么没发现」)已整理成 **`<工作根>\_负面案例库_开源前必读.md`**:**每次开源前必读**,且该文件**不得进任何公开仓库**。
---
### R-O17 · 🚫 **范围收缩判据:「缺少也不影响项目运行」**(2026-09-19 用户定;同日经真机验证**修正**)
> 用户原话:**「我说的不用开源 是指缺少也不影响项目运行 比如之前的 测试用例」**。
> R-O16(测试用例不开源)与 R-O14(K8s 不带)都是这条判据的**实例**。
#### 🔴🔴 首要铁律(2026-09-19 真机验证倒逼立,**违反即可能删掉生产在跑的进程入口**)
> **「从代码入口不可达」≠「不影响运行」。**
> 用户原话:**「若某份分析以 dshs 控制面为根做可达性,"零引用"是根进程选错了,不是事实。」**
| 事实 | 说明 |
|---|---|
| **多进程系统里,"别人的入口"从本进程看必然不可达** | 本项目实测:`src/net/relay/{server,main,index,join,keys,placement}.ts` + `content/runtime.ts`(**214 KB,`server.ts` 单文件 112 KB**)从控制面 `main`/`bin` **完全不可达**:因为 **relay 是另一个进程**,入口 `main.ts` 由 **systemd `ExecStart`** 拉起,生产上两台中继 **`active running`**、监听 `127.0.0.1:20080`。控制面不 import 服务端族是**设计使然** |
| **接线线索常常不在代码仓** | 实测:源仓 + 导出物 **0 个 `.service` / `.timer`** ⇒ **在仓内做可达性,永远看不到 systemd 接线** ⇒ **仓内查不到接线时,默认「它在别处被接线」,而不是「它没人用」** |
| ⇒ **所以必须先做「接线核实」** | 问:**这个子系统的进程入口是哪个?由谁拉起?**(`systemd` / `cron` / `docker` / 一键安装脚本 / 手动),**并去真机看状态**(`systemctl status` / `list-units`)。<br>⚠️ **答不上来 ⇒ 不能删**(不是"先删了再说") |
| ⚠️ **「能力已备、门面未接」也要保留** | `join.ts`(一键入网:只有 barrel + 测试,它要 POST 的控制面端点尚无路由)· `placement.ts`(节点选点算法:**零调用点、零测试**)—— 用户判定**保留**:**删掉 = 删掉后续自助入网/自动选点的唯一判据来源**。⇒ **「未接线」≠「死代码」** |
#### 判据与四步
| 项 | 规定 |
|---|---|
| **判据** | **缺少这个文件,项目还能照常跑吗?** 不能 ⇒ **必带**;能 ⇒ 进候选,再看它是否属「对外文档」 |
| ⛔ **不是**判据 | **「从代码入口不可达」**(首条铁律,最危险)<br>「有没有人引用」(`web/i18n.js` 只在**注释**里被提到,却是**运行时静态资源**)<br>「名字像测试」(`verify-*.mjs` 名字像测试,实为**产品自带校验**,见 R-O16)<br>「在不可达单里」(`semver-shim.d.ts` 不可达,但删了 **`tsc` 报 TS7016**) |
| **必须走四步** | ① ~~真实 import 图 + 可达性~~ → **改为「接线核实」**:系统有几个**进程入口**?各自的**拉起方式**是什么?<br>② **移除闭包**:候选集移除后,**保留文件还有没有边指向它们**(0 条只是**必要条件,不是充分条件**,见铁律)<br>③ **查界面牵连**:`grep` 文件名在 **`.ts`** 里的位置:若出现在**返回给前端的字符串**里(API 响应字段 / 报错提示),**必须连文案一起改**<br>④ **查引用**:⚠️ **三个维度都要查**:导出物文档 · **代码** · **源仓内部文档**(`dsh-server-docs/01-规范/02-运维手册.md` / `04-调整方案/` / `05-交接单/`)。<br>  🔴 本轮我**只查了导出物**,于是漏掉「这些运维脚本在运维手册里有成文流程」这一铁证 |
| **工具** | **`_check_unused.py`**(只报告不删;给三张单子:死文件 / 入口不可达的 src 模块 / 名字像运维辅助的脚本)<br>🔴 **只报告**:范围收缩属「**影响对外可见面**」⇒ **必须人工确认**;<br>🔴 且**它的「不可达」单必须配合接线核实才能用**:该单里的 **relay 服务端族是生产在跑的**。 |
| **固有风险项** | **二进制(图片)不受任何探针覆盖** ⇒ `screenshots/` 是否含真实用户名/域名/IP **只能人工逐张目视**,**每次发版前必看** |
**本判据下已确认的两类**(2026-09-19 实测):
1. **内部运维 / CI / 死文件**:`ci.sh` · `session-gc.cjs` · `ws-cleanup.cjs` · `clean-ws-pollution.cjs` · `purge-trash.sh` · `storage-report.cjs` · `instance-mem-sample.cjs` · `mksess.cjs` · `migrate-sqlite-to-pg.mjs` · **`build-web.mjs`(空操作占位)** · **`.github/workflows/build.yml`(监听 `master` 而导出仓是 `main` ⇒ 永不触发)**
2. **独立 daemon(最隐蔽)**:`src/net/relay/` 的**服务端族** 7 文件 / **214 KB**:平台**从不 import**(只用 `client` 侧拨出),服务端是**我们自己中继节点**上跑的软件,头注含内部拓扑(「47 无本机防火墙」)。🔑 **靠「可达性 + 移除闭包」两步才看得出来**
⚠️ **连带纪律**:删 relay 服务端族时,`src/net/relay/jitter.ts` 头注写着「与 `07-scripts/overlay-jitter.cjs` 逐字同口径」⇒ 若同批删那个脚本,**要改成中性说法**(否则是悬空引用)。
---
## 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 必须一致**,同步前**先抢全局执行锁**。
> 📂 **保留/移除清单 + 脱敏映射表 + 保留未动三节已下沉** → `references/01-保留移除与脱敏口径.md`(**改脱敏口径 / 保留范围前必读,它是唯一口径**)
## 5. 授权结构(**现状:2026-09-17 定案「双轨」—— AGPL-3.0 原样 + 商业授权**)
> 🔴 **现状(以本条为准,覆盖本章所有历史描述)**:**双轨授权**:
> - **轨 1 · 开源轨 = AGPL-3.0 原样(默认)**:`LICENSE` = AGPL-3.0 官方全文(**34,523 B,逐字节未改**);`package.json` 保持 `"license": "AGPL-3.0-only"`;已在 `OVERLAY` 与 `REQUIRED_EXPORT`(**60 项**)内。
> - **轨 2 · 商业轨**:给「**不愿承担 AGPL 开源义务**」(闭源托管 / 嵌入专有产品)的人 ⇒ 联系 `maogeigei@gmail.com` 谈条款与报价。**商业授权是「另开一条平行许可路径」,不是「对 AGPL 加限制」**:这是它不违反 OSD 的关键。**自 2026-09-18 起轨 2 有独立文件**:`COMMERCIAL-LICENSE.md` + `.zh-CN.md`(提交 `25f930f`)。
> - 🔴 **三条措辞铁律(2026-09-18 立,⛔ 违反即等于把项目踢出开源)**:① 全文写成「**平行的许可选择**」(parallel choice),⛔ **绝不**写成"对 AGPL 附加的限制";② **文件名刻意不以 `LICENSE` 开头**:免被 licensee 误判为主许可;③ **正文不含项目名**(含新名 / 旧名)⇒ **改名窗口期两版通用**,零泄漏面。
> - 🔴 **归档里的自拟 `LICENSE` 不得放回**:`_授权归档_发布时再放回\LICENSE`(5,931 B,自拟「个人免费/商用须书面授权」)的 §三「商业使用必须事先取得书面授权」**放在已发布 AGPL 项目里 = §10 追加限制** ⇒ 正是罗盒案否定、09-17 定案要避开的那一步。**归档 ≠ 照原样拷回**(逐项判定 → `_导出说明与脱敏台账.md` §F)。
> - **README 末节** = `## License` / 中文 `## 授权`,承载双轨表述(各 4 段),**授权压轴**符合 R-O12;⚠️ 改后须复核 **英文文档汉字数 = 4**。
> - ⛔ **仍有意不带回**:`LICENSE-UPSTREAM-MIT.txt` · `THIRD-PARTY-NOTICES.md`。上游口径:**DeepSeek Harness(MIT,不包含、不分发其代码)**;README 文末保留一行对 `dshs`(MIT)的道义致敬(⛔ 不许删)。
> - ✅ **发版前必查**:`LICENSE` 被 OVERLAY 正确恢复 · `package.json` license 由规则生成 · `required present: 60/60` · **README 双轨表述中英同构** · **轨 2 文件存在且中英对等(`_check_parity.mjs` 的 `PAIRS` 已登记)**。
> 🔴🔴 **两条铁律(2026-09-17 用户连问两轮后确立,不许放宽)**:
> 1. **绝不在 AGPL 上加任何限制**:包括「只禁止销售源项目」这种看起来很窄的限制。依据:AGPL **§10** 禁追加 further restriction、**§7** 把非许可性附加条款定义为 "further restriction";且 **OSD 第 6 条「不得歧视任何领域」** ⇒ **判据是「有没有附加限制」,不是「限制多窄」**(Commons Clause 官方对 "Is this Open Source?" 的回答就是 **No.**)。
> 2. **也不需要加**:AGPL 的 copyleft **本身就实现了**「防白嫖转售」:想**闭源**改造后销售/托管的人,**§13** 要求其向使用者提供全部修改源码 ⇒ 不愿开源就只能来买商业授权。**这就是双轨的全部机制。**
> ⚠️ **两条配套事实**:Commons Clause 原文 "The combined text **replaces** the existing license"(**只能替换、不能叠加**);官方 FAQ "Licenses applied to previous versions are **not revoked**"(**已发布版本收不回** ⇒ 本项目 v1.0.0–v1.2.0 永远是 AGPL)。
> 📜 **历史(2026-09-13 15:1x – 09-17 的中间状态,仅作留痕,⛔ 不是要求)**:一度把三份授权文件**全部移除**(`LICENSE` / `LICENSE-UPSTREAM-MIT.txt` / `THIRD-PARTY-NOTICES.md`),`package.json` 的 license 回到上游原值 `MIT`;09-14 一度**记录**为「AGPL + 商业授权」,但**当时 README 实际只有 AGPL 单轨**(09-17 核实并改正后真正落地双轨)。原文备份在 **`E:\ProgramData\_已移出项目_授权归档\_授权归档_发布时再放回\`**(⚠️ 09-13 曾误记为"该目录已不存在",**2026-09-18 实测仍在**;恢复步骤见 `_导出说明与脱敏台账.md` §F)。
> ⛔ **不要再照那段历史去移除 `LICENSE`**:那会让公开仓库退回「保留所有权利」状态。
> ⚠️ **硬法律前提(客观事实,任何会话都不许删)**:**上游 DeepSeek Harness 是 MIT,且本项目不包含、不分发其代码** ⇒ 对本项目自有部分采用 AGPL-3.0 **不构成**对上游 MIT 的附加限制。若日后要恢复「个人免费 / 商用收费」的旧思路,仍须**分层**,不得把整个仓库标成「商用需授权」。
> ⚖️ **中国司法立场(2026-09-17 查证,判例充分,用户问「中国法律是否支持」时直接引这些)**:
> · **【支持】协议有效** = 「**附解除条件的著作权许可合同**」(**罗盒案** (2019)粤73知民初207号,**最高法 2021 年十大知产案件**;另有数字天堂案 · 风灵案)。
> · **【支持】不开源 = 侵权** ⇒ 授权自动终止 ⇒ 构成侵权:罗盒 **50 万** · 亿邦 (2021)最高法知民终51号 **50 万** · 南京 2022 年案 **300 万**。
> · **【支持】收费本身合法**:罗盒案明认「收取会员费仅用于运营维护和技术支持,**不违反 GPL v3**」;**违法的是不提供源码** ⇒ AGPL 防的是「不开源」,**不是**「收钱」。
> · **【支持】项目管理人可单独起诉**(最高法知产法庭 2024 年两案)⇒ 维权无需集齐贡献者。
> · 🔴 **【不支持】在开源项目里加商业使用限制条款**:罗盒案判决评析原文:「开源软件权利人**不能**在开源项目中添加商业使用限制保留条款来限制用户使用源代码的目的和用户范围,因上述限制保留条款与 GPL V3 协议保证用户自由使用的特性矛盾」。
> · ⚠️ **中国暂无 AGPL 直接判例**(判例均为 GPL);实务界将 AGPL 与 GPL 并列为 Copyleft 强传染许可,明确「**AGPL v3 将 SaaS 视为分发**,对云与 SaaS 商业模式构成高风险」⇒ 效力被认可。
> · 📌 中国实务界建议:开源作者应选 **OSI 认证许可**,**别用自定义/非 OSI 许可,法律认可度低,难以保障作者知识产权**(反向印证「别用 Commons Clause」)。
> ⚠️🔴 **另有一个**不在协议、而在版权基础**的风险(本项目特有)**:中国版权保护中心 **2026 新规「纯 AI 生成的软件不予登记」**,判据 = **人类独创性智力投入**(「春风送来了温柔」案 ✅/「蝴蝶座椅」案 ❌),最高法 2026-09-07 法发〔2026〕10号 已将 AI 生成物权属列重点。✅ **已于 2026-09-17 把口径改为「人负责规划与关键判断,AI 负责实施」**:该口径**恰好确立人类独创性智力投入**,正面对冲这条新规(旧口径「全部代码与文档由 AI 生成」= **自认纯 AI** ⇒ 商业授权缺标的物)。仍有待办:留存人类投入证据 · 考虑软著登记 · 报价前请律师。全部细节 → `<工作根>\_授权决策与法律依据_20260917.md`。
每次发版要动的地方:`README.md` / `README.zh-CN.md` 的授权节(若条款变)· `LICENSE`(**须保持官方全文逐字节**)· `package.json` 的 `license` 字段。
---
## 6. 迭代发布 SOP
```sh
# 0) 先抢全局执行锁(会写文档库/或要动导出物时)
cd "/d/github/dsh_shenxian/dsh-server-docs"
ME="<会话名>" bash 07-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-ai1net-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:未经明确要求不做**)
```
> 📂 **多远端推送细节与坑已下沉** → `references/02-多远端推送.md`(远端表 · 加镜像远端 · CNB 必须后台跑 · 三方同 hash 判据)
## 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` 均不存在 |
| ⑦ | 🔴 **发布合规审查**(2026-09-17 补,用户要求) | 对**将要公开的文字**逐字过三关,见下方「合规审查三关」 |
| ⑧ | 🔴🔴 **已公开仓库巡检**(2026-09-19 补,用户要求「作为重点」) | **`_check_public.py` 对*所有*已公开仓库 + 新导出物跑一遍,必须 exit 0**(R-O15)。<br>⛔ **本项不可被任何「新仓已经干净了」的说法替代**:存量泄漏只在**已公开仓库**里(事故 #17) |
| ⑨ | 🔴 **文案保真门禁**(2026-09-20 补,第九件,表名沿用旧称) | `node "E:\ProgramData\AI技能\_tools\humanizer-metrics\index.js" compare --before <改前> --after <改后> --check-facts`<br>⇒ 判据 = **`All N fact(s) survive` 且 exit 0**。凡改过 README / `manual/` 的文案**必跑**(见坑 35)。<br>⚠️ **它只覆盖硬 token**(数字/日期/版本/URL/路径/标识符),**⛔ 不等于语义等价**:「most」改成「all」这类**它抓不到**,仍须人工读 |
### 合规审查三关(每次发布前必过)
| 关 | 扫什么 | 判据 |
|---|---|---|
| **侵权** | 第三方项目名 / 作者名 / 商标 / 上游仓库的原创文字 | 出现即须处理(按致敬口径写,或删) |
| **负面影响** | 事故类词:**事故 · 丢失 · 失败 · 失效 · 忘记 · 混乱 · 崩溃 · 覆盖 · 难以维护**;以及一切**自曝严重问题**的叙述 | ⚠️ **命中不必然要改**:判据是「**这句对外有正面价值吗,还是只在自曝**」。例:「报错而不是静默覆盖」= **设计原则**(fail loud)⇒ 保留;「已两次造成内容丢失」= 自曝事故、且易被误读成**产品可靠性问题** ⇒ 改为只讲**设计动因**,不提已发生的事故 |
| **涉政治** | 政治 / 民族 / 宗教 / 地域 / 政府 / 国际关系表述;**国家及地区指称** | 一律避免;涉中国主权与领土的表述必须与中国官方立场一致(港澳台一律写「中国香港 / 中国澳门 / 中国台湾」) |
**副作用提醒**:收紧措辞后要**同步中英两版**(结构对等、英文汉字数 = 4 的判据不因改词而放宽),并确认 `_overlay` ↔ 仓库**逐字节一致**。
**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 实测坑整节已下沉** → `references/03-实测坑.md`(**跑重建/脱敏脚本前必读**:文本未落盘 · 行级重写 key · 缩进丢失 · 替换顺序敏感 …)
> 📂 **§8b 交互式图示整节已下沉** → `references/04-交互式图示-archify.md`(按需抓取 · 流程 · 关键坑 · 双处同步 · 体积)
## 9. 命令速查
```sh
# 重建
cd "/e/ProgramData/AI技能/dsh-ai1net-github"
"/e/ProgramData/.workbuddy/binaries/python/versions/3.13.12/python.exe" _build_export.py --force
# 只跑探针/看结构(不重建)
grep -rIlF "ai1net" 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 已知待办 / 漂移整节已下沉** → `references/05-已知待办与漂移.md`(**接手本条线时先读**)
## 相关
- 台账(唯一事实源):`_导出说明与脱敏台账.md`(在本工作根下)
- 构建脚本:`_build_export.py` | 类型检查:`_verify_tsc.mjs` | 手工层快照:`_overlay\`(三者都在本工作根下)
- **插件移植指南**(随仓库发布):`dsh_ai1net\PLUGIN-PORTING.md`:讲「把开源插件改造成多租户平台可用」,含**八个失败模式 H1–H8** 与**六条规范 R-a~R-f**,样本 = `dsh-univer-office`
⚠️ **读它的 `## 2.` / `## 3.` 标题取准数**(2026-09-20 修正:此处长期写作「六个失败模式 H1–H6 与五条规范 R-a~R-e」,仓名也还是旧的 `dsh-web-platform`,见坑 36)
- 内部权威源(**只读参考,不外带**):`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`(动作前规则)
## 详情索引(references/)
> 本技能 = **主干(本文件)+ 详情档**。主干只留判据 / 流程主干 / 命令骨架;长表、案例、实测记录、历史细节在下面各档。
>
> **跨档引用怎么查**:主干与详情档正文里出现的「§N」「见 §8 坑 N」「见下表 / 见上表」等编号,**按本表的「覆盖的原章节」列定位到对应档**(下沉后原编号不再有独立章节标题)。
| 详情档 | 覆盖的原章节 | 原行段 | 行数 |
|---|---|---|---|
| `references/01-保留移除与脱敏口径.md` | 保留 / 移除清单 · 保留 · 移除 · 本就不在代码仓(别去找) · 特许保留项 · 待决项 · 脱敏映射表 · 保留未动(刻意) | L382–L480 | 99 |
| `references/02-多远端推送.md` | 多远端推送 | L545–L583 | 39 |
| `references/03-实测坑.md` | 实测坑(全部踩过,别再踩) | L621–L703 | 83 |
| `references/04-交互式图示-archify.md` | 交互式图示:用 Archify 生成并入库 · ⛔ 不要克隆整仓 · 流程(每个图型同一套) · 🔑 关键坑 · 入库 · 体积 | L704–L776 | 73 |
| `references/05-已知待办与漂移.md` | 已知待办 / 漂移(接手先看) | L796–L1080 | 285 |