Parcourir la source

docs: fix NIH-audit review findings (Codex round 1)

- Drop the AGENTS.md budget bump: trim filler words in the layout map
  and command comments so the new convention line fits the existing
  1680 ceiling (1679/1680; master was 1680/1680)
- timers/promises note: 'Replace both' -> all three sites, and add
  pty-local to the acceptance criteria (EN+ZH)
- execa note: 17 value-taking options plus boolean flags, not 18 (EN+ZH)
- rejected roll-up: lsp-local src is ~1,800 lines, not 2,112 (EN+ZH)
- re-record the three touched i18n pairs
Tianyi Cui il y a 2 mois
Parent
commit
c9dc097749

+ 2 - 2
.agents/notes/proposed/simplification/2026-07-26-builtin-timer-promises-for-hand-rolled-sleeps.i18n.yaml

@@ -2,5 +2,5 @@
 # side as of the last confirmed-consistent state. Both languages carry equal authority;
 # side as of the last confirmed-consistent state. Both languages carry equal authority;
 # after editing either side, bring the other along and re-record with:
 # after editing either side, bring the other along and re-record with:
 #   pnpm run verify-translation-pairing --write
 #   pnpm run verify-translation-pairing --write
-2026-07-26-builtin-timer-promises-for-hand-rolled-sleeps.md: 036e2f2906ca99aaab30a2164649f9c750b4ad21
-2026-07-26-builtin-timer-promises-for-hand-rolled-sleeps.zh.md: 15a6f0dd412d142647df2722335a454a028cb798
+2026-07-26-builtin-timer-promises-for-hand-rolled-sleeps.md: 1a012aeabc7f9445127d6b8edcbe2f72e62f0eba
+2026-07-26-builtin-timer-promises-for-hand-rolled-sleeps.zh.md: 742d2c5ee8573c9b2bdf555c938c83dd6ea9f999

+ 3 - 3
.agents/notes/proposed/simplification/2026-07-26-builtin-timer-promises-for-hand-rolled-sleeps.md

@@ -14,7 +14,7 @@ Three packages hand-roll promise-wrapped timers that the `node:timers/promises`
 
 
 ## Proposal
 ## Proposal
 
 
-Replace both with `import { setTimeout } from 'node:timers/promises'`:
+Replace all three with `import { setTimeout } from 'node:timers/promises'`:
 
 
 - llm-retry: `try { await setTimeout(delayMs, undefined, { signal }); /* retry */ } catch { /* abort → fail */ }` — with a signal, the promise rejects only with the abort error, and a pre-aborted signal rejects immediately; behavior is identical, including timer clearing on abort. The empty `catch` names the abort rejection per the repo's empty-catch rule.
 - llm-retry: `try { await setTimeout(delayMs, undefined, { signal }); /* retry */ } catch { /* abort → fail */ }` — with a signal, the promise rejects only with the abort error, and a pre-aborted signal rejects immediately; behavior is identical, including timer clearing on abort. The empty `catch` names the abort rejection per the repo's empty-catch rule.
 - workflow-workerthread: `setTimeout(ms, undefined, { ref: false })` — exact semantics including not holding the event loop open.
 - workflow-workerthread: `setTimeout(ms, undefined, { ref: false })` — exact semantics including not holding the event loop open.
@@ -29,8 +29,8 @@ No dedicated tests pin the helpers themselves; the packages' behavior suites kee
 
 
 ## Acceptance criteria
 ## Acceptance criteria
 
 
-- Neither package defines a promise-wrapped `setTimeout` helper; both import from `node:timers/promises`.
-- `llm-retry` and `workflow-workerthread` test suites pass unchanged (behavioral parity).
+- None of the three packages defines a promise-wrapped `setTimeout` helper; all import from `node:timers/promises`.
+- The `llm-retry`, `workflow-workerthread`, and `pty-local` test suites pass unchanged (behavioral parity).
 
 
 ## Risks
 ## Risks
 
 

