# dsh-study-buddy 设计文档(v1.0 · 文档式笔记) > 版本基线:v1.1.1 | 状态:现行设计 | 读者:插件/预设的维护者与二次开发者 > 相关:`docs/技术文档.md`(API 与实现细节)、`docs/refactor/需求分析.md`(需求基线)、`docs/refactor/架构选型.md`(本轮选型)、 > `docs/refactor/重构计划.md`(执行顺序与偏离记录)、`docs/用户使用指南.md`(使用者视角)、`docs/经验文档.md`(方法论与踩坑) --- ## 1. 这是什么 `dsh-study-buddy` 是给 DeepSeek Harness(DSH)的**「通用学习 Agent」模式**,由两部分组成: | 组成 | 是什么 | 装在哪 | | :-- | :-- | :-- | | 插件 | 注册 18 个 `note_*` / `study_*` 工具,用 `node:fs` 直写用户 Obsidian vault | DSH profile 的 `node_modules` | | 预设 | 「学习伙伴」Agent:persona + 6 个技能 + 默认期望模板 + 插件挂载行 | 随包发:`package.json` 的 `dsh.bundle.patch` → `presets/study.patch.yml`(**声明式**;目录式预设自 DSH 0.1.7 起已删除) | | 机器相关配置 | `vaultRoot`(以及可选的 `skipDirs` / `searchRoots` / `domainFolders`) | 用户级:环境变量 `DSH_STUDY_VAULT` 或 `/study-buddy.json`(包内零绝对路径) | 一句话定位:**快节奏、听指挥、把知识沉淀成可独立复读的文档式笔记,按用户确认过的目录结构写进 vault**。 三条不变量(任何改动都不得破坏): 1. **磁盘是唯一真相**——笔记就是普通 `.md`;用户手改、git 回滚永远有效;`.study/` 只存过程状态(删掉不丢笔记)。 2. **决定权在用户**——讲不讲、写什么、**落到哪个目录**、改不改旧笔记,都由用户下令;Agent 只给建议与对比。 3. **fail-loud**——配置错误、状态损坏、门禁未满足一律抛错并给修复步骤,不静默降级。 --- ## 2. 设计转向:从「原子卡片」到「文档式笔记」 ### 2.1 为什么要转(v0.9 → v1.0) | 旧问题 | 表现 | v1.0 的对策 | | :-- | :-- | :-- | | **内容简陋** | 卡片是"骨架":固定小节 + 要点式条目,读完仍要回原始资料 | 块 = **可独立阅读的知识单元**;写法由《笔记期望.md》驱动,尽细尽全、字数不设限 | | **笔记被无脑堆叠** | 一块一块按领域键丢进目录,没有阅读顺序、没有入口 | 归档前**必须先做文件夹规划**(用户确认);每个主题目录有 `微目录.md`;有覆盖度总览 | | **约束过多且写死在代码里** | 三型模板 + 必填小节 + 定义 ≤60 字硬拒绝 + 分型推断 + 14 条 lint 打分 | **模板/分型/字数硬限/分值全部删除**;唯一约束来源是用户自己的期望文件 | ### 2.2 三类读者与三种读法(决定笔记的物理结构) | 读者状态 | 想干什么 | 笔记应提供 | | :-- | :-- | :-- | | 完全遗忘 | 从头学一遍 | 完整推导、定义、例子、边界与反例 | | 半遗忘 | 快速捡起 | 顶部一句话定位 + 主干结论 | | 未遗忘(在干活) | 查一个细节 | 表格 / 代码片段 / 判据(可跳读定位) | 结构不再由代码强加,而是**由期望文件里的"结构偏好"小节表达**(默认模板见 `presets/study/assets/笔记期望.md` 的「结构」一节)。 ### 2.3 目录模型:资料 / 章 / 节 / 块 ``` 游戏开发 ├── 框架设计 │ └── 技能系统设计 │ ├── 微目录.md ← 主题导航入口(note_toc 生成) │ ├── 为什么要推翻教程做法.md │ └── 三层骨架与唯一装配点.md 计算机通识 ├── 计算方法 │ ├── 第1章 误差与有效数字.md ← 章可以是文件… │ └── 第2章 线性方程组数值解法 ← …也可以是目录 │ ├── 微目录.md │ └── 直接法 │ ├── 微目录.md │ └── 高斯消元法.md ``` - 层级**理论支持 N 级**,课程笔记常用四层;项目类笔记可少一层。 - **文件名不加序号前缀**:阅读顺序由 frontmatter `顺序` 键与微目录承担(文件可自由改名)。 - 目录**不预创建**:规划确认时不落盘,由 `note_write` 首次写入时创建("没确认的东西不落盘")。 --- ## 3. 硬门禁:本版的核心机制 需求 R25 要求"未读期望 / 未确认规划 / 越界路径 → 拒绝写入",且**由工具强制**而不是靠 persona 自觉。 理由:persona 是提示词,模型可能忘;而门禁若只落在进程内存,DSH 重启就凭空失效。 ``` note_write 前置校验(按序短路,任一失败即抛错且磁盘零改动) ① 期望已读:.study/session.json 的签名 == 当前《笔记期望.md》的 mtime|ctime|size ② 规划有效:.study/plans/.json 存在、confirmed、未过 planTtlHours ③ 路径在规划内:path 在 rootPath 之下、title 在 items 里、该 title 未被消费 ``` `confirmed` **只能由 `note_plan(action=confirm)` 置位**(同一个 `rootPath` 参数在 create 时传规划根、在 confirm/abandon 时传 planId, 因此 schema 的必填集合不随动作漂移)。提案与确认是同一条凭据的两次写:确认后才算"用户拍过板"。 | 设计点 | 取舍 | | :-- | :-- | | 状态落 `.study/` 而非内存 | 重启后门禁仍有效;代价是磁盘多两个文件(都是过程状态,删掉不丢笔记) | | 用**文件签名**而非"读过一次" | 用户中途改期望 → 签名变 → 必须重读(热配置立即生效,需求 F1) | | **不回退**到内建默认写法 | 允许回退等于代码里还得留一份默认模板,"约束只从期望来"立刻变成两处口径 | | 规划**会过期**(默认 24h)+ 可 `abandon` + 需显式 `confirm` | 门禁必须可解,也必须可开:一次误操作不能把笔记写入永久锁死,而"没有任何入口能置位 confirmed"等于永久锁死(v1.0.0 真机检查的 P0) | | 确认时把有效期**重新起算** | 用户的"现在就写"才是凭据生效时刻;否则搁置过久的提案会在确认后立刻过期,等价于"确认也没用"。代价是 `createdAt` 不再等于提案时刻,故 confirm 的返回文案与文档都写明这一点 | | 消费按**标题**记账 | 同一规划里一个标题只能写一次,防止"边写边改结构" | | 提案**只在对话里**(需求 R11) | vault 不出现可见的规划文件;`.study/plans/` 只存凭据与消费记录 | **失败文案一律给修复步骤**——门禁最现实的风险是把用户永久锁在外面(见 `docs/refactor/重构计划.md` §八 风险)。 --- ## 4. 笔记模型 ### 4.1 frontmatter(六键 + 三文档键) ```yaml --- ID: 202610241430_ab12cd # note_write 自动生成 标题: 高斯消元法 领域: #计算方法-线性方程组 # 首个标签 = 领域键(可选) 来源: 《计算方法》 # 必填 状态: 已确认 # 草稿 / 已确认 / 需更新 来源章节: 《计算方法》第2章 线性方程组数值解法 / 2.1节 顺序: 3 简介: 用初等行变换把系数矩阵化为上三角,再回代求解。 --- ``` - **只认这几个键**;`简介` 无长度限制(这是 v0.9 的 `定义 ≤60 字硬拒绝` 的替代:保留检索价值,去掉约束)。 - `定义` 作为 `简介` 的别名仍可读(存量卡不必改写);`模板` 键读取时忽略(模板概念已退场)。 ### 4.2 文档类型三态 | 类型 | 判据 | 能力 | | :-- | :-- | :-- | | `block` | 有 ID **且**有 `来源章节` | 完整能力;计入覆盖度 | | `legacy` | 有 ID,无 `来源章节` | 可检索/读取/更新;补上 `来源章节` 即升级为块 | | `note` | 无 ID | 只读;`linkIntoNotes: true` 才允许写关联 | ### 4.3 `来源章节` 语法与归一 ``` 《资料名》第N章 章标题 / N.N节 ← 规范写法 《资料名》第N章 章标题 ← 无节号合法 ``` 归一无损:全角数字、`第 2 章` 的空格、全角斜杠、多余空白统一;**识别不出时原文保留**并进总览的"未归类"(不报错、不猜测)。 --- ## 5. 架构 ### 5.1 分层与数据流 ``` Obsidian vault(磁盘 = 唯一真相)+ .study/(过程状态) │ node:fs 扫描(vault + searchRoots + 会话工作目录) ▼ vault.ts ──► search.ts 索引与加权召回(mtime/ctime/size 签名缓存,**不常驻正文**) │ │ │ ▼ │ IndexedCard ──► frontmatter.ts(六键 + 三文档键) │ │ ▼ ▼ notemodel.ts(段落模型)─┬──► note.ts(块契约:校验/渲染/三动作更新) ├──► links.ts(关联增删 + 改名/入链/断链) ├──► lint.ts + lintrules.ts(4 条规则,无分值) ├──► overview.ts(覆盖度聚合,纯函数) ├──► sourceSection.ts(来源章节归一) └──► order.ts(排序口径:唯一文本比较来源) │ ▼ dirs.ts(DirIndex/层级/期望路径/目录列举) + store.ts(session.json) │ ▼ gate.ts(三连校验)+ planstore.ts(规划凭据)+ archive.ts(先存档后写正文) │ ▼ index.ts(VaultStore 业务编排 + apply)──► tools.ts(18 个工具) │ └──► host.ts(**唯一宿主接触面**:契约表 + 特性探测/降级)+ config.ts(配置来源链) ``` ### 5.2 模块边界(23 个) | 模块 | 职责 | 依赖 | 纯度 | | :-- | :-- | :-- | :-- | | `host.ts` | **唯一宿主接触面**:`HOST_CONTRACTS` 契约表 + 能力探测/降级(tools / systemPrompt / agent-pre-step / **注入消息来源准入** / 会话快照与 cwd)+ 可选 dsh-loader 稳定入口 | node:path, node:module | 纯探测 | | `config.ts` | 配置来源链(行 config / `DSH_STUDY_VAULT` / `/study-buddy.json`)→ `VaultLayout`;未知键告警、缺 vaultRoot fail-loud | dirs, lint, vault | IO | | `vault.ts` | 扫描/跳过清单/路径安全/原子写/落盘解析/`readNoteSource` | node:fs | IO | | `search.ts` | token 化、倒排、字段加权、多根去重、`snippetOf` | frontmatter, vault | 纯 + IO | | `frontmatter.ts` | 六键 + 三文档键解析/生成;`简介` 提取 | notemodel | 纯 | | `note.ts` | ID/校验/渲染/`append`·`replace`·`definition`/wikilink 关联 | frontmatter, notemodel, sourceSection | 纯 | | `notemodel.ts` | 段落切分/渲染/标题匹配/块插入/围栏语言/块抹除 | — | 纯 | | `dirs.ts` | 路径归一、DirIndex、层级与目录名校验、`listDir`、期望路径 | vault | 纯 + IO | | `gate.ts` | 三连校验判定 + 提案渲染 | store, planstore, dirs, vault | 纯判定 + IO | | `planstore.ts` | 规划记录状态机(建/确认/消费/放弃/过期) | dirs, vault | IO | | `archive.ts` | 存档写/列/读 + `archiveThenWrite`(先存档后写正文) | notemodel, vault | IO | | `store.ts` | `.study/session.json`(签名、已读规划) | vault | IO | | `overview.ts` | 覆盖度聚合与渲染 | sourceSection | 纯 | | `sourceSection.ts` | `来源章节` 语法与归一 | — | 纯 | | `order.ts` | 文本全序比较(显式 `zh-Hans-CN` + 数值序) | — | 纯 | | `links.ts` | 关联增删/判重 + 改名/入链重写/断链/文件名计划 | frontmatter, notemodel, note, vault | 纯 | | `lint.ts` | `RULES` 注册表(4 条)+ 报告 + 期望检查项解析 | notemodel, lintrules | 纯 | | `lintrules.ts` | 会话残留/代码块语言/外部资源判定 + 共享正则 | notemodel | 纯 | | `state.ts` / `memory.ts` | `.study/progress.json` / `memory.json` | vault | IO | | `opener.ts` | 开场门禁(提示段 + 预步提醒) | — | 纯 | | `tools.ts` | 18 个工具定义(schema 即文档;描述由注册表派生) | note, lint | 纯 | | `index.ts` | `VaultStore`(业务编排)+ `apply`(注册与生命周期) | 全部 | IO | **设计约束**:除 `vault/search/planstore/archive/store/state/memory/dirs/config`(IO)与 `host`(宿主探测)外全部是**纯文本变换、零宿主依赖**,可独立单测;`@deepseek-ai/*` 保持构建 external,且 `src/` 与 `lib/` 都**不得**出现平台包的静态 import(`tools/verify-contract.mjs` 有断言)。 > `state.ts` 与 `memory.ts` **未合并**:合并只少一个文件却增加一次回归面(见 `docs/refactor/重构计划.md` §九 偏离记录)。 ### 5.3 一次工具调用的生命周期 ``` 工具 execute(args, exec) → VaultStore.() → assertVault()(fail-loud:vault 不可读立即报错) → 门禁(仅 note_write):gate.checkWrite 三连校验 → ensureIndex(sessionCwd)(TTL 内复用;未命中/找不到时 force 重扫) → 业务:解析 ref → 纯函数变换 → 写前校验(可写性/冲突)→ atomicWrite → invalidate()(写后失效缓存) → 返回文本(固定前缀回显行:已写入/已更新/已生成/未命中…;有跳过项时追加 ⚠ 行) ``` --- ## 6. 关键设计决策(含取舍) > v0.9 的 D1~D21 中与卡片/模板/分值相关的条目已随功能删除;下表是 v1.0 现行决策, > 编号延续历史(v0.9 的 D5/D6/D7/D9 等仍适用于改名与关联路径),历史明细见 git 历史与 `docs/archive/审查修复记录.md`。 | # | 决策 | 取舍与理由 | | :-- | :-- | :-- | | D1 | 笔记即普通 `.md`,不建独立数据库 | 与旧笔记同库同目录,Obsidian/git 直接可用;代价是索引按签名重扫 | | D2 | **索引不常驻正文**(只留元数据 + 倒排 token) | 文档式笔记单篇可达 10KB 级,常驻全库正文会线性膨胀;代价是 `note_search` 的 snippet 要现读命中前 N 篇、批量体检要逐篇读盘(低频重操作,可接受) | | D3 | 门禁状态落 `.study/` 而非进程内存 | 重启后仍有效;代价是多两个过程状态文件(删掉不丢笔记) | | D4 | 期望"已读"用**文件签名**判定 | 用户中途改期望必须重读(热配置立即生效);代价是每次写入前多一次 `stat` | | D5 | 期望文件缺失**不回退**到内建写法 | 允许回退 = 代码里还得留一份默认模板,热配置立刻变成两处口径 | | D6 | 规划提案**只在对话里**,凭据落 `.study/plans/` | vault 不出现可见规划文件;代价是"上次规划与完成度"不可查(路线图项) | | D7 | 规划**会过期**且可 `abandon`,并需 `note_plan(action=confirm)` 显式置位 | 门禁必须可解**也必须可开**;过期默认 24h(`planTtlHours`),**确认时重新起算** | | D8 | 消费按**标题**记账 | 一个规划里一个标题只能写一次,防"边写边改结构" | | D9 | **先存档、后写正文**收成唯一入口 `archiveThenWrite` | 任何替换路径只要走它就不会写出"正文已改、历史已丢";存档失败即中止 | | D10 | 历史改**外部存档**,正文不留 `
` | 折叠块会让笔记随时间变长、正文与历史混在一起("堆叠"的另一种形态);代价是历史在 `.study/` 里不显眼——用 `note_history` 可列可读可恢复 | | D11 | 关联改 **Obsidian wikilink** | Obsidian 里可点、改名由 `note_rename` 统一改写;代价是失去"ID 锚点"的强定位(改用标题寻址 + 断链检测) | | D12 | 关联判重**限「关联」小节内** | 全正文判重会把正文里恰好以 `- 前置:` 开头的行误判成"已关联",静默跳过真实关联(v0.9 的 BIZ-6 教训) | | D13 | 改名**只写 vault 内文件**;文件名仅在"= 旧标题清洗结果"时同步 | 避免"改标题顺带搬文件"的意外;只读根命中时报告标注"只读根未写入" | | D14 | 覆盖度**不虚构章节** | 完整性只由笔记里真实出现的章节号与用户声明的资料构成;缺口只给"未归类"与"节号跳号" | | D15 | 节号按**数值**排序 | `2.10` 必须排在 `2.2` 之后(字符串比较会错) | | D16 | lint **废弃 100 分制**,只给问题清单 | 分值建立在"模板缺节扣 5 分"之上;模板退场后分值不再可比,留着只会误导 | | D17 | 规则收敛为**单一注册表**(4 条) | `RULES` 是 id/级别/标题的唯一来源;工具描述与 `ruleIds()` 自动跟随 | | D18 | "检查什么"也走**热配置**(`expect-rule`) | 用户在期望文件里写 `- [检查] 禁止 xxx` 才检查;不写就不检查——避免把检查写死在代码里 | | D19 | 写路径**不为提示付全库代价** | 同名提示由 `titleHints`(索引重建时填充、写入后增量维护)提供 O(1) 查询 | | D20 | 缓存**不能牺牲可见性** | TTL 只作用于命中路径;未命中/找不到时强制重扫一次("刚写完就能搜到"是核心用法) | | D21 | 安全阀**拦异常根、不拒绝正常大库** | 文件数超限从"抛错让工具全不可用"改为**截断 + 显式警告** | | D22 | 构建前**清空 `lib/types`** | 删掉的模块若留下 `.d.ts`,消费方仍能 import 到"不存在的 API"——编译期零信号 | | D23 | `note_write` 的**规划凭据必填** | 让"先规划后落盘"成为接口契约而不是流程建议;代价是模型必须传 planId(被拒时按提示走) | | D24 | 文本排序**显式 pin locale**(`order.ts` 的 `compareText`) | 裸 `localeCompare` 按宿主默认 locale 比较,同一 vault 在不同机器/CI 上顺序不同(v1.0.0 在 en-US runner 上暴露);改用 `zh-Hans-CN` + 数值序让结果只由内容决定——代价是依赖 Node 自带 ICU 的 zh 数据(缺失时守卫测试报红,不静默)。不做"ASCII 走码位"快路径:混用两种比较器会破环(传递性) | | D25 | 预设随包以**声明式 bundle patch** 交付;`vaultRoot` 等机器相关值走用户级来源链 | DSH 0.1.7-rc.1 删除了目录式预设(`d1e22a7e24` / #4569),"复制预设目录"的交付方式彻底失效且**失效形态是行消失而非报错**;同时包要开源就必须把作者本机路径赶出仓库。代价:预设改动需重新装包/重建副本(`pnpm run verify:deploy` 比对哈希),`vaultRoot` 改动需重启 DSH(挂载期不变量) | | D26 | 宿主耦合收成一个 **`host.ts` 适配层 + `HOST_CONTRACTS` 契约表 + 可执行探针**,不引 dsh-loader 作硬依赖 | 平台改动不会变成编译错误:0.1.3 换 `session.events`、0.1.7 删目录式预设、更早还有 typert codec 换形状,三次都是"契约漂移"而非代码错。契约表给出"平台源码坐标 + 失效后果 + 改哪里",探针(`tools/verify-contract.mjs`)对**运行中的 DSH** 断言并带负向断言;dsh-loader 覆盖面(settings/web/包名别名/`Session.events`)不含本插件的三个真实触点,故只做**可选探测**(不 import、不 inject——inject 了但用户没装会让整行永远 `pending`) | --- ## 7. 扩展点(下一轮怎么加东西) | 想加什么 | 改哪里 | 需要同步 | | :-- | :-- | :-- | | 一条 lint 规则 | `lint.ts` 的 `RULES` 加一个数组元素(判定逻辑放 `lintrules.ts` 或就地) | `tests/lint.spec.ts`(正例 + **白名单反例**);工具描述与 `rule` 枚举自动跟随 | | 一个新工具 | `tools.ts` 加定义 + `VaultStore` 加方法 | `tests/tools.spec.ts` 名字集合、`tests/apply.spec.ts` 工具数 | | 一个 frontmatter 键 | `frontmatter.ts` 的 `CardMeta` + 渲染 + `note.ts` 校验 | `tests/frontmatter.spec.ts` 往返、`note_write` schema、技能与指南 | | 一种目录导航视图 | `dirs.ts` 加纯函数 + `note_list` 加参数 | `tests/dirsession.spec.ts` | | 覆盖度新口径 | `overview.ts` 加纯函数 | `tests/overview.spec.ts` | | 期望文件新维度 | 只改 `presets/study/assets/笔记期望.md` 与技能文案(**不改代码**) | 技能与指南 | | DSH 升级后某个宿主契约漂移(工具/提示段/事件/会话) | `src/host.ts` 的探测与 `HOST_CONTRACTS` 表(排障说明同步);必要时 `tools/verify-contract.mjs` 的探针 | `docs/技术文档.md` §9.2、`tools/README.md`、经验文档 | | 预设组合变化(加/减工具行、改 persona) | `presets/study.patch.yml` 的声明 `config.plugins` | `tests/preset.spec.ts`(行集合/键集合)、`pnpm run verify:deploy`(仓库 ↔ 已安装副本) | | 存档策略(保留 N 版 / 压缩) | `archive.ts` | `tests/gate.spec.ts` 的存档段 | | 语义检索(路线图) | `search.ts` 增召回通道 + 索引侧向量缓存开关 | 需求明确不在本轮 | --- ## 8. 验收与质量门 | 门 | 标准 | | :-- | :-- | | 构建 | `pnpm run check` = typecheck + vitest(293 项)+ esbuild 全绿 | | 交付形态 | `package.json` 声明 `dsh.bundle.patch`;`presets/study.patch.yml` 声明 `preset-study`(12 行组合 + 3 个压缩子行),`study` 行 config 键 ⊆ 插件认得的键且**不含 `vaultRoot`**——由 `tools/verify-contract.mjs` 与 `tests/preset.spec.ts` 双侧钉住 | | 宿主契约 | `HOST_CONTRACTS` 6 个接触点逐点对**运行中的 DSH** 断言(`pnpm run verify:contract`,含负向对照;注入消息来源另有"喂给平台自己的 v4 行编码器"的活断言);探针定位不到 DSH 时跳过而不是假红(CI 在 ubuntu 上没有 DSH) | | 工具面 | **18 个工具**,名字集合由 `tests/tools.spec.ts` 钉住;`card_*` 整族**必须不存在** | | 排序口径 | `src/` 中不存在裸 `localeCompare`/`Intl.Collator`(源码守卫);`String.prototype.localeCompare` 换成 en-US 实现后,聚合与目录顺序不变 | | 约束解除 | `src/` 中不存在三型模板、必填小节检查、字数硬拒绝、分型推断;`templateHints`/`mocDir` 配置键不存在 | | 硬门禁 | 三条门禁各自有端到端用例,**每条都断言目标文件不存在**(磁盘零改动) | | 数据安全 | 原子写、路径越界拒绝、只读根先校验后写盘、rename 冲突零写入、replace 先存档 | | 不退化 | 多根检索与歧义、跳过项按原因回显、TTL 强制重扫、`_autoPrefs` 语义——全部有用例 | | 性能 | 工具 schema ≤ 旧基线 +10%(实测 14.7 KB / 18 工具);索引不常驻正文有守门用例 | | 一致性 | 文档/配置/技能三处口径由测试钉住(领域键表、规则清单、preset config 键、期望模板可解析) | | 产物 | `lib/index.js` 无 `@deepseek-ai` 运行时导入;冒烟加载输出 18 个工具 | | 用户数据 | 只写可写根、原子写、`dryRun` 可预演、失败不写半个文件、跳过文件必须回显 | **遗忘测试**(人工,每轮迭代做一次):随机抽 5 篇笔记只给标题,限时 5 分钟口述"在解决什么问题、主线是什么、关键结论、怎么验证",按覆盖度/因果连贯/可操作各 0~5 分,目标均值 ≥10/15。 --- ## 9. 边界与已知限制 - 检索是**关键词召回**(CJK 双字组 + 英文单词),无语义向量;近义改写可能漏检。 - 索引 TTL 默认 2s,但未命中强制重扫一次,因此"刚写完立刻读"仍即时可见。 - 索引只驻留元数据 + token,但**单次重建仍要读全库正文**做 token 化(读盘量与 v0.9 持平)。 - 覆盖度依赖 `来源章节` 的书写规范;解析不出的条目进"未归类",不做语义补全。 - 微目录的**生成段**会被重写;用户手写内容须放在 `note_toc:begin` 之前。 - `note_lint` 只覆盖 4 条可判定规则;"段落长度""因果连贯"等人工约束没有自动校验。 - 旧二进制 Office 格式(.ppt/.doc/.xls)不直接读。 - 规划提案不落盘 → "上次规划与完成度"不可查(路线图项:计划块 vs 已落块对照)。 - 记忆/进度中的文本会随开场门禁进入上下文,插件只给"指令性语句"软提示,不做内容净化。 - **预设随包发**:改了 `presets/study.patch.yml`/技能后,要重建 profile 里的副本(`pnpm run verify:deploy` 会比对哈希)或重新装包;`vaultRoot` 是挂载期不变量,改完需重启 DSH(或让预设重新挂载)。 - 兼容层是**可选**的:装了 `@dsh-plugin/dsh-loader` 就优先走它的稳定入口,没装完全照常;它的覆盖面不含 本插件的三个真实触点(`tools.register` / `systemPrompt.section` / `agent/pre-step`),所以真防线是 `src/host.ts` 的探测 + 探针断言,不是它。