Files
deepseek-harness/packages/settings/settings-local
Yichen Jiang ec0786e099 feat(settings): add user-settings seam (ctx.settings) + file provider
Two-package capability family mirroring session-persistence/:

- dsh-settings: abstract Settings service — namespace registry with
  caller-fiber effect registrations, layered resolution (schema defaults
  < composition base < user document), schemastery validation,
  per-namespace deep-equal commit detection, and the settings/updated
  event. Boot/registration validation fails loud; provider publishes
  keep last-good per namespace.
- dsh-settings-local: settings.yaml/.json provider — resolveSpec
  defaulting to $DSH_HOME/settings.yaml, chokidar hot reload,
  content-equality self-write suppression, atomic 0600 tmp+rename
  writes, comment-preserving YAML namespace patching.

Consumers register inside ctx.inject(['settings'], …), so every
composition works unchanged without a mounted provider. Real Loader +
Include composition test proves cordis.yml boot and external-edit hot
propagation; HMR disposal test proves registry cleanup. Both packages
hold per-file 100% coverage.

Doc budgets rise 1705→1710 (AGENTS.md) and 835→845 (packages/README.md):
one structural line per file for the new package group.

Agent Note: .agents/notes/implemented/architecture/2026-07-28-user-settings-seam.md
2026-07-29 10:19:32 +08:00
..

@deepseek-ai/dsh-settings-local

English | 中文

File-backed settings provider. One YAML or JSON document carries every namespace section; external edits hot-publish through ctx.settings, and update() writes back atomically while preserving the user's YAML comments and any section owned by a plugin that is not currently loaded.

Config

Field Meaning Default
path Settings document path; extension picks the format (.yaml/.yml/.json) settings.yaml under the harness home
dshHome Harness home used when path is omitted $DSH_HOME or ~/.dsh
watch Watch the document and hot-publish external edits true
debounceMs Watcher write-settle window in milliseconds 100

Defaulting is one explicit resolveSpec(config) step; an unsupported extension fails at load.

Behavior

  • Boot fails loud, reload keeps last-good. An existing-but-invalid document fails plugin load; once live, an unreadable or unparsable edit warns and keeps the last good sections. A missing document resolves every namespace from defaults and base; deleting it publishes the same empty state.
  • Write-back is atomic and owner-only. persist writes <path>.tmp with mode 0600 and renames over the target. YAML writes patch one namespace in the comment-preserving document; JSON re-serializes.
  • Self-write suppression by content. The provider caches the last good text; a watcher event whose content equals the cache (its own write included) is a no-op.

Model Experience

Indirectly, through consumers of ctx.settings: this provider only stores and publishes namespace sections, and each consumer's own surface documents any model effect.

KV Cache effect

No direct invalidation; the consuming plugin owns any request-prefix changes.

Known Limitations and Deferred Work

  • No cross-process write lock — concurrent writers (for example TUI and web on one home) converge by atomic replace plus watcher reload, last write wins; a lockfile is deferred until real contention shows up.
  • Comment preservation is YAML-only — JSON documents re-serialize without comments (JSON has none) and lose hand formatting.
  • No value indirection — sections hold literal values; ${env:VAR}-style references for secrets are a deferred seam-level feature.