+ 3 - 3
.agents/notes/proposed/simplification/2026-07-26-builtin-timer-promises-for-hand-rolled-sleeps.zh.md

@@ -14,7 +14,7 @@ Status: proposed
 
 
 ## 提案
 ## 提案
 
 
-用 `import { setTimeout } from 'node:timers/promises'` 替换上述实现:
+用 `import { setTimeout } from 'node:timers/promises'` 替换这三处实现:
 
 
 - llm-retry:`try { await setTimeout(delayMs, undefined, { signal }); /* retry */ } catch { /* abort → fail */ }`。传入 signal 后,该 promise 只会以 abort 错误拒绝,已提前中止的 signal 则立即拒绝;行为完全一致,包括中止时清除定时器。按仓库的空 catch 规则,这个空 `catch` 注明其吞下的是 abort 拒绝。
 - llm-retry:`try { await setTimeout(delayMs, undefined, { signal }); /* retry */ } catch { /* abort → fail */ }`。传入 signal 后,该 promise 只会以 abort 错误拒绝,已提前中止的 signal 则立即拒绝;行为完全一致,包括中止时清除定时器。按仓库的空 catch 规则,这个空 `catch` 注明其吞下的是 abort 拒绝。
 - workflow-workerthread:`setTimeout(ms, undefined, { ref: false })`,语义完全等价,包括不会让事件循环保持存活。
 - workflow-workerthread:`setTimeout(ms, undefined, { ref: false })`,语义完全等价,包括不会让事件循环保持存活。
@@ -29,8 +29,8 @@ Status: proposed
 
 
 ## 验收标准
 ## 验收标准
 
 
-- 上述包不再各自定义 promise 包装的 `setTimeout` 辅助函数,而是都从 `node:timers/promises` 导入。
-- `llm-retry` 与 `workflow-workerthread` 的测试套件原样通过(行为等价)。
+- 这三个包都不再各自定义 promise 包装的 `setTimeout` 辅助函数,而是都从 `node:timers/promises` 导入。
+- `llm-retry`、`workflow-workerthread` 与 `pty-local` 的测试套件原样通过(行为等价)。
 
 
 ## 风险
 ## 风险
 
 

+ 2 - 2
.agents/notes/proposed/testing/2026-07-26-execa-for-test-subprocess-plumbing.i18n.yaml

@@ -2,5 +2,5 @@
 # side as of the last confirmed-consistent state. Both languages carry equal authority;
 # side as of the last confirmed-consistent state. Both languages carry equal authority;
 # after editing either side, bring the other along and re-record with:
 # after editing either side, bring the other along and re-record with:
 #   pnpm run verify-translation-pairing --write
 #   pnpm run verify-translation-pairing --write
-2026-07-26-execa-for-test-subprocess-plumbing.md: 3b2ba9062a72dfe03c9e9a84fa13fe23da39302a
-2026-07-26-execa-for-test-subprocess-plumbing.zh.md: 61e12233fc49ca7788882ca409d6f67f030d2475
+2026-07-26-execa-for-test-subprocess-plumbing.md: 99a86258fe4d59db6a0e144dbcee94c095f70f8f
+2026-07-26-execa-for-test-subprocess-plumbing.zh.md: 525e09f07ce3e5dc61f1cadab5c11ea0790cccee

+ 1 - 1
.agents/notes/proposed/testing/2026-07-26-execa-for-test-subprocess-plumbing.md

@@ -10,7 +10,7 @@ Roughly ten e2e/smoke files re-derive the same spawn-collect-timeout choreograph
 
 
 Two related test-infra hand-rolls compound the case:
 Two related test-infra hand-rolls compound the case:
 
 
