# AGENTS.md 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//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 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.