# @deepseek-ai/dsh-settings-file

[English](README.md) | 中文

基于文件的设置提供方。一个 YAML 或 JSON 文档承载全部 namespace 分节；外部编辑经 `ctx.settings` 热发布，`update()` 在写锁下先重读文档再原子写回，保留用户的 YAML 注释、当前未加载插件所拥有的分节，以及任何本进程尚未观察到的磁盘变更。

## 配置

| 字段 | 含义 | 默认 |
|---|---|---|
| `path` | 设置文档路径；扩展名决定格式（`.yaml`/`.yml`/`.json`） | harness home 下的 `settings.yaml` |
| `dshHome` | `path` 省略时使用的 harness home | `$DSH_HOME` 或 `~/.dsh` |
| `watch` | 监听文档并热发布外部编辑 | `true` |
| `debounceMs` | watcher 写入稳定窗口（毫秒） | `100` |

默认值解析是一步显式的 `resolveSpec(config)`；不支持的扩展名在加载时报错。

## 行为

- **启动失败并明确报错，重载保留最后可用值。** 存在但非法的文档使插件加载失败；运行中不可读或不可解析的编辑只告警并保留最后可用分节。文档缺失时所有 namespace 按默认值与 `base` 解析；删除文档发布同样的空状态。
- **每次写入都是一次读-改-写。** persist 先重读文档并把任何差异发布进 seam——无论是仍在 watcher 防抖窗口内的外部编辑、watcher 漏掉的变更，还是另一个进程的写入——再基于这份新鲜文本渲染，因此写入绝不会复活陈旧文档，也不会丢掉未观察到的同级分节。若磁盘上的文档已变为非法，写入会明确报错并拒绝执行，而不是覆盖用户的手工编辑。
- **写入持有跨进程写锁。** 读-渲染-rename 流程在以 `wx` 创建的同级锁文件 `<file>.lock` 保护下运行，带指数退避与 2 s 的获取期限。竞争者会超时，但不会移除现有锁，因为锁龄无法区分已经崩溃的所有者与被暂停但仍存活的写入方；遗留锁恢复须由操作者执行。读取方从不取锁：rename 提交是原子的，重载因此始终一致。
- **写回原子、仅属主可访问、抗符号链接。** 渲染以 `0600` 权限独占创建随机后缀临时同级文件（`wx` 拒绝跟随预埋符号链接）后 rename 覆盖目标，失败时清理临时文件。
- **YAML 编辑是叶子级 diff。** 写入只设置发生变化的值、只删除被移除的键，因此注释、锚点与排版在每个未触碰的节点上以及每个被改键值对的键上都得以保留；被改的数组（或其他非 map 值）整体替换，其中的注释随之一同被换掉。JSON 重新序列化，无注释。
- **重载与写入共享一条操作链。** watcher 刷新与来自各 namespace 队列的 persist 按队列顺序逐个执行；每次渲染都基于上一次操作提交后的文本。
- **watcher 的 ready 信号做一次对账。** 初始加载与 watcher 自身的建立存在竞态，因此其间写入的变更绝不会触发事件；ready 时的对账补上这个启动缺口。
- **原生 watcher 接收规范化路径。** 在 Chokidar 打开目标之前，提供方会对层级最深的现有祖先路径执行 realpath 解析，再拼回缺失的后缀。文件访问和面向用户的诊断仍使用配置路径，从而避免 Windows 在 libuv 内部混用 8.3 别名与长格式事件路径。
- **dispose（资源释放）在每种 watch 模式下都保证完全停稳。** 卸载先把提供方标记为已关闭，在 watcher 存在时将其关闭，再等待所有已排队或进行中的文档操作完成，之后不再有任何发布。
- **按内容抑制自写。** 提供方缓存最后可用文本；watcher 事件内容与缓存相同（含自己的写入）即为 no-op。
- **Host 配置适配器会收到解析后的路径。** `ctx.settings.documentPath` 是 `resolveSpec()` 得出的绝对文件名，包括自定义 YAML/JSON 路径；`prepareDocument()` 会保留现有文件，或在 Host 打开文档前，以仅属主可访问的权限独占创建缺失的空文件。浏览器只收到可用性标志，绝不重建 `$DSH_HOME`，也绝不提交文件系统目标。

## 模型体验

间接生效：本提供方只存储并发布 namespace 分节，任何模型效果都经由 `ctx.settings` 的消费方产生，并由各消费方自己的接口文档说明。

#### KV Cache 影响

无直接失效；请求前缀的任何变更均由消费方插件负责。

## 已知限制与暂缓事项

- **同 namespace 冲突仍是后写胜出** — 写锁加读-改-写让并发写入者不会丢掉彼此的 namespace，但两个写入者编辑同一个 namespace 时仍以较后的写入为准；没有按值合并，也没有修订检查。
- **漏掉的 watcher 事件在下一个信号前保持不可见** — 读取从不重新 stat 文件，因此 watcher 漏报的变更只会在下一个事件、下一次写入或重启时被并入。
- **注释保留仅限 YAML 且仅限 map 形状** — JSON 文档重新序列化，无注释（JSON 本身没有），且被改数组内部的注释（或行内附着在被改标量值上的注释）随其所描述的值一同被换掉。
- **无值间接引用** — 分节存字面值；面向密钥的 `${env:VAR}` 式引用是暂缓实现的 seam 层功能。