-- `packages/support/llm-mock-server/src/cli.ts` hand-tokenizes 18 `--flag value` options (~45–60 lines of loop and value-extraction helpers) where the `node:util` `parseArgs` builtin is already the repo idiom (`cli-demo`, `acp-demo`, `verify-runtime-closure.ts`, `packages/sdk/scripts`).
+- `packages/support/llm-mock-server/src/cli.ts` hand-tokenizes 17 value-taking `--flag value` options plus boolean flags (~45–60 lines of loop and value-extraction helpers) where the `node:util` `parseArgs` builtin is already the repo idiom (`cli-demo`, `acp-demo`, `verify-runtime-closure.ts`, `packages/sdk/scripts`).
 - `apps/web/tests/smoke-real.e2e.ts` and `apps/web/tests/scaffold.ts` carry two verbatim copies of a regex `.env` parser (~20 lines) where the `process.loadEnvFile` builtin has exactly the required no-override semantics — and the vitest e2e/snapshot/web configs already load root `.env` with it before these files run, making the copies arguably dead.
 - `apps/web/tests/smoke-real.e2e.ts` and `apps/web/tests/scaffold.ts` carry two verbatim copies of a regex `.env` parser (~20 lines) where the `process.loadEnvFile` builtin has exactly the required no-override semantics — and the vitest e2e/snapshot/web configs already load root `.env` with it before these files run, making the copies arguably dead.
 - The snapshot harness hand-rolls three poll-until-deadline loops (`waitForPersistedTurnStart`/`waitForPersistedTurnEnd`/`waitForWorkspaceFile` in `packages/support/acp-snapshot/src/harness.ts`, ~55 lines) plus `waitForFile` in `crash-recovery.e2e.ts`, where `vi.waitFor`/`expect.poll` cover the shape — vitest is already a runtime dependency of `dsh-acp-snapshot`, so this adds nothing.
 - The snapshot harness hand-rolls three poll-until-deadline loops (`waitForPersistedTurnStart`/`waitForPersistedTurnEnd`/`waitForWorkspaceFile` in `packages/support/acp-snapshot/src/harness.ts`, ~55 lines) plus `waitForFile` in `crash-recovery.e2e.ts`, where `vi.waitFor`/`expect.poll` cover the shape — vitest is already a runtime dependency of `dsh-acp-snapshot`, so this adds nothing.
 
 

+ 1 - 1
.agents/notes/proposed/testing/2026-07-26-execa-for-test-subprocess-plumbing.zh.md

@@ -10,7 +10,7 @@ Status: proposed
 
 
 另有两处相关的测试基础设施手写代码进一步强化了替换的理由:
 另有两处相关的测试基础设施手写代码进一步强化了替换的理由:
 
 
