回收 411 MB(470 M → 58.8 M),全部经回收站,可恢复: - 待清理/(146.2 M,含 relay 分片 128 M 与 42 项过程目录) - tmp/(32.4 M,按接续棒命名的过程临时区) - .workbuddy/tmp/(39.5 M) - 4 份 workbuddy.db 冗余副本(101 M,09-23 事故的坏副本 / 抢救产物) - tmp/im16/gw/centrifugo 二进制(63.9 M,可重下)+ 缓存残留 入库范围:常驻规则(CODEBUDDY.md / README.md / state.py)、在途接续入口与 接续包、docs/、交付物/、交接单/、归档/、scripts/、.codebuddy/、 .workbuddy/memory/;共 398 件,其中 >60 KB 的 26 件全为文档。 排除(.gitignore):tmp/、待清理/、运行态日志与缓存、*.db 与 DB 备份整目录、 打包二进制(*.tar.gz / *.tgz)、记忆修复前备份。
12 KiB
项目代码 · 分层范式与迭代风险评估(2026-09-16)
性质:只读架构评估(实测探针,非印象判断)。⛔ 未改任何代码。 取证:
D:\github\dsh_shenxian\src全量静态扫描(HEAD4e3a1a4)—— 58 个.ts、14,053 行;统计目录规模、文件扇出、跨目录依赖矩阵(探针脚本内部聚合,只输出摘要)。 一句话判定:范式方向是对的(按域分目录 + 依赖稀疏 + 扇出低),但有两类会被大规模迭代放大的隐患 —— ① 4 组双向依赖(含"基础层反向依赖业务层")② 缺领域层,导致业务规则沉进 route 文件。 关于"改一处要不要读全仓":现在还不用,但已经出现"改一处要连读 2–3 个大文件(约 2,500 行)"的模式;覆盖网络是最后一次能以低成本立规矩的机会。
一、实测数据
1.1 目录规模
| 目录 | 文件 | 行数 | 占比 |
|---|---|---|---|
web(routes + server + nginx) |
24 | 5,593 | 40% |
supervisor |
11 | 3,295 | 23% |
db |
10 | 2,960 | 21% |
(root)(config / isolation / crypto / cli / index) |
5 | 836 | 6% |
worker |
2 | 708 | 5% |
fs |
6 | 661 | 5% |
| 合计 | 58 | 14,053 | — |
1.2 最大的 12 个文件 —— 行数大 ≠ 耦合高
| 行数 | 文件 | 内部依赖数(扇出) |
|---|---|---|
| 1,259 | supervisor/orchestrator.ts |
6 |
| 756 | db/pg.ts |
4 |
| 752 | db/repo.ts |
3 |
| 743 | web/routes/business-plugins.ts |
4 |
| 706 | supervisor/proxy.ts |
3 |
| 687 | web/routes/skills.ts |
3 |
| 514 | worker/agent.ts |
8 |
| 500 | web/routes/whitelist.ts |
4 |
⇒ 关键读数:1,259 行的文件只依赖 6 个模块。 说明它大是因为"同领域逻辑都塞在一个文件里",不是因为"牵扯面广"。对"改一处要不要读全仓"来说,这是好消息。
1.3 跨目录依赖矩阵(全部 23 条边)
| from → to | 次数 | from → to | 次数 | |
|---|---|---|---|---|
| web → supervisor | 11 | supervisor → worker | 2 | |
| web → fs | 9 | supervisor → web | 2 ⚠️ | |
| web → (root) | 4 | worker → supervisor | 2 ⚠️ | |
| web → db | 3 | db → (root) | 2 | |
| web → nginx | 1 | (root) → web | 2 ⚠️ | |
| fs → web | 3 ⚠️ | (root) → fs | 2 | |
| supervisor → db | 3 | (root) → db | 1 ⚠️ | |
| worker → fs | 3 | fs → worker | 1 ⚠️ | |
| worker → (root) | 2 | fs → (root) | 1 | |
| supervisor → (root) | 1 | (root) → 其他 | — |
⇒ 关键读数:整仓只有 23 条跨目录边,最粗的一条是 11 次。没有"上帝模块"。
二、范式评估
2.1 合理之处(4 条)
- 按域分目录:
web(入口/HTTP)·supervisor(进程生命周期)·db(存储)·fs(用户文件)·worker(远程节点)—— 边界与职责对得上。 - 依赖稀疏:23 条边、最大 11 —— 远低于同类单体项目。
- 抽象缝存在:
Spawner接口把"后端"抽出来了,route 层只依赖接口。 - 文件头注释质量高(这一点被低估):
agent.ts头部 8 行讲清"它是什么 / 四条纪律 / 引用设计章节",spawner.ts同理。这就是"不用读全仓"的现成机制 —— 它把"这个文件负责什么"变成可低成本获取的信息。
2.2 隐患(4 条,均有数据支撑)
| # | 隐患 | 数据 | 后果 |
|---|---|---|---|
| 1 | 双向依赖 4 组 | web ↔ supervisor(11/2)· web ↔ fs(9/3)· supervisor ↔ worker(2/2)· fs ↔ worker(1/3) |
改任一侧都要看另一侧 ⇒ 上下文成本翻倍;且无法单独测试 |
| 2 | 基础层反向依赖业务层 | (root) → web 2 处 · (root) → db 1 处 · db → (root) 2 处 |
共享层(config 等)依赖上层 ⇒ 分层方向被破坏,这是最该修的一条 |
| 3 | 缺领域层 | web 占 40% 行数;business-plugins.ts 743 行、skills.ts 687 行 |
业务规则沉在 route 文件里 ⇒ 改规则要读 route;同规则无法被 worker/CLI 复用;难以单测 |
| 4 | web 层过重 |
24 文件 / 5,593 行 | 入口层变成事实上的"业务层",进一步加剧 #3 |
三、是否需要分层:需要补两层,但不需要重构
3.1 现状的实际分层(隐式)
web(routes) ──► supervisor / db / fs ──► (root: config / types)
▲ ▲ │
└────────────────────┴────────────────────────┘
(反向边,违规)
3.2 建议的目标分层(方向规则要写下来)
① 入口层 web/routes · cli (只做 HTTP/CLI 编解码)
② 领域层 domain/* ← 【新增】业务规则,纯函数优先
③ 能力层 supervisor · db · fs · worker · net/*
④ 基础层 config · types · crypto ← 【禁止反向依赖】
依赖方向:① → ② → ③ → ④,单向,⛔ 不得回指
- 补 ② 领域层:把"业务规则"从 route 抽出。收益 = route 瘦身 + 可单测 + 可被 worker/CLI 复用。
- 补
net/作为能力层的一员(与 supervisor/db/fs 同级),不是新的一层 —— 这样覆盖网络不会变成一个"横跨所有层的特权模块"。 - 把依赖方向写进架构文档/CODEBUDDY:现在规则是隐式的,靠自觉。
四、「改一处要读全仓」的风险评估(这是用户真正问的)
4.1 分规模档看
| 规模 | 读全仓的代价 | 结论 |
|---|---|---|
| 现在(58 文件 / 14k 行) | 约 15 万字符 ≈ 4–5 万 token | 可行但不必 —— 实际不需要读全仓 |
| 覆盖网络落地后(+3 |
约 22k 行 ≈ 7–8 万 token | ⚠️ 开始变贵,且依赖边会从 23 条涨到 40+ |
| 继续叠加(游戏 / 房间层 / 发布层) | — | 🔴 若不立规矩,会真变成"改一处读全仓" |
4.2 真正的风险不在规模,在边界模糊
现在已经在发生:改 business-plugins.ts(743 行)时,你无法只靠它自己判断"这条规则属于插件管理还是实例生命周期" —— 因为没有领域层,规则一半在 route、一半在 orchestrator.ts(1,259 行)和 repo.ts(752 行)。
⇒ 一次改动实际要连读 3 个大文件 ≈ 2,500 行,这才是成本所在,而与仓库总规模无关。
4.3 三条可落地的规矩(覆盖网络正好是载体)
- 单向依赖:
net/*不得依赖web/*;web可以依赖net。先定规则再写第一行代码 —— 现在新增目录的成本最低。 - 契约前置:先写接口与类型,再写实现。会合中继拆分方案的 S0 已经是这个做法(纯新增
src/net/rendezvous.ts/reachability.ts,零行为变化)—— 把它变成制度,而不是一次性动作。 - 文件头注释制度化成"模块索引":现有头注释写得很好,只要规定"每个模块目录入口必须写明:职责 / 依赖谁 / 被谁依赖",就能把"读全仓"降级为"读索引 + 读相关模块"。
- ⛔ 禁止基础层反向依赖:
config/types不得 importweb/db—— 现在有 3 处,趁早清掉。
4.4 一句话回答
现在不会"改一处读全仓";但已经会"改一处读 2–3 个大文件";覆盖网络之后如果不定依赖方向,就会真的变成读全仓。 ⇒ 需要做的不是重构,而是"立规矩 + 补一层 domain"。
五、本机纳入测试环境(你已给的许可)
定位:本机(Windows 开发机)= 客户端类型的第一个测试节点,正好补上可行性评估的缺口 1("dsh 在 Windows 上能否起实例 + 实例内 bash 是否可用"至今未实测)。
形态判定:Windows 无 bwrap / systemd / uid 隔离 ⇒ 只能走 soft 模式(实例 = 裸子进程)—— 而这正好就是客户端形态的目标模式,不是降级。
可先验的三件事(都不动服务器):
- 本机起一个实例(
soft模式)⇒ 实例页 200; - 实例内跑一次 bash 工具 ⇒ 验证 dsh 在 Windows 的沙箱后端行为(这是全案唯一"尚无证据"的技术点);
- 本机与 47 / 106 之间的真实网络画像(NAT 类型 / 打洞可行性 / 与中继的 RTT 与 jitter)—— 直接填"最该先测的三项"里的两项。
⚠️ 两点注意:① 本机是唯一的开发机,起实例会占资源,建议用最小配额;② 本机作为"客户端节点"参与网络时,不得顺手把它接进现有生产链路(要独立形态、独立开关)。
六、我选了什么(可推翻)
- 不重构,只做两件事:补
domain层 + 把"单向依赖"写成书面规则(四层:entry → domain → capability → base)。 net/定位为能力层的一员(与 supervisor/db/fs 同级),不是特权跨层模块。- 把 S0 的"契约前置"制度化 —— 每个新增模块先出接口与类型文件。
- 清掉 3 处基础层反向依赖(
(root) → web2 +(root) → db1),列为独立小任务。 - 本机作为第一个客户端测试节点,先做 §五 的三件验证。
七、本次未做
- ⛔ 未改任何代码、未动服务器、未写文档库(全局执行锁被
修复轮-决策方法-2b占用)。 - 探针脚本落
_中间产物_待清理/_arch_probe.py(可复跑;结论已固化到本文件)。 - 📌 未新增上抛项。
八、执行计划与实际落地(2026-09-16 13:2x 追加)
8.1 🔴 勘误:§2.2 隐患②「基础层反向依赖 3 处」不成立
复核命令(只读):
grep -n "from '\./\(web\|db\|fs\|supervisor\|worker\|net\)/" src/*.ts
只命中 src/cli.ts(5 条);config.ts / crypto.ts / isolation.ts / index.ts 零 import。
⇒ 根因:上一轮统计把 cli.ts 误算进了"基础层"。它是入口层① —— import db/fs/web 属 ① → ③ 的合法方向。
⇒ 结论修正:
① 真实违规 = 0 处 ⇒ §4.3 第 4 条「清掉 3 处基础层反向依赖」撤销(没有可清的);
② §2.2 隐患② 应改写为「边界模糊」——真实成本在 web/routes 里沉着的业务规则(§4.2 那条),不在依赖方向。
⇒ 教训与 S0 的两处勘误同类:接手前人结论,先做一次最小取证。
8.2 ✅ P0 已落地(本机 · 2 个文件)
| 文件 | 改动 |
|---|---|
docs/architecture.md |
新增:四层定义 + 依赖方向 ①→②→③→④ 单向,⛔ 不得回指 + R1–R4 判据(怎么算违反)+ 三条工程纪律(契约前置 / 模块头注释 = 索引 / 动手前自查)+ §3 现状实测 + §4 优先级 |
README.md |
文档表新增一行指向它("动手改 src/ 前读一遍") |
- 验收:规则可从
README → docs/architecture.md两级直达;四层归属带可判定的判据,不是口号。 - 回滚:删
docs/architecture.md+ 删README.md那一行。 - 为什么先做它:零代码风险,而"扩张期走丢方向"的回头成本高得多(§四 4.1 的风险分档)。
- ⏳ 未做:git commit(未授权)。
8.3 剩下的两件(均需先出清单,命中 R7)
P1 · 补 src/domain/* —— 把 web/routes 里的业务规则抽出来。
⚠️ 行为敏感重构(不是纯类型);影响面 >10 文件 ⇒ 动工前先出受影响清单。
判据(做它的唯一理由):让"改插件管理规则"不再需要连读 orchestrator.ts(1,259) + repo.ts(752)。
P2 · 模块头注释补全 —— 按 docs/architecture.md §2.2 给各模块入口补「职责 / 依赖谁 / 被谁依赖」。
批量改 >10 文件 ⇒ 同样先出清单。
顺序建议:P0 ✅ → 覆盖网络 S1–S4 → P1。 P1 不阻塞覆盖网络;反过来,覆盖网络的新模块(
net/*)正好是「契约前置」的第一个样板。