dsh-thesis 1.0.0 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/LICENSE +21 -21
  2. package/NOTICE.md +1 -1
  3. package/README.md +48 -204
  4. package/docs/autopilot.md +73 -0
  5. package/docs/config.md +113 -0
  6. package/{DESIGN.md → docs/design.md} +94 -73
  7. package/docs/host-api.md +728 -640
  8. package/docs/release.md +126 -0
  9. package/docs/tools.md +76 -0
  10. package/docs/troubleshooting.md +88 -0
  11. package/docs/usage.md +87 -0
  12. package/lib/autopilot/index.d.ts +38 -0
  13. package/lib/autopilot/index.js +159 -0
  14. package/lib/autopilot/intent.d.ts +40 -0
  15. package/lib/autopilot/intent.js +82 -0
  16. package/lib/autopilot/messages.d.ts +22 -0
  17. package/lib/autopilot/messages.js +35 -0
  18. package/lib/autopilot/types.d.ts +60 -0
  19. package/lib/autopilot/types.js +10 -0
  20. package/lib/codewalk/engine.d.ts +1 -1
  21. package/lib/codewalk/engine.js +1 -1
  22. package/lib/codewalk/gate.d.ts +1 -1
  23. package/lib/codewalk/gate.js +1 -1
  24. package/lib/codewalk/host-types.d.ts +4 -31
  25. package/lib/codewalk/prestep.js +1 -1
  26. package/lib/codewalk/tools.js +2 -2
  27. package/lib/config.d.ts +17 -0
  28. package/lib/config.js +9 -1
  29. package/lib/dedup/index.js +0 -6
  30. package/lib/gates/messages.d.ts +7 -3
  31. package/lib/gates/messages.js +7 -4
  32. package/lib/index.d.ts +3 -1
  33. package/lib/index.js +10 -7
  34. package/lib/ingest/index.d.ts +1 -1
  35. package/lib/ingest/index.js +1 -3
  36. package/lib/intake/gate-rules.d.ts +6 -5
  37. package/lib/intake/gate-rules.js +6 -2
  38. package/lib/intake/index.d.ts +0 -17
  39. package/lib/intake/index.js +0 -51
  40. package/lib/intake/questions.d.ts +1 -1
  41. package/lib/intake/questions.js +1 -1
  42. package/lib/paper/lib/docx.js +70 -70
  43. package/lib/paper/lib/layout.d.ts +1 -1
  44. package/lib/paper/lib/layout.js +372 -372
  45. package/lib/paper/lib/ledger.d.ts +1 -1
  46. package/lib/paper/lib/ledger.js +1 -1
  47. package/lib/paper/lib/lit-cache.js +7 -7
  48. package/lib/paper/register.d.ts +2 -2
  49. package/lib/paper/register.js +2 -8
  50. package/lib/paper/tools/decision.js +5 -5
  51. package/lib/paper/tools/init.d.ts +1 -1
  52. package/lib/paper/tools/init.js +1 -1
  53. package/lib/paper/tools/lit.js +32 -32
  54. package/lib/ppt/index.d.ts +1 -2
  55. package/lib/ppt/index.js +1 -2
  56. package/lib/shared/define-tool.d.ts +1 -1
  57. package/lib/shared/define-tool.js +1 -1
  58. package/package.json +26 -21
  59. package/skills/code-walkthrough/SKILL.md +5 -3
  60. package/skills/thesis-defense/SKILL.md +1 -1
  61. package/skills/thesis-pipeline/SKILL.md +3 -3
  62. package/lib/codewalk/commands.d.ts +0 -23
  63. package/lib/codewalk/commands.js +0 -168
  64. package/lib/commands.d.ts +0 -33
  65. package/lib/commands.js +0 -60
  66. package/lib/dedup/commands.d.ts +0 -21
  67. package/lib/dedup/commands.js +0 -93
  68. package/lib/ingest/commands.d.ts +0 -23
  69. package/lib/ingest/commands.js +0 -74
  70. package/lib/paper/commands.d.ts +0 -13
  71. package/lib/paper/commands.js +0 -84
  72. package/lib/ppt/commands.d.ts +0 -28
  73. package/lib/ppt/commands.js +0 -71
@@ -1,4 +1,7 @@
1
- # dsh-thesis 设计文档
1
+ # 设计文档
2
+
3
+ > 这是 dsh-thesis 的设计与决策记录(ADR)。**怎么用**看 [usage.md](./usage.md),
4
+ > **字段怎么填**看 [config.md](./config.md),**工具叫什么**看 [tools.md](./tools.md)。
2
5
 