-- `packages/support/llm-mock-server/src/cli.ts` 手工逐个切分 18 个 `--flag value` 选项(约 45–60 行的循环与取值辅助函数),而 `node:util` 内置的 `parseArgs` 早已是本仓库的惯用写法(`cli-demo`、`acp-demo`、`verify-runtime-closure.ts`、`packages/sdk/scripts`)。
+- `packages/support/llm-mock-server/src/cli.ts` 手工逐个切分 17 个带值的 `--flag value` 选项外加若干布尔标志(约 45–60 行的循环与取值辅助函数),而 `node:util` 内置的 `parseArgs` 早已是本仓库的惯用写法(`cli-demo`、`acp-demo`、`verify-runtime-closure.ts`、`packages/sdk/scripts`)。
 - `apps/web/tests/smoke-real.e2e.ts` 与 `apps/web/tests/scaffold.ts` 携带两份逐字相同的正则 `.env` 解析器拷贝(约 20 行),而内置的 `process.loadEnvFile` 恰好具备所需的「不覆盖已有值」语义;并且 vitest 的 e2e/snapshot/web 配置在这些文件运行之前就已用它加载了根 `.env`,这两份拷贝几乎可以视为死代码。
 - `apps/web/tests/smoke-real.e2e.ts` 与 `apps/web/tests/scaffold.ts` 携带两份逐字相同的正则 `.env` 解析器拷贝(约 20 行),而内置的 `process.loadEnvFile` 恰好具备所需的「不覆盖已有值」语义;并且 vitest 的 e2e/snapshot/web 配置在这些文件运行之前就已用它加载了根 `.env`,这两份拷贝几乎可以视为死代码。
 - 快照 harness 手写了三个「轮询直到截止时间」的循环(`packages/support/acp-snapshot/src/harness.ts` 中的 `waitForPersistedTurnStart`/`waitForPersistedTurnEnd`/`waitForWorkspaceFile`,约 55 行),外加 `crash-recovery.e2e.ts` 中的 `waitForFile`,而 `vi.waitFor`/`expect.poll` 正好覆盖这种形态;vitest 本来就是 `dsh-acp-snapshot` 的运行时依赖,因此这不新增任何东西。
 - 快照 harness 手写了三个「轮询直到截止时间」的循环(`packages/support/acp-snapshot/src/harness.ts` 中的 `waitForPersistedTurnStart`/`waitForPersistedTurnEnd`/`waitForWorkspaceFile`,约 55 行),外加 `crash-recovery.e2e.ts` 中的 `waitForFile`,而 `vi.waitFor`/`expect.poll` 正好覆盖这种形态;vitest 本来就是 `dsh-acp-snapshot` 的运行时依赖,因此这不新增任何东西。
 
 

+ 2 - 2
.agents/notes/rejected/simplification/2026-07-26-dependency-swaps-rejected-by-nih-audit.i18n.yaml

@@ -2,5 +2,5 @@
 # side as of the last confirmed-consistent state. Both languages carry equal authority;
 # side as of the last confirmed-consistent state. Both languages carry equal authority;
 # after editing either side, bring the other along and re-record with:
 # after editing either side, bring the other along and re-record with:
 #   pnpm run verify-translation-pairing --write
 #   pnpm run verify-translation-pairing --write
-2026-07-26-dependency-swaps-rejected-by-nih-audit.md: 6ee4ce36bcc25b206eebedd18270021e4937761f
-2026-07-26-dependency-swaps-rejected-by-nih-audit.zh.md: b983dfcfa12171bfe1ae9bc79936d3a5876e5e68
+2026-07-26-dependency-swaps-rejected-by-nih-audit.md: c988ca0c75e9c50686551f3be1971d736b971e2a
+2026-07-26-dependency-swaps-rejected-by-nih-audit.zh.md: e85161cb2ee616d388aa2a9dd065c315c60cd44a

+ 1 - 1
.agents/notes/rejected/simplification/2026-07-26-dependency-swaps-rejected-by-nih-audit.md

@@ -14,7 +14,7 @@ Adopt the following dependency swaps. Rejected — per-item evidence below; a fu
 
 
 **Protocol and parsing:**
 **Protocol and parsing:**
 
 
