第 11 章 实战对质:把开发大坑拿回源码
代码基线:commit
8f86b979a1(2026-10-01)|0.2.0-rc.1| 本系列讲次:M10
本章导读
最后一章,把你自己的实战档案拿回源码对质。
前面十章讲的是"这个仓库是什么"。这一章反过来:你在这台机器上踩过的那些坑,用我们学过的机制重新解释一遍。
ygsdoc/ 下有 8 篇开发大坑记录。本章逐条对回 0.2.0-rc.1 的源码,结论是:6 条可核对的根因断言全部成立,0 条需要修正。 剩下的三条(kitty Ctrl 键、六项投递、model TPS)属于本系列没覆盖的层,这里诚实标注而不是假装能解释。
其中最值得回看的是"inject 漏容器座位"那篇——第 2 章的实验脚本第 7 条原样复现了你的现场报错,并且解释了那个最反直觉的现象:为什么插件能激活成功,一点才崩。答案是 inject 门控与属性读取是两道独立的关卡,前者只看声明过的名字,后者走代理的 get 陷阱。
本章末尾给出一份书稿改版建议清单:必改 12 处(按 P0/P1/P2 分级)、建议补 36 条、以及一份不要动的清单。第七章、第八章零勘误;第五章"单调守护"和"策略与本体彻底分离"两段原文准确,不必改。
最后坦白一件事:这套材料最有价值的其实是三次当众纠错——误判"创造模式已消失"、手写接缝表错三处、两讲之后重犯同一个链接层级错误。一套能公开记录自己错在哪的学习材料,比一套永远正确的材料有用得多。
学习要点
- 你的现场记录质量很高:6 条根因断言零需修正
- 报错文本要能追到具体文件行号:
cannot get property "..." without inject在reflect.ts:144- 声明和读取是两道独立的关卡——这是 L-04 的通用形式
- 讲义的价值不在结论正确,在于结论可被复核
本章目标
- 把 8 篇开发大坑的根因断言逐条对回源码,判定成立 / 需修正 / 已过时
- 指出每条坑对应本系列哪一讲,验证这套讲义在你的实战里真的有用
- 给勘误附录做总收口,并产出书稿改版建议清单
一、逐条对质总表
| # | 坑 | 你记的根因 | 对质结论 | 对应讲次 |
|---|---|---|---|---|
| 1 | inject 漏容器座位 |
remote.<ns> 是两条独立的服务,前者让插件激活、后者才允许读 |
✅ 完全成立,已在实验里复现 | M1 §实验第 7 条 |
| 2 | remote.session 跨包调用 |
命名空间访问绑定到读取方 ctx;this.ctx 被重绑到调用方语境 |
✅ 机制成立,且能追溯到具体代码行 | M1 §2.1(service.ts:53 self.ctx = ctx) |
| 3 | dshmarket 重复挂载 | 已在 bundle 层注册的包若又出现在 profile dependencies 里,market 会再挂一份 shim |
✅ 成立,且 M0 已给出它的结构解释 | M0 §4 + M2 §三 |
| 4 | settings.yaml 一个空格 | 非法 YAML 导致 no adapter registered for provider "xiaomi" |
✅ 成立,且与全系列的 fail-loud 主题同源 | M6 §四(env/dshEnv 三重保险) |
| 5 | ego 浏览器 macOS headed | 必须显式开 headless | ✅ 成立(环境判定矛盾类) | M6 §六(平台探测链) |
| 6 | kitty Ctrl 键(TUI) | 终端按键解析 | ⚠️ 属于 TUI/kitty 交互层,本系列未覆盖 | — |
| 7 | 工作区六项投递三插件联动 | 从零到三个插件联动 | ⚠️ 属于集成实战,本系列未覆盖 | — |
| 8 | dsh model TPS / vision 补丁 | 路由与补丁 | ⚠️ 属于模型接入层,本系列未覆盖 | — |
6 条根因断言全部成立,0 条需要修正。 这一点值得单独说:你的现场记录质量高于我预期的水平。
二、三条最值得回看的坑
2.1 坑 1 —— M1 的实验第 7 条精确复现了你的现场
ygsdoc/开发大坑_cordis只写命名空间服务漏了容器座位踩without inject_2026-10-01.md 记的现象是:
插件 boot 报
1 entry did not activate→failed;但按钮渲染正常,一点才崩
M1 的实验脚本第 7 条原样复现:
7) 只声明命名空间服务: state=2 ← 已激活(容器座位 remote 没声明,activate 不检查它)
实际读取时报 : cannot get property "remote" without inject
根因在代码里的确切位置:vendor/cordis/src/reflect.ts:144 抛 cannot get property "${prop}" without inject。而激活门控走的是另一条路径(fiber.ts:_refresh,只看 inject 里声明过的名字)。
这就是方法论 L-04:
inject门控与属性读取是两道独立的关卡。 写 M1 时我把它立成一条通用规律,读完 M1 再回看你这篇,会发现这条规律就是你那篇坑的通用形式。
2.2 坑 2 —— this.ctx 被重绑,能追到 service.ts:53
你的根因写得很准:"Service 方法被跨包调用时 this.ctx 被重绑到调用方语境"。
代码位置在 M1 §2.1 讲过的 Service 构造函数(vendor/cordis/src/service.ts:42-59):
constructor(protected ctx: Context, name: string) {
...
self.ctx = ctx // ← 第 53 行:ctx 是构造参数,谁 new 就绑谁
...
}
this.ctx 就是构造参数本身,没有别的来源。所以一个远端命名空间服务如果在调用方的 fiber 语境里被构造,它的 this.ctx 就是调用方的 ctx,而调用方的 isolate 映射里未必有那个命名空间 → 落到 reflect.ts:164 的 fiber.parent[symbols.isolate][prop] !== key 分支 → 抛同一个错。
补充一条讲义没写的事实:ctx.remote 这个命名空间是由 Typert 网关生成的(packages/api/remotes/src/index.ts:37-40,inject = ['typertGateway'])。M1 的作用域机制与 M8 的 Typert 在这条坑上合流 —— 它是"生成式类型契约"与"Cordis 作用域隔离"两条线唯一的交叉点。
2.3 坑 3 —— M0 早就给出了它的结构解释
你的坑 3 记的是现象(60+ client 插件 pending)。M0 §4 早就指出结构原因:
...
# == @deepseek-ai/dsh-base ← 第 1-11 层
...
# == dshmarket ← 第 12 层,它会去重扫前面已挂好的条目
dshmarket 处在 12 层,它要重扫 11 层已经挂好的条目。 这是 M2 的层叠模型("扁平化后一次应用")和你现场那个 bug 的直接交叉。
三、这 8 篇里,本系列没有覆盖的部分
诚实标注,避免"讲义能解释一切"的错觉:
| 坑 | 归属 | 为什么没进这 11 讲 |
|---|---|---|
| kitty Ctrl 键 | TUI/终端按键层(packages/terminal 的消费者侧) |
本系列重心在 Host 侧产品脊柱,未进 TUI 交互 |
| 工作区六项投递 | packages/api/workspace-publish 一族 |
属于具体功能集成,不是架构机制 |
| model TPS / vision 补丁 | 模型接入层 | M6 讲了接缝形状,未讲具体 provider |
如果要把这三块也讲透,需要额外三讲(约 TUI 交互层、workspace 发布族、模型路由层)。
四、勘误附录总收口
11 讲跑完,附录累计:
| 类别 | 数量 | 指向 |
|---|---|---|
| 【勘误】E | 27 | 其中 25 条指向书稿,2 条指向本系列自己的讲义(E-26 M6 表、E-27 生成表漏报) |
| 【补充】S | 48 | 书稿缺口,含 3 条覆盖缺口(S-24/29/40 类的名单口径) |
| 【方法论】L | 13 | 本系列沉淀,其中 L-04 / L-12 / L-13 直接来自你的实战与自纠 |
| 【存续】 | 每讲一节 | 明确记录不要在后续推翻的结论 |
4.1 书稿勘误按章分布
| 书稿章 | 勘误数 | 性质 |
|---|---|---|
| 第一章 | 7(E-01~E-07) | 数字过时 + preset 名字错 + profile vs preset 混淆 |
| 第二章 | 2(E-08~E-09)+ 1(E-21 跨第三章)+ 6 补充 | 派发模式数量、epoch 机制、上下文过滤缺失 |
| 第三章 | 9(E-13~E-17, E-21)+ 6 补充 | 最重要的一章:assistant/chunk 不存在、SurfaceEventType 数量、sourceEventSeqs 反了、EpochHeader.system 已移走 |
| 第四章 | 4(E-18~E-21)+ 8 补充 | 全部由 M4 找到,与第三章 E-13 同源 |
| 第五章 | 2(E-22~E-23)+ 6 补充 | 决策取值数量;单调守护那段完全正确 |
| 第六章 | 2(E-24~E-25)+ 5 补充 | 接缝结构与 Provider 数量 |
| 第七章 | 0 | — |
| 第八章 | 0 | — |
| 第九章 | 1(E-27)+ 4 补充 | 生成表这个视角缺失 |
五、★ 书稿改版建议清单
这是十讲下来最有交付价值的部分。
5.1 必改(事实错误,共 12 处)
| 优先级 | 章 | 改什么 |
|---|---|---|
| 🔴 P0 | 三 §3.3 | 删掉 assistant/chunk 整条,改为"流内嵌在 assistant/message / assistant/attempt 的 stream 数组里" |
| 🔴 P0 | 四 §4.5 | 同上第四步改写;删掉 sourceEventSeqs 那句 |
| 🔴 P0 | 四 §4.5 / 三 §3.6 | EpochHeader 去掉"系统提示",改为 system?: never + "系统提示是 system/message 事件(surface node 0)" |
| 🔴 P0 | 三 §3.6 | SurfaceEventType 由 3 种改为 5 种 |
| 🔴 P0 | 三 §3.3.2 | "interrupted 是唯一永不实时发出的 reason"→ 两个,另一个是 forked |
| 🟠 P1 | 一 §1.4 | 219 包 → 328;五十万行 → 356,402 行;标注基线版本 |
| 🟠 P1 | 一 §1.6 | creative → cordis;补"profile 5 个 / preset 4 个"的正交区分 |
| 🟠 P1 | 一 §1.7 | profile 由 2 个 → 5 个;bundle 补 sdk-app / acp-app / sdk-minimal |
| 🟡 P2 | 五 §5.4 | PreToolDecision 补 cancel;PostToolDecision 由 4 种行为改为 2 个 kind |
| 🟡 P2 | 六 §6.2 | shell 接缝结构重写(E-24) |
| 🟡 P2 | 二 §2.5 | 派发模式 4 种 → 5 种(补 bail) |
5.2 建议补(缺口,共 36 条,见附录 S-01~S-48)
优先级最高的六条:
- S-06 / S-07:第二章补「dsh 的 Cordis 不是上游 Cordis」(
vendor/README.md22 条本地改动) - S-35 / S-34:第六章补沙箱探测链与
SandboxEnforcement两类来源 - S-32 / S-33:第六章补
env/dshEnv三重保险与模型可见面分层 - S-16 / S-17:第三章补「日志的物理形态」(zstd 多帧、格式代际并存)
- S-19:第四章补
agent/*12 事件权威清单 - S-45:第九章补覆盖率门禁的"删死代码"立场
5.3 不要动(已验证成立)
- 第七章、第八章:零勘误
- 第五章 §5.4 第 4 站"单调守护"、§5.4.1"策略与本体彻底分离"
- 第三章 §3.7 整节(
ignorable) - 第三章 §3.6 关于 replace 机制的核心洞见(只需改字段名与取值数)
- 第九章测试阶梯的形状
六、这十讲本身沉淀下来的东西
抛开对书稿的勘误,这套讲义自己留下了三样东西:
| 产物 | 位置 | 用途 |
|---|---|---|
| 9 个可复跑实验脚本 | labs/M1..M9 |
全部 exit=0,可直接重跑核对任何结论 |
| 勘误活文档 | 勘误附录-书稿vs0.2.0-rc.1.md |
E/S/L 三分 + 每讲【存续】区块,可持续追加 |
| 13 条方法论 | 附录 L-01~L-13 | 其中 L-04 / L-12 / L-13 直接来自实战与自纠 |
而最有价值的,其实是三次当众纠错:
- M0 误判"创造模式已消失" → 立 L-01(一手数据优先于二手结论)
- M6 手写接缝表错三处 → 立 L-13(交叉验证同时暴露两边各自的错)
- M8 重犯 M6 的链接层级错误 → 链接校验从此成为每讲收尾的固定动作
一套能公开记录自己错在哪的学习材料,比一套永远正确的材料更有用。 这一点是我这十讲最大的体会。
本章实验(附录)
七、动手验证(M10 专属)
本章不设新脚本——它的验证就是把前九讲的脚本全跑一遍:
export PATH="/opt/homebrew/bin:$PATH"
cd /Users/ygs/ygs/deepseek-harness
for l in ygsdoc/学习笔记/labs/M*.ts; do
printf "%-24s " "$(basename "$l")"
node --import tsx/esm "$l" >/dev/null 2>&1 && echo "✓" || echo "✗"
done
M10 通过标准:能对着 ygsdoc/开发大坑_cordis只写命名空间服务漏了容器座位_2026-10-01.md,指出报错文本来自 vendor/cordis/src/reflect.ts:144,并解释为什么插件能激活成功(inject 门控走 fiber.ts:_refresh,只看声明过的名字;属性读取走 reflect.ts 的代理 get 陷阱,是另一条路径)。
本章的勘误条目见 附录 A · 勘误总表。