3
6
  **一句话**:让一个对流程不了解的本科生,把「学校模板 + 论文要求 + 实验数据 + 代码 + 参考文献」这堆材料,
4
7
  在 AI 的带教下变成**一篇真实系统与真实数据背书、符合学校规范、经得起查重与答辩的论文**,
@@ -29,7 +32,7 @@
29
32
 
30
33
  ```
31
34
  ┌──────────────────────────────────────────────────────────────────────┐
32
- │ DSH(Web / CLI):自然语言 + /thesis-* 命令 │
35
+ │ DSH(Web / CLI):自然语言;插件自己问一道选择题、自己续跑 │
33
36
  ├──────────────────────────────────────────────────────────────────────┤
34
37
  │ 单一插件 dsh-thesis(Cordis 插件,纯 TypeScript,一行 cordis.patch.yml)│
35
38
  │ │
@@ -54,12 +57,12 @@
54
57
  |---|---|---|
55
58
  | `src/index.ts` | 唯一装配层:`apply()` 调各模块 register,最后装闸门 | `name` / `inject` / `Config` / `apply` |
56
59
  | `src/config.ts` | 统一配置 schema + 默认值单一来源 + `resolveConfig` | `Config` / `PAPER_DEFAULTS` |
57
- | `src/commands.ts` | 斜杠命令的聚合层(`/thesis-defense` 同时覆盖幻灯与代码演练) | `registerDefenseCommand` |
60
+ | `src/autopilot/**` | 自动驾驶:启动选择题 + 建目标自动续轮 + 关卡拍板(不注册任何命令) | `installAutopilot` |
58
61
  | `src/shared/**` | 工具胶水、`definePaperTool`、文本与 fs 错误工具、`spawnCommand` | `definePaperTool` 等 |
59
62
  | `src/gates/**` | 写作意图闸门(`agent/pre-step`)+ 不可逆操作闸门(`tools/pre-execute`) | `installWritingGate` / `installDangerousGate` |
60
63
  | `src/ingest/**` | ZIP/OOXML/PDF 解析 → 文本;源码目录 → **代码结构摘要**(不贴正文) | `registerIngest` |
61
64
  | `src/intake/**` | 问题库 + 状态机 + 意图规格渲染 + 规格闸门 | `registerIntake` |
62
- | `src/paper/**` | 论文流水线(纯逻辑 + 工具 + 命令) | `registerPaper` |
65
+ | `src/paper/**` | 论文流水线(纯逻辑 + 工具 + 子命令词) | `registerPaper` |
63
66
  | `src/dedup/**` | 归一化 + 相似度 + 改写处方 + 报告 | `registerDedup` |
64
67
  | `src/ppt/**` | 幻灯计划 + Marp 渲染 + 转换器探测 | `registerPpt` |
65
68
  | `src/codewalk/**` | 代码演练:阶段状态机 + 验证门 + 可评分追问 | `registerCodewalk` |
@@ -83,81 +86,40 @@
83
86
 
84
87
  ### 3.1 工具(21 个,三个命名空间)
85
88
 
86
- **`thesis_*`:操作论文工作区里的东西**
87
-
88
- | 工具 | 学生视角的一句话 |
89
- |---|---|
90
- | `thesis_ingest` | 把我的材料读进来(docx/xlsx/pptx/pdf/csv/代码 → 文本与结构摘要 + 材料清单) |
91
- | `thesis_intake` | 一次问我一个要求问题,问完写成《意图规格》 |
92
- | `thesis_init` | 建论文工作区(目录、台账、决定日志、七章模板、技能包) |
93
- | `thesis_progress` | 看进度 / 过闸门 G1-G4 |
94
- | `thesis_decide` | 记下我为什么这么定(答辩要用的证据) |
95
- | `thesis_lit_search` / `thesis_lit_save` / `thesis_lit_note` | 真实检索文献 / 收录进 refs.bib / 生成阅读笔记 |
96
- | `thesis_review` | 单章六项评审(字数、大纲、引用、图表编号、未完成标记、G2 清单) |
97
- | `thesis_check` | 全文五项检查(引用双向一致、字数、编号、术语、学校模板探测) |
98
- | `thesis_stylecheck` | 写作风格自查(六条启发式,附行号与改写建议) |
99
- | `thesis_build` | 出论文 `.docx`(优先 pandoc + 学校模板,无 pandoc 用内置零依赖生成器) |
100
- | `thesis_originality` | 原创性自查:scan 定位高风险段落 → verify 复测降幅 → report 汇总 |
101
-
102
- **`thesis_*` 的答辩部分**
103
-
104
- | 工具 | 学生视角的一句话 |
105
- |---|---|
106
- | `thesis_defense` | 提取答辩素材 + 六类必问问题库(附证据锚点) |
107
- | `thesis_slides` | 生成 10-12 页 Marp 幻灯,并可选转换为 `.pptx` |
108
-
109
- **`defense_code_*`:代码演练(答辩层)**
110
-
111
- | 工具 | 学生视角的一句话 |
112
- |---|---|
113
- | `defense_code_status` | 我的系统我讲得清吗(阶段、里程碑、验证门、未通过的问题) |
114
- | `defense_code_next` | 下一个该弄懂的模块(含 todo 与评分要点) |
115
- | `defense_code_update` | 登记/推进/评分/重试(每次改动立即落盘) |
116
-
117
- **`fact_*`:跨会话事实**
89
+ `thesis_*` / `defense_code_*` / `fact_*` 三组,名字即契约(改名就是破坏性变更)。
90
+ **逐个工具的用途、参数与边界见 [tools.md](./tools.md)**,这里只保留设计约束:
118
91
 
119
- | 工具 | 学生视角的一句话 |
120
- |---|---|
121
- | `fact_search` / `fact_remember` / `fact_context` | 上次记下的学校规范还在吗 / 记住这条稳定事实 / 把这个课题已知事实拉回来 |
92
+ - 工具名与命名空间不允许游离:`npm run audit` 断言注册数恰好 21、且全落在三个前缀内;
93
+ - 每个工具都必须有 `output.schema` 与 `output.render`,返回值是**有界字符串**(不返回对象、不做流式);
94
+ - 工具只操作论文工作区里的东西,跨会话的东西一律走 `fact_*`,两者不混用。
95
+ ### 3.2 命令面:**一条斜杠命令都没有**
122
96
 
123
- ### 3.2 命令(9 个)
97
+ `ctx.commands` 一次都不注册。人不再操作命令行,入口换成两条宿主事件:
124
98
 
125
- `/thesis-status` `/thesis-decide` `/thesis-lit` `/thesis-check` `/thesis-build`
126
- `/thesis-ingest` `/thesis-intake` `/thesis-originality` `/thesis-defense`
99
+ | 事件 | 插件做什么 | 人看到什么 |
100
+ |---|---|---|
101
+ | `agent/pre-step` | 认出「论文工作区 + 主 agent + 还没定过意图」→ 问**一道**选择题(`ctx.userQuestions.ask`)→ 答案写进决定日志 → `ctx.goals.create` 建目标(宿主 `goal-round-driver` 此后自动续轮)→ 注入 operating contract | 会话刚开始时的一个选项框 |
102
+ | `tools/pre-execute` | 模型要过 G1-G4 时把决策改成 `ask` | 审批界面的允许 / 拒绝 |
127
103
 
128
- 命令名必须匹配 DSH 的 `/^[a-z][a-z0-9_-]*$/`;`/thesis-defense` 的子命令按侧分派:
129
- 幻灯侧 `prepare`(= outline)/ `convert` / `check` / `guide`,代码演练侧 `new` / `status` / `check`。
104
+ 两条铁律:**只对主 agent 生效**(`delegationDepthOf` 同款规则:头里的委派深度 > 0 一律跳过,
105
+ 否则子 agent 会再问一遍)、**只对论文工作区生效**(`findThesisRoot` 找不到台账就完全不介入,
106
+ 别的项目里装了也不受打扰)。取舍与代价见 ADR-15;`npm run audit` 把「命令注册数必须是 0」变成断言。
130
107
 
131
108
  ---
132
109
 
133
110
  ## 4. 配置契约
134
111
 
135
112
  所有字段都有出厂默认值,`PAPER_DEFAULTS` 是 schema `.default()` 与代码侧回退的**单一来源**
136
- (绝不让「缺配置」退化成「静默关闸门」)。配置面只用论文领域词汇:
137
-
138
- ```ts
139
- export interface PaperConfig extends GateOptions {
140
- workspace?: string // 缺省从会话 cwd 向上探测 00-管理/进度台账.md
141
- memoryPath?: string // 缺省 $DSH_HOME/paper-memory.db
142
- ingest?: Partial<IngestOptions>
143
- intake?: Partial<IntakeOptions>
144
- similarity?: Partial<SimilarityOptions>
145
- stylecheck?: Partial<StylecheckOptions>
146
- ppt?: Partial<PptOptions>
147
- defense?: Partial<DefenseOptions>
148
- codeWalkthrough?: Partial<CodeWalkthroughOptions>
149
- recall?: Partial<RecallOptions>
150
- }
151
- ```
152
-
153
- `GateOptions` 管两个闸门:`writingGate`(要求没立稳时拦截「开始写」)、
154
- `destructiveGate`(不可逆命令前提请人类拍板),以及它们各自的模式列表与 `maxQuestions`。
113
+ (绝不让「缺配置」退化成「静默关闸门」)。配置面只用论文领域词汇,不引入别的概念体系。
155
114
 
156
- **`workspace` 的语义是确定的**:配置了就只认它;该路径下没有台账时**明确报错**,
157
- 绝不悄悄退回到 cwd 探测去写到别的目录。
115
+ **字段清单、默认值、取值范围与 YAML 示例见 [config.md](./config.md)。**
158
116
 
159
- ---
117
+ 两条设计约束(写在这里是因为它们属于架构决定,不属于字段说明):
160
118
 
119
+ 1. 新增字段必须同时进 `Config`(schemastery schema)、`PAPER_DEFAULTS`、`resolveConfig` 三处,
120
+ 缺一处就会被 `npm run test:sandbox` 的「默认值来源一致」用例抓住;
121
+ 2. 开关的默认值必须是**开**:缺配置绝不能等于关闸门(`writingGate` / `destructiveGate` /
122
+ `autopilot.gateApproval` 都遵守这条)。
161
123
  ## 5. 领域闭环:要求与被约束的产出物必须可达
162
124
 
163
125
  追问问题声明它影响哪些产出物:`affects: IntakeAffect[]`。这不是描述性文字,
@@ -187,7 +149,7 @@ export type IntakeAffect =
187
149
 
188
150
  `dsh plugin add` 一次装完,一个 `Config`,一套技能,模块间共享上下文(intake 写规格、写作读规格、
189
151
  原创性自查读章节、幻灯读答辩素材)。代价是包变大;用「目录即模块 + 无跨模块反向依赖」控制耦合。
190
- `/thesis-defense` 需要同时覆盖幻灯与代码演练,因此它的注册放在装配层(`src/commands.ts`),
152
+ 答辩能力横跨幻灯与代码演练,因此两者的装配都在装配层(`src/index.ts`)完成,
191
153
  而不是让 `ppt/` 与 `codewalk/` 互相依赖。
192
154
 
193
155
  ### ADR-2 材料先追问、后动手
@@ -265,6 +227,60 @@ export type IntakeAffect =
265
227
  (某些受限环境里管道 stdio 直接被拒),需要输出时才 `capture`。
266
228
  统一实现在 `src/shared/spawn.ts`,并给 pandoc 之类的调用配了超时。
267
229
 
230
+ ### ADR-13 宿主 peer range 是**声明**,必须与实测运行期一致
231
+
232
+ DSH 的 profile 预检(`dsh-app-boot` 的 `evaluatePluginCompatibility`,在
233
+ `prepareProfileEntries` 里逐行执行)只认一件事:插件 `package.json` 里
234
+ `@deepseek-ai/dsh` / `@deepseek-ai/dsh-*` 的 `peerDependencies` 是否被当前运行期版本
235
+ 满足(SemVer,`includePrerelease: true`)。不满足 → 该行 `disabled` + stderr 警告,
236
+ **表现是「装了但没反应」**,而不是任何一条插件自己的报错。`engines.dsh` 当前不强制,
237
+ `peerDependenciesMeta.optional` 也不豁免这条规则。
238
+
239
+ 本仓库实际踩过:声明 `@deepseek-ai/dsh-tools: ^0.1.0`(对着 0.1.5-rc.1 写的),
240
+ 在 dsh 0.2.0-rc.2 上整个插件被静默停用。当时运行期 import 只有
241
+ `@deepseek-ai/dsh-tools` 的 `defineTool` 与 `schemastery`(ADR-5),
242
+ 两者的契约在 0.2.0-rc.2 上逐字未变——**唯一的问题就是这行声明**。
243
+
244
+ 处理方式(避免下次再靠人肉记):
245
+
246
+ 1. 声明写成**有下界的 range**:`>=<实测版本> <下一小版本>-0`。
247
+ 末尾的 `-0` 不是装饰:`<0.3.0` 在 `includePrerelease: true` 下会把 `0.3.0-rc.1`
248
+ 也算进去,`<0.3.0-0` 才是真正的「不跨小版本」。
249
+ 2. 判据不自己实现:`scripts/lib/host-compat.ts` 直接 import 已安装的
250
+ `dsh-app-boot`,用宿主自己的函数判断宿主自己的规则(同源,不会漂移)。
251
+ 3. 两处强制执行:`npm run test:host-load`(本仓库 manifest **与已安装副本**两份都查,
252
+ 因为 preflight 读的是后者)与 `prepublishOnly` 的 `scripts/prepublish-guard.ts`
253
+ (跨小版本后忘了改声明就发不出去)。
254
+ 4. 跨小版本时先跑 `npm run test:host` + `npm run test:host-load`,再改 range 的下界。
255
+
256
+ ### ADR-15 不提供斜杠命令,插件在后台把流程推起来
257
+
258
+ 命令是给人操作 CLI 的入口。这套流程真正的主角是模型与流水线,人只需要在两处出现:
259
+ **开头选一次意图**、**过关卡时拍一次板**。给他们九条命令(后来收敛成一条 + 九个动词)
260
+ 是把「操作界面」当成了产品,而用户要的是「我说一句、它自己跑」。
261
+
262
+ 于是命令面整体删除(ctx.commands 一次都不注册),换成两条宿主事件:
263
+
264
+ 1. gent/pre-step:论文工作区 + 主 agent + 还没有意图规格时,用 ctx.userQuestions.ask
265
+ 问**一道**选择题(四档:从零走到答辩 / 只补合规与答辩 / 只做答辩准备 / 先别自动跑)。
266
+ 选定后把意图写进决定日志、ctx.goals.create 建目标(宿主的 goal-round-driver 按它自动续轮),
267
+ 再注入一条 operating contract(怎么推进、怎么并行、哪里必须停)。
268
+ 2. ools/pre-execute:模型要过 G1-G4 时把决策改成 sk,由人在审批界面拍一次板。
269
+ 这是「人是作者」这条红线在自动驾驶下的落点:可以自动化流程,不能自动化拍板。
270
+
271
+ 边界(都写成了断言,不靠自觉):
272
+
273
+ - **只对主 agent**:委派深度 > 0 一律跳过,否则子 agent 会各自再问一遍(delegationDepthOf 同款规则)。
274
+ - **只对论文工作区**: indThesisRoot 找不到台账就完全不介入,别的项目里装了也不受打扰。
275
+ - **只问一次**:同一个 agent 只问一次;已有 意图规格.md 的会话只记一条日志、不重复打扰。
276
+ - **宿主缺服务就静默退化**:没有 userQuestions / goals 的组合同样能跑,只是退回单轮。
277
+ - **可关**:utopilot: { enabled: false } 退回「纯工具 + 技能」模式。
278
+
279
+ 代价:这是一次**给人的表面的破坏性变更**(命令全没了)。21 个工具名一字未动,
280
+ 所以模型侧与自动化不受影响;版本如实走 2.0.0。
281
+
282
+ 之前的中间形态(一条 /thesis + 九个动词)在这个版本里被整体替换:它解决了「列表太吵」,
283
+ 但没解决「人不该操作命令行」。留在这里作为教训:**先问清人要的是入口还是自动化**。
268
284
  ---
269
285
 
270
286
  ## 7. 数据与产物
@@ -310,14 +326,19 @@ export type IntakeAffect =
310
326
 
311
327
  | 层 | 命令 | 内容 |
312
328
  |---|---|---|
313
- | 纯逻辑与契约 | `npm run test:sandbox` | 430 条:相似度、归一化、处方、问题排序、状态机、幻灯渲染、OOXML/PDF 解析、记忆 SQL、代码结构摘要;装配契约(21 工具 / 9 命令 / 2 服务 / 4 类监听);`definePaperTool` 的 DSL→JSON Schema 编译 |
329
+ | 纯逻辑与契约 | `npm run test:sandbox` | 422 条:相似度、归一化、处方、问题排序、状态机、幻灯渲染、OOXML/PDF 解析、记忆 SQL、代码结构摘要;装配契约(21 工具 / **0 条命令** / 2 服务 / 5 类监听);自动驾驶的四种「不该介入」与关卡拍板;`definePaperTool` 的 DSL→JSON Schema 编译 |
314
330
  | **真实场景端到端** | `npm run test:e2e` | 65 条断言,见 §9.1 |
315
- | **真实宿主** | `npm run test:host` | 24 项:真实 cordis + dsh-tools 装配、工具 schema 被真实注册表接受、真实执行工具、宿主拒绝缺参、**闸门在真实 waterfall 上真的注入**、卸载后工具注销 |
316
- | **真实引擎装载** | `npm run test:host-load` | 11 项:真实 `dsh` 引擎解析并校验本插件 |
331
+ | **真实宿主** | `npm run test:host` | 23 项:真实 cordis + dsh-tools 装配、工具 schema 被真实注册表接受、真实执行工具、宿主拒绝缺参、**闸门在真实 waterfall 上真的注入**、卸载后工具注销 |
332
+ | **真实引擎装载** | `npm run test:host-load` | 13 项:用宿主自己的预检函数判 manifest(本仓库 + 已安装副本)会不会被停用,再用真实 `dsh` 引擎 `--dump-config` 组合 profile 并校验 `Config`;`DSH_HOME` 指向工作区内临时目录,无需放宽权限 |
317
333
  | 对外表面审计 | `npm run audit` | 工具/命令/技能契约一致 + 技能命名/BOM 检查,见 §9.3 |
334
+ | 脚本类型检查 | `npm run typecheck:scripts` | `scripts/**` 全部是 `.ts`,用 `tsconfig.scripts.json` 单独检查(`strict: false`:它们是从 JS 迁过来的构建脚本,只求抓错不追求类型完备)|
318
335
  | 真实网络(可选) | `npm run test:net` | 真实学术 API 检索 → 收录 → 笔记 |
319
336
 
320
- `npm run verify` = build + typecheck + 单元/契约 + 审计 + 端到端,是主线不变的守门命令。
337
+ `npm run verify` = build + typecheck(src 与 scripts)+ 单元/契约 + 审计 + 端到端,是主线不变的守门命令。
338
+
339
+ **全部源码与工具链都是 `.ts`**:`src/` 由 `tsc` 编译成 `lib/`(发布产物,`.gitignore` 里),`scripts/`、`tests/`
340
+ 由 Node 24 直接执行(type stripping,不需要 ts-node 之类的加载器)。代价是**跑脚本的工具链 Node 需要 ≥ 22.18**,
341
+ 而装机运行时不要求(它读 `lib/` 里的 JS)。
321
342
 
322
343
  ### 9.1 真实场景端到端基准
323
344
 
@@ -348,7 +369,7 @@ export type IntakeAffect =
348
369
 
349
370
  对外名字(工具名、命令名、技能名)是**契约**:学生与宿主都按名字调用,改名而不改文档
350
371
  就是文档骗人,改名而不改测试就是契约失守。这类漂移只靠人工看必然漏,
351
- 故 `scripts/audit-surface.mjs` 把它们固定成断言:**注册集必须恰好等于契约集**,
372
+ 故 `scripts/audit-surface.ts` 把它们固定成断言:**注册集必须恰好等于契约集**,
352
373
  多一个少一个都失败;技能 frontmatter 的 `name` 必须等于目录名(DSH 按目录名发现技能);
353
374
  `SKILL.md` 不得带 BOM(带 BOM 时宿主解析 frontmatter 返回 undefined,**整个技能被静默忽略**——
354
375
  文件明明在、技能却不存在,是最难排查的一类故障)。
@@ -371,7 +392,7 @@ export type IntakeAffect =
371
392
  5. **代码演练内置语言矩阵只覆盖 Go/TS/Python/Rust**,其余语言需在配置里补 `gates`。
372
393
  6. **子进程不可取消**:pandoc/marp/git 走 `spawnSync`,有超时上限,但 `exec.signal` 不进入子进程。
373
394
  7. **受限环境下管道 stdio 被拒**:某些沙箱里 `stdio: 'pipe'` 会 `EPERM`,故默认 `stdio: 'ignore'`。
374
- 8. **宿主契约以本地 shim 声明**:真实签名核对见 [docs/host-api.md](./docs/host-api.md),
395
+ 8. **宿主契约以本地 shim 声明**:真实签名核对见 [host-api.md](./host-api.md),
375
396
  运行时行为由端到端基准与真实宿主冒烟兜底。
376
397
  9. **真实 `dsh` 引擎的装载验证需要写 `$DSH_HOME/profiles/<name>/cordis.yml`**(引擎在
377
398
  `--dump-config` 前就会写它)。受限沙箱下该写入会被拒,`npm run test:host-load` 因此需要放宽文件权限;