-- **`vscode-jsonrpc` for LSP base-protocol framing/correlation** (`lsp-local`): the swappable core is ~255 of 2,112 src lines; the package cannot express the configured `maxMessageBytes` incoming-size bound (restoring it means rebuilding the deleted framing), inverts the cancel-grace teardown semantics (`raceAbort` rejects immediately then tears down; vscode-jsonrpc keeps the promise pending), errors on pre-header stdout banners real servers emit, and is CJS in an ESM-everywhere repo. The [LSP seam note](../../implemented/architecture/2026-07-15-lsp-capability-seam.md) assigns JSON-RPC ownership to `dsh-lsp-local`; this audit is the explicit on-record weighing of the dependency it lacked.
+- **`vscode-jsonrpc` for LSP base-protocol framing/correlation** (`lsp-local`): the swappable core is ~255 of ~1,800 src lines; the package cannot express the configured `maxMessageBytes` incoming-size bound (restoring it means rebuilding the deleted framing), inverts the cancel-grace teardown semantics (`raceAbort` rejects immediately then tears down; vscode-jsonrpc keeps the promise pending), errors on pre-header stdout banners real servers emit, and is CJS in an ESM-everywhere repo. The [LSP seam note](../../implemented/architecture/2026-07-15-lsp-capability-seam.md) assigns JSON-RPC ownership to `dsh-lsp-local`; this audit is the explicit on-record weighing of the dependency it lacked.
 - **`vscode-languageserver-types` for lsp-local's wire-type subset**: ~80 type lines and ~45 guard lines, but upstream guards differ in both directions (accept `uri: undefined` the repo must reject; require `targetRange` the repo tolerates absent), and the initialize-result shapes live in `vscode-languageserver-protocol`, dragging `vscode-jsonrpc` in as a runtime dep — ~1 MB for 80 spec-exact lines.
 - **`vscode-languageserver-types` for lsp-local's wire-type subset**: ~80 type lines and ~45 guard lines, but upstream guards differ in both directions (accept `uri: undefined` the repo must reject; require `targetRange` the repo tolerates absent), and the initialize-result shapes live in `vscode-languageserver-protocol`, dragging `vscode-jsonrpc` in as a runtime dep — ~1 MB for 80 spec-exact lines.
 - **`json-rpc-2.0` for `dsh-jsonrpc`**: deletable correlation/dispatch is real (~100–130 lines) but the NDJSON wire must stay bit-identical for the hand-rolled Python SDK client, the package is single-maintainer, and the [GUI RPC note](../../implemented/architecture/2026-07-19-gui-layering-and-rpc-protocol.md) already treats this package as a frozen narrow surface. `vscode-jsonrpc` is a worse fit still (Content-Length framing, cancellation vocabulary the protocol lacks).
 - **`json-rpc-2.0` for `dsh-jsonrpc`**: deletable correlation/dispatch is real (~100–130 lines) but the NDJSON wire must stay bit-identical for the hand-rolled Python SDK client, the package is single-maintainer, and the [GUI RPC note](../../implemented/architecture/2026-07-19-gui-layering-and-rpc-protocol.md) already treats this package as a frozen narrow surface. `vscode-jsonrpc` is a worse fit still (Content-Length framing, cancellation vocabulary the protocol lacks).
 - **`jsonrpcclient` for the Python SDK client**: v4 builds/parses messages only — ~20 lines — while the 500 lines that matter (subprocess lifecycle, threaded reader, id correlation, bidirectional server-role responses) stay; the library is in low-maintenance mode.
 - **`jsonrpcclient` for the Python SDK client**: v4 builds/parses messages only — ~20 lines — while the 500 lines that matter (subprocess lifecycle, threaded reader, id correlation, bidirectional server-role responses) stay; the library is in low-maintenance mode.

+ 1 - 1
.agents/notes/rejected/simplification/2026-07-26-dependency-swaps-rejected-by-nih-audit.zh.md

@@ -14,7 +14,7 @@ Status: rejected — 下列每一项替换在证据上都未达到净简化门
 
 
 **协议与解析:**
 **协议与解析:**
 
 
