AGENTS.md

August 14, 2026 · View on GitHub

Out-of-tree DeepSeek Harness plugin (host + Web client bundle): form-level and file-level configuration management with an open registration SDK. Self-contained repository — every source, compiler, documentation, and project-reference path stays below this root; lib/ is committed and kept in sync with source.

Layout

src/index.ts            host entry: function plugin (name/inject/Config/apply, no default export)
src/config.ts           serializable schema + direct-call defaults (message only)
src/runtime.ts          Cordis activation + host-boundary wiring (ctx.dshSettingsPlus, fabric binding)
src/crawler.ts          read-only enumeration: listNamespaces (settings.describe, redacted) / listCompositionConfigs (registry rows)
src/service.ts          the dshSettingsPlus host service: reads delegate to the crawler, writes route through the settings seam with expectedRevision guards
src/fabric.ts           optional gateway exposed-namespaces widening (loaded from host context at binding time; three-posture)
src/sdk.ts              open registration SDK: registerUserSettings / defineSettingsSection / settingsNamespace brand + ./sdk export
src/file-browser.ts     closed writable manifest ($DSH_HOME root yml/yaml + profiles/<name>/cordis*.yml) + realpath boundary; never writes
src/file-store.ts       atomic reads/writes through the injected FsLike channel, byte caps, expected-version write guard
src/yaml-editor.ts      comment-preserving YAML round-trip
src/patch-validator.ts  semantic validation that rejects patch edits breaking the document
src/hmr-aware.ts        save strategy copy: the host owns reloading, this plugin never suggests a restart
src/invariant.ts        invariant companion (documented empty installer — see its reason comment)
src/client/             browser half, served as the single file lib/client.js
  index.ts              settings.section registrations: entry (order 30) + three-phase status row + per-entry sections, signature-diff reconcile
  catalog.ts            observable catalog store + injectable settingsPlusCatalog source seam
  component.tsx         entry / status-row / data-section components
  schema-form.tsx       generic schema form renderer (whitelisted controls, secret placeholder protocol, reset, conflict, restart notice)
  form-logic.ts         pure form logic: path ops, secret placeholder decisions, conflict classification
  locales.ts            zh/en dictionaries; zh is the key-set source of truth, en satisfies Record<keyof typeof zh, string>
tests/                  node-env host specs + jsdom client specs (client.spec.ts, schema-form.spec.tsx, form-logic.spec.ts, …)

Contracts with the harness (do not drift)

  • The crawler and the SDK consume the host settings seam through minimal local contracts, never an import of the host package: crawler calls only ctx.settings.describe({ redactSecrets: true }), the SDK only ctx.settings.register(...). There is no second enumeration surface — the host settings service is the single arbiter of registered namespaces, and duplicates fail loud at register time.
  • listCompositionConfigs reads live ctx.registry rows: only rows whose Config exposes toJSON (schemastery) and whose fibers carry a string row id are surfaced; secrets are stripped by the schema-walking redactValue.
  • The dshSettingsPlus write boundary is narrow by design: updateComposition/removeComposition accept only ids that are registered settings namespaces. Plain cordis.yml rows are rejected loudly — file-surface or cordis.patch.yml is the answer.
  • cordis.patch.yml carries three rows: dsh-settings-plus, dsh-settings-plus-invariant, and a disabled cordis-fabric stub. The Fabric binding is optional: no facade mounted → safe no-op; facade mounted but malformed → loud error. Never edit the patch or the binding without updating src/fabric.ts together.
  • The file surface never touches anything outside the writable manifest: file-browser.ts enumerates, every write path goes through isWritableTarget + realpath boundary, and the file store adds byte caps and an expected-version guard. No file creation, deletion, or rename — ever.
  • The client composes only through standing seams: ctx.slots.inject('settings.section', ...) with declaration-aware deferral, ctx.locale.register, and the settingsPlusCatalog source seam. Section registration uses signature diffing (${key}\u0000${label}): unchanged signatures are reused, anything else is disposed and rebuilt. The catalog production default is the honest local empty source until the host wire surface lands.
  • The schema form's secret placeholder protocol is fixed: placeholders never travel, empty input keeps the stored value, only explicit input commits; per-field reset uses unset ops, never value-bearing writes. Revision conflicts surface a reload prompt (form.conflict).
  • Locale keys: src/client/locales.ts is the key-set source of truth; any new copy the client renders must be added to both dictionaries (the en side is compile-checked against zh). Chinese is the product copy; code comments, JSDoc, and the English README are English.

Check ladder

pnpm run typecheck and pnpm test must be green before every commit, then pnpm run build (which also regenerates lib/), and finally pnpm run verify:self-contained (rejects dependency specs, out-of-repo paths, external/broken Markdown links, absolute workstation paths, and malformed skill metadata). pnpm run prepare regenerates consumer artifacts for Git/tarball installs. Keep lib/ committed and in sync: any source change that alters built output lands with its lib/ regeneration in the same commit.

Commit discipline

  • Conventional commits (feat:/fix:/docs:/build:/chore:) with a body: what and why.
  • One concern per commit; run the full check ladder before committing; never commit node_modules/, logs, or credentials.