-- **以 `vscode-jsonrpc` 承担 LSP 基础协议的分帧/关联**(`lsp-local`):可替换的核心只占 src 全部 2,112 行中的约 255 行;该包无法表达可配置的 `maxMessageBytes` 入站大小上限(要恢复它就得重建被删掉的分帧代码),反转了取消宽限期的拆除语义(`raceAbort` 立即 reject 再拆除;vscode-jsonrpc 让 promise 保持挂起),会在真实服务器输出的 header 前 stdout 横幅上报错,而且在这个 ESM 通行的仓库里它是 CJS。[LSP seam 决策](../../implemented/architecture/2026-07-15-lsp-capability-seam.md)把 JSON-RPC 的所有权划给 `dsh-lsp-local`;本次审计正是对该决策当时缺失的这项依赖权衡的明文记录。
+- **以 `vscode-jsonrpc` 承担 LSP 基础协议的分帧/关联**(`lsp-local`):可替换的核心只占 src 约 1,800 行中的约 255 行;该包无法表达可配置的 `maxMessageBytes` 入站大小上限(要恢复它就得重建被删掉的分帧代码),反转了取消宽限期的拆除语义(`raceAbort` 立即 reject 再拆除;vscode-jsonrpc 让 promise 保持挂起),会在真实服务器输出的 header 前 stdout 横幅上报错,而且在这个 ESM 通行的仓库里它是 CJS。[LSP seam 决策](../../implemented/architecture/2026-07-15-lsp-capability-seam.md)把 JSON-RPC 的所有权划给 `dsh-lsp-local`;本次审计正是对该决策当时缺失的这项依赖权衡的明文记录。
 - **以 `vscode-languageserver-types` 承担 lsp-local 的协议类型子集**:约 80 行类型加约 45 行守卫,但上游守卫在两个方向上都与本仓库不一致(接受本仓库必须拒绝的 `uri: undefined`;强制要求本仓库容忍缺失的 `targetRange`),而且 initialize 结果的形状住在 `vscode-languageserver-protocol` 里,会把 `vscode-jsonrpc` 拖成运行时依赖——为 80 行严格贴合规范的代码付出约 1 MB。
 - **以 `vscode-languageserver-types` 承担 lsp-local 的协议类型子集**:约 80 行类型加约 45 行守卫,但上游守卫在两个方向上都与本仓库不一致(接受本仓库必须拒绝的 `uri: undefined`;强制要求本仓库容忍缺失的 `targetRange`),而且 initialize 结果的形状住在 `vscode-languageserver-protocol` 里,会把 `vscode-jsonrpc` 拖成运行时依赖——为 80 行严格贴合规范的代码付出约 1 MB。
 - **以 `json-rpc-2.0` 替换 `dsh-jsonrpc`**:可删除的关联/分发代码确实存在(约 100–130 行),但 NDJSON 协议格式(wire format)必须与手写的 Python SDK 客户端逐位一致,该包只有单一维护者,且 [GUI RPC 决策](../../implemented/architecture/2026-07-19-gui-layering-and-rpc-protocol.md)已把这个包当作冻结的窄接口面对待。`vscode-jsonrpc` 更不合适(Content-Length 分帧、该协议并不具备的取消词汇)。
 - **以 `json-rpc-2.0` 替换 `dsh-jsonrpc`**:可删除的关联/分发代码确实存在(约 100–130 行),但 NDJSON 协议格式(wire format)必须与手写的 Python SDK 客户端逐位一致,该包只有单一维护者,且 [GUI RPC 决策](../../implemented/architecture/2026-07-19-gui-layering-and-rpc-protocol.md)已把这个包当作冻结的窄接口面对待。`vscode-jsonrpc` 更不合适(Content-Length 分帧、该协议并不具备的取消词汇)。
 - **以 `jsonrpcclient` 承担 Python SDK 客户端**:v4 只做消息的构造/解析——约 20 行——而真正要紧的 500 行(子进程生命周期、线程化读取器、id 关联、双向的服务端角色应答)全都保留;该库处于低维护模式。
 - **以 `jsonrpcclient` 承担 Python SDK 客户端**:v4 只做消息的构造/解析——约 20 行——而真正要紧的 500 行(子进程生命周期、线程化读取器、id 关联、双向的服务端角色应答)全都保留;该库处于低维护模式。

+ 9 - 9
AGENTS.md

@@ -12,7 +12,7 @@ DeepSeek Harness SDK is a plugin-based agent harness on vendored Cordis: **every
 vendor/      Vendored Cordis source — manifest + sync procedure in vendor/README.md
 vendor/      Vendored Cordis source — manifest + sync procedure in vendor/README.md
 packages/    @deepseek-ai/dsh-<pkg> workspaces at packages/<group>/<pkg>/
 packages/    @deepseek-ai/dsh-<pkg> workspaces at packages/<group>/<pkg>/
   core/        product API spine: session, system-prompt, tools, agent, agent-loop
   core/        product API spine: session, system-prompt, tools, agent, agent-loop
-  llm/         LLM seam + the DeepSeek adapters (hand-rolled + pi-ai design twin)
+  llm/         LLM seam + DeepSeek adapters (hand-rolled + pi-ai design twin)
   bash/        bash executor seam + local impl + model-facing bash tools
   bash/        bash executor seam + local impl + model-facing bash tools
   pty/         persistent PTY seam/backend/tools
   pty/         persistent PTY seam/backend/tools
   fs/          filesystem seam + local impl + policy gate + read/write/edit tools
   fs/          filesystem seam + local impl + policy gate + read/write/edit tools
@@ -22,17 +22,17 @@ packages/    @deepseek-ai/dsh-<pkg> workspaces at packages/<group>/<pkg>/
   compact/     compaction seam + basic backend
   compact/     compaction seam + basic backend
   context/     request-context plugins
   context/     request-context plugins
   subagent/    subagent seam + spawn/fork/ACP backends + delegation tool
   subagent/    subagent seam + spawn/fork/ACP backends + delegation tool
-  workflow/    workflow seam + worker-thread engine + the workflow tool
-  todo/        the todo_write tool
+  workflow/    workflow seam + worker-thread engine + workflow tool
+  todo/        todo_write tool
   plan/        plan mode as logged per-agent collaboration state
   plan/        plan mode as logged per-agent collaboration state
   guard/       loop-hygiene plugins
   guard/       loop-hygiene plugins
   cordis/      self-referential toolset: the agent inspects/mounts plugins in its own runtime
   cordis/      self-referential toolset: the agent inspects/mounts plugins in its own runtime
-  hooks/       Claude Code / Codex hook bridges + shared wire-protocol library
+  hooks/       Claude Code/Codex hook bridges + shared wire-protocol library
   session-persistence/  persistence seam + JSONL/SQLite backends
   session-persistence/  persistence seam + JSONL/SQLite backends
   acp/         automation-only Agent Client Protocol server
   acp/         automation-only Agent Client Protocol server
   ui/          TUI/JSON-RPC bridges; boot, approval, interaction plugins
   ui/          TUI/JSON-RPC bridges; boot, approval, interaction plugins
   examples/    demo bundles (agent-spine + TUI/CLI/ACP/JSON-RPC bins) leaves load
   examples/    demo bundles (agent-spine + TUI/CLI/ACP/JSON-RPC bins) leaves load
-  support/     dev/test infrastructure packages
+  support/     dev/test infrastructure
   util/        zero-dependency utilities
   util/        zero-dependency utilities
 python/      Python SDK and bundled runtime (see python/README.md)
 python/      Python SDK and bundled runtime (see python/README.md)
 native/      node-addon-landlock-run source of record (see native/README.md)
 native/      node-addon-landlock-run source of record (see native/README.md)
@@ -60,11 +60,11 @@ pnpm run lint
 pnpm run duplication    # cross-file TypeScript clone detection
 pnpm run duplication    # cross-file TypeScript clone detection
 pnpm run build          # tsc emits lib/types, tsdown bundles runtime
 pnpm run build          # tsc emits lib/types, tsdown bundles runtime
 pnpm run hygiene        # knip + publint + workspace constraints + NodeNext consumer check
 pnpm run hygiene        # knip + publint + workspace constraints + NodeNext consumer check
-pnpm run doc-sync       # all documentation gates; see the doc-sync leaf list in scripts/run-gates.ts
-pnpm run website:build  # VitePress build (doubles as the site's dead-link check)
+pnpm run doc-sync       # all documentation gates; leaf list in scripts/run-gates.ts
+pnpm run website:build  # VitePress build (doubles as dead-link check)
 pnpm run demo:headless "task" # one-shot agent (needs DEEPSEEK_API_KEY)
 pnpm run demo:headless "task" # one-shot agent (needs DEEPSEEK_API_KEY)
 pnpm run demo:tui       # full-screen TUI coding agent (needs DEEPSEEK_API_KEY)
 pnpm run demo:tui       # full-screen TUI coding agent (needs DEEPSEEK_API_KEY)
-pnpm run demo:cordis    # self-referential demo: the agent modifies its own runtime (needs key)
+pnpm run demo:cordis    # the agent modifies its own runtime (needs key)
 pnpm run demo:acp       # ACP automation server (needs DEEPSEEK_API_KEY)
 pnpm run demo:acp       # ACP automation server (needs DEEPSEEK_API_KEY)
 ```
 ```
 
 
@@ -96,7 +96,7 @@ Real-API tests and demos read `DEEPSEEK_API_KEY`, optional `DEEPSEEK_BASE_URL`,
 - **Model-visible ⟺ logged**: anything that reaches a model request must be reconstructable from the session log; a new model-visible input requires a session event.
 - **Model-visible ⟺ logged**: anything that reaches a model request must be reconstructable from the session log; a new model-visible input requires a session event.
 - **Plugins, not loop changes**: new behavior goes on the documented extension seams; changing `agent-loop` requires updating docs/architecture.md.
 - **Plugins, not loop changes**: new behavior goes on the documented extension seams; changing `agent-loop` requires updating docs/architecture.md.
 - **Capability seams are three packages** — interface / implementation / consumer; don't split preemptively.
 - **Capability seams are three packages** — interface / implementation / consumer; don't split preemptively.
-- **Prefer maintained dependencies over hand-rolling** when the swap genuinely deletes owned code and tests ([policy](.agents/notes/implemented/process/2026-07-26-dependencies-over-hand-rolling.md)).
+- **Prefer maintained dependencies over hand-rolling** when they genuinely delete owned code and tests ([policy](.agents/notes/implemented/process/2026-07-26-dependencies-over-hand-rolling.md)).
 - **Explicit > implicit at package seams**: defaulting is an explicit `resolve(request): Spec` step in the owning implementation, never a hidden `?? default` inside `run()` (the `dsh-bash` request/spec split is the template).
 - **Explicit > implicit at package seams**: defaulting is an explicit `resolve(request): Spec` step in the owning implementation, never a hidden `?? default` inside `run()` (the `dsh-bash` request/spec split is the template).
 - **No hardcoded tunables in plugins**: deployment-varying choices are validated `Config` fields changeable from cordis.yml; a `DEFAULT_*` constant or test seam is not configurability. Protocol constants, external specs, and security invariants stay fixed.
 - **No hardcoded tunables in plugins**: deployment-varying choices are validated `Config` fields changeable from cordis.yml; a `DEFAULT_*` constant or test seam is not configurability. Protocol constants, external specs, and security invariants stay fixed.
 - **Misconfiguration fails loud** at load when self-contained, otherwise at the earliest resolvable point; never silently skip a missing referent.
 - **Misconfiguration fails loud** at load when self-contained, otherwise at the earliest resolvable point; never silently skip a missing referent.

+ 1 - 1
scripts/doc-budgets.manifest.json

@@ -1,5 +1,5 @@
 {
 {
-  "AGENTS.md": 1700,
+  "AGENTS.md": 1680,
   "docs/AGENTS.md": 1150,
   "docs/AGENTS.md": 1150,
   "docs/architecture.md": 1800,
   "docs/architecture.md": 1800,
   "docs/cordis-primer.md": 600,
   "docs/cordis-primer.md": 600,