docs(i18n): refresh Agent Note translations after merge

This commit is contained in:
Tianyi Cui
2026-07-23 00:10:53 +08:00
parent cd17f8d23a
commit 744d65d63c
325 changed files with 1535 additions and 1321 deletions

View File

@@ -2,5 +2,5 @@
# 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:
# pnpm run verify-translation-pairing --write
2026-06-16-typed-event-schemas.md: 93e470218e810c9c9370dd1c7cae5420c93fa7bf
2026-06-16-typed-event-schemas.zh.md: bca4265527507750abe5b8c114f14508cee91cb9
2026-06-16-typed-event-schemas.md: 97a7d0c3787eb5556696e25a6c8b1bb75642aba9
2026-06-16-typed-event-schemas.zh.md: f79a607eaa723c00b00e984e1a6983d24d99c127

View File

@@ -1,4 +1,4 @@
# RFC: 事件词汇的运行时 schema(Zod 与 merge-extensible-map 模式之辩)
# Agent Note: 事件词汇的运行时 schema(Zod 与 merge-extensible-map 模式之辩)
Status: proposed
@@ -6,7 +6,7 @@ Status: proposed
## 问题
harness 将其核心词汇——内容块、消息来源、结束原因、轮次触发器、轮次结束原因与会话事件——建模为 **merge-extensible map**:一个 TypeScript `interface`(如 `SessionEventMap`、`ContentBlockMap`),插件通过声明合并对其扩展,公开联合类型则以 `Map[keyof Map]` 派生。这是本仓库的通用扩展模式,记录在 [docs/architecture.md](../../../architecture.md) 中("The same merge-extensible-map pattern is used for `MessageSource`, `FinishReason`, `TurnTrigger`, and `TurnEndReason`"),`defineTool` 的 `InferArgs` DSL 和 `assertNever` 穷举约定都依赖于它。
harness 将其核心词汇——内容块、消息来源、结束原因、轮次触发器、轮次结束原因与会话事件——建模为 **merge-extensible map**:一个 TypeScript `interface`(如 `SessionEventMap`、`ContentBlockMap`),插件通过声明合并对其扩展,公开联合类型则以 `Map[keyof Map]` 派生。这是本仓库的通用扩展模式,记录在 [docs/architecture.md](../../../../docs/architecture.md) 中(「The same merge-extensible-map pattern is used for `MessageSource`, `FinishReason`, `TurnTrigger`, and `TurnEndReason`」),`defineTool` 的 `InferArgs` DSL 和 `assertNever` 穷举约定都依赖于它。
该模式**仅存在于编译期**。类型在运行时消失:没有 schema 对象可供校验传入值、解析不可信输入或在运行时枚举变体。[会话持久化契约](../../implemented/architecture/2026-06-14-session-persistence.md)暴露了两个后果:
@@ -15,7 +15,7 @@ harness 将其核心词汇——内容块、消息来源、结束原因、轮次
由此引出问题:事件词汇是否应迁移到 **Zod** 或其他运行时 schema 库,使持久化和插件边界拥有运行时 schema 而非被擦除的类型。
本 RFC 界定该问题的范围,不提出具体实现。
本 Agent Note 界定该问题的范围,不提出具体实现。
## 为什么这不是一个持久化层的改动
@@ -30,16 +30,16 @@ harness 将其核心词汇——内容块、消息来源、结束原因、轮次
- **六个 merge-extensible map**(约 370 行核心类型):`ContentBlockMap`、`MessageSourceMap`、`FinishReasonMap`(位于 `dsh-llm`);`TurnTriggerMap`、`TurnEndReasonMap`、`SessionEventMap`(位于 `dsh-session`)。
- **约 10 处 `declare module` 扩展点**,分布在 `dsh-agent`、`dsh-agent-loop`、`dsh-bash`、`dsh-llm`、`dsh-session`、`dsh-session-persistence`、`dsh-system-prompt`、`dsh-tools` 各包中——每处都将从声明合并改为运行时 `register()` 调用。
- **事件生产者**——agent loop(智能体循环)中 16 处 `session.append(...)` 调用——形状不变,但现在在边界处被校验。
- **约 7 个 switch 消费方**,对这些联合类型进行分支:`deriveMessages`(`dsh-session`)、`BlockAssembler`(`dsh-llm`)、`dsh-invariants` 插件、两个 LLM(大语言模型)适配器(`dsh-llm-deepseek`、`dsh-llm-pi-ai`)以及工具 schema 层(`dsh-tools`)。`assertNever` 对封闭联合类型的穷举 vs 对可扩展联合类型的 fall-through 约定(一条已记录的 lint 规则)需要重新考量——运行时变体在静态层面不可穷举。
- **约 7 个 switch 消费方**,对这些联合类型进行分支:`deriveMessages` 与包自有的不变式 companion(`dsh-session`)、`BlockAssembler`(`dsh-llm`)、两个 LLM(大语言模型)适配器(`dsh-llm-deepseek`、`dsh-llm-pi-ai`)以及工具 schema 层(`dsh-tools`)。`assertNever` 对封闭联合类型的穷举 vs 对可扩展联合类型的 fall-through 约定(一条已记录的 lint 规则)需要重新考量——运行时变体在静态层面不可穷举。
- **`defineTool` 的 `InferArgs` DSL**(`dsh-tools`),它从编译期 schema 规范派生出零类型转换的 `execute` 参数类型——这是当前方案的标杆用例。
- **文档**:architecture.md(该模式被描述为基础性的)、[dev-mode invariants](../../implemented/architecture/2026-06-11-dev-invariants-over-deep-readonly.md),以及所有引用该模式的 RFC。
- **文档**:architecture.md(该模式被描述为基础性的)、[dev-mode invariants](../../implemented/architecture/2026-06-11-dev-invariants-over-deep-readonly.md),以及所有引用该模式的 Agent Note。
这是一次仓库级别的词汇重新设计,而非持久化的实现细节。
## 曾考虑的替代方案
### A. 维持现状——merge-extensible 类型 + 持久化边界处 `isJsonValue`
保留编译期模式。持久化继续使用不透明 JSON + 可序列化性守卫。插件通过声明合并扩展;事件*形状*的正确性由生产者负责,编译期由 TypeScript 保证,开发模式下由 `dsh-invariants` 插件的结构检查保证。
保留编译期模式。持久化继续使用不透明 JSON + 可序列化性守卫。插件通过声明合并扩展;事件*形状*的正确性由生产者负责,并由 TypeScript 在编译期保证。启用包自有的不变式 companion 后,它们会检查选定的跨记录关系,但不提供通用运行时形状 schema。
- **优点**:零变动;插件扩展只需一行 `interface` 增补,享有完整类型推断,无需运行时注册仪式;无新运行时依赖;`defineTool` DSL 与 `assertNever` 穷举继续工作。
- **缺点**:持久化边界和插件 seam 处无运行时结构校验;格式错误但仍为合法 JSON 的数据被延迟捕获。
@@ -58,11 +58,11 @@ harness 将其核心词汇——内容块、消息来源、结束原因、轮次
## 提案
推迟。如果需要在持久化边界做运行时校验,**方案 B**(对封闭的头部和元数据形状使用 schemastery)是现有约定下的适度步骤。**方案 C** 是一个架构决策,需要自己的实现 RFC,其中包括 Zod 与 schemastery 之间的选择。
推迟。如果需要在持久化边界做运行时校验,**方案 B**(对封闭的头部和元数据形状使用 schemastery)是现有约定下的适度步骤。**方案 C** 是一个架构决策,需要自己的实现 Agent Note,其中包括 Zod 与 schemastery 之间的选择。
## 验收标准
- 方案 C 只能通过自己的实现 RFC 推进,绝不能作为持久化的附带改动。
- 方案 C 只能通过自己的实现 Agent Note 推进,绝不能作为持久化的附带改动。
- 如果采纳方案 B,封闭的头部/元数据形状(JSONL 的 `isHeaderLine` 守卫及同类)改用 schemastery 校验,替代手写守卫,merge-extensible map 保持不动。
## 风险
@@ -74,4 +74,4 @@ harness 将其核心词汇——内容块、消息来源、结束原因、轮次
- 如果采用注册表,库选 **schemastery**(已在仓库中,已作为配置 schema 库)还是 **Zod**(生态更丰富,目前仅为传递依赖)?同时维护两个 schema 库本身就是一种成本。
- 能否采用混合方案:保留编译期推断(使 `defineTool` 和插件开发体验不受影响),同时为每个变体添加*可选*的运行时 schema,仅在持久化/协议边界校验,而非每次进程内 append 都校验?
- `dsh-invariants` 插件在开发模式下是否已覆盖了足够多的运行时形状缺口,使得边界校验仅在面对真正不可信输入(重新加载外部修改过的日志)时才有必要?
- `ctx.invariants` 服务启用后是否已覆盖了足够多的运行时形状缺口,使得边界校验仅在面对真正不可信输入(重新加载外部修改过的日志)时才有必要?

View File

@@ -2,5 +2,5 @@
# 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:
# pnpm run verify-translation-pairing --write
2026-06-30-pre-tool-input-rewrite.md: 85ece78f3bf188b3b702b1af539256747c1cfab2
2026-06-30-pre-tool-input-rewrite.zh.md: 6a5c2b52627d21476b96448dc120155eab7f2223
2026-06-30-pre-tool-input-rewrite.md: 5de605e7edf63046b14a125d53072aab9bb5d37e
2026-06-30-pre-tool-input-rewrite.zh.md: 7105fadd5b42c73b0493443935a9db1e37e525ab

View File

@@ -1,4 +1,4 @@
# RFC: 工具执行前输入重写——一致性设计
# Agent Note: 工具执行前输入重写——一致性设计
Status: proposed
@@ -6,7 +6,7 @@ Status: proposed
## 问题
[拦截 seam RFC](../../implemented/feature/2026-06-30-interception-seams.md) 将 `tools/pre-execute` 定义为一道针对执行的允许/拒绝/询问门禁,此时执行的身份标识已受保护、参数已被深度冻结。Claude Code 的 `PreToolUse` 钩子还提供了 `updatedInput`,因此忠实的桥接需要一个显式的重写机制。重写不能是对现有执行对象的可变逃逸口:它必须保持持久化历史、审计记录、展示层与实际执行值之间的一致性。
[拦截 seam Agent Note](../../implemented/feature/2026-06-30-interception-seams.md) 将 `tools/pre-execute` 定义为一道针对执行的允许/拒绝/询问门禁,此时执行的身份标识已受保护、参数已被深度冻结。Claude Code 的 `PreToolUse` 钩子还提供了 `updatedInput`,因此忠实的桥接需要一个显式的重写机制。重写不能是对现有执行对象的可变逃逸口:它必须保持持久化历史、审计记录、展示层与实际执行值之间的一致性。
## 问题本质:执行前参数的三个读取方

View File

@@ -2,5 +2,5 @@
# 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:
# pnpm run verify-translation-pairing --write
2026-07-07-claude-code-and-codex-subagent-backends.md: 5585ea30a5ba1b4200f096069a28ccf1c3cef727
2026-07-07-claude-code-and-codex-subagent-backends.zh.md: 2a6dd2cdea34cb777df5455f0bc12dc7073ff2a2
2026-07-07-claude-code-and-codex-subagent-backends.md: ee8576f97a9fdef8c88dcad3a73f28b63ca3ebe1
2026-07-07-claude-code-and-codex-subagent-backends.zh.md: 893d6a3cbac5293d6fdf2d6fa4d7ad00f856c61d

View File

@@ -1,4 +1,4 @@
# RFC: Claude Code 与 Codex subagent 后端(向外部编码 agent 的进程外委派)
# Agent Note: Claude Code 与 Codex subagent 后端(向外部编码 agent 的进程外委派)
Status: proposed
@@ -6,23 +6,23 @@ Status: proposed
## 问题
为 Claude Code 和 Codex 添加隔离的 subagent 提供方。既有的[命名提供方 seam](../../implemented/feature/2026-06-21-subagent-capability-seam.md) 和 [ACP 后端](../../implemented/feature/2026-06-22-acp-subagent-backend.md)已确立了进程边界的形状。harness 的一个轮次应能将一个自包含任务委派给上述任一产品,并接收其最终答案,同时不暴露父进程的密钥,也不继承来自 `~/.claude` 或 `~/.codex` 的宿主配置。
subagent seam([seam Agent Note](../../implemented/feature/2026-06-21-subagent-capability-seam.md))在 `ctx.subagents` 上托管多个命名提供方,ACP 后端([ACP 后端 Agent Note](../../implemented/feature/2026-06-22-acp-subagent-backend.md))证明了该 seam 能跨越进程边界泛化;其「未来提供方」一节明确将 Codex app-server 与 Claude Code Agent SDK 列为机械上相似的兄弟。如今真正值得委派的就是这两个引擎:harness 的一个轮次应能把一个自包含任务交给真实的 Claude Code 或真实的 Codex——一个拥有自身模型、工具与沙箱的独立产品——并取回一个最终答案,同时父部署不向子进程泄漏密钥,子进程行为也不静默依赖宿主机上碰巧存在的 `~/.claude` / `~/.codex` 状态。
## 提案
两个兄弟提供方包(ACP 后端的结构变体),加一次提取:
- `@deepseek-ai/dsh-subagent-claude-code`:通过 `@anthropic-ai/claude-agent-sdk` 的 `query()` 驱动一个 Claude Code 子进程(SDK 在父进程中运行,并将其内置的 `claude` CLI 作为子进程 spawn)。提供方名称为 `claude-code`:子进程是 Claude Code 这个*产品*,而非 Anthropic 模型适配器——"claude" 保留给未来的 `dsh-llm` 适配器。
- `@deepseek-ai/dsh-subagent-claude-code`:通过 `@anthropic-ai/claude-agent-sdk` 的 `query()` 驱动一个 Claude Code 子进程(SDK 在父进程中运行,并将其内置的 `claude` CLI 作为子进程 spawn)。提供方名称为 `claude-code`:子进程是 Claude Code 这个*产品*,而非 Anthropic 模型适配器——「claude」保留给未来的 `dsh-llm` 适配器。
- `@deepseek-ai/dsh-subagent-codex`:spawn `codex app-server`,通过其 JSON-RPC-over-stdio 协议驱动一个 thread/turn,使用包内一个手写的换行 JSON 客户端(约 200–300 行)。
- `@deepseek-ai/dsh-subagent-process`:纯库(沿用 `subagent-inprocess` 的先例),提取 `dsh-subagent-acp` 已有且两个新后端都需要的内容:凭证环境清洗(`SENSITIVE_ENV_PATTERN`/`buildChildEnv`)、EOF → SIGTERM → SIGKILL 的 dispose 阶梯,以及新的隔离配置目录辅助函数(`mkdtemp` 创建、尽力删除)。ACP 后端迁移到该库上;`bash-local` 的兄弟副本保持不动以限制变更范围。
- `@deepseek-ai/dsh-subagent-process`:纯库(沿用 `subagent-inprocess` 的先例),提取 `dsh-subagent-acp` 已有且两个新后端都需要的内容:凭证环境清洗(`buildChildEnv`)、EOF → SIGTERM → SIGKILL 的 dispose 阶梯,以及新的隔离配置目录辅助函数(`mkdtemp` 创建、尽力删除)。ACP 后端迁移到该库上;`bash-local` 的兄弟副本保持不动以限制变更范围。
两个提供方遵循 ACP 后端契约:每次 `start` 创建一个全新子进程、一次 prompt 往返、不继承父上下文也不声明可选能力、忽略 `request.parent` 和 `request.agentOptions`、使用随机的品牌化 agent id。`result` 从不 reject;子进程失败映射为 stop reason,原始错误送入 logger。每个提供方以不同的工具名挂载 `dsh-tool-subagent`。工具结果是唯一新增的模型可见产物,因此无需新的会话事件;工作区变更仍是 transcript(文本记录)回放之外的环境副作用。
两个提供方逐字复制 ACP 后端的 seam 姿态:每次 `start` 创建全新子进程、恰好一次 prompt 往返、所有能力均为 `false`、`inheritsParentContext: false`、忽略 `request.parent`/`request.agentOptions`、`id = SessionId(randomUUID())`,且 `result` 从不 reject——子进程级失败扁平化为 stop reason,原始错误则通过 `onError` spec 回调送到 `ctx.logger`。模型暴露无需新代码:每个提供方各加载一次 `dsh-tool-subagent`,使用不同的 `toolName`(`subagent_claude_code`、`subagent_codex`)。无需新的会话事件——唯一的模型可见产物是工具结果,因此可重建性与 ACP 完全相同。明确边界:会话日志重建模型可见的 transcript(文本记录),而不是工作区变更历史——获准写入的子进程将文件作为日志之外的环境副作用进行修改,与 bash 工具和 ACP 后端现有行为完全一致;回放复现请求,而非磁盘。
## 已验证的接口事实(固定版本)
两个集成面在本提案之前均已针对固定版本进行了验证——阅读类型与打包源码、运行无需密钥的 spike——而非仅依赖厂商文档。固定版本是验证基线,不是运行时契约:后端不执行运行时版本探测(无 `codex --version` 门禁、无 SDK 版本嗅探)。兼容性在开发时强制执行——每次依赖升级都会针对真实加载路径重跑无密钥套件——在运行时则通过大声失败来保障:协议层面的意外通过 `onError` 结算为 `error`,绝不静默异常。
**`@anthropic-ai/claude-agent-sdk` 0.3.202。** `options.env` 会替换子进程环境(不与 `process.env` 合并),恰好满足清洗需求。`settingSources` 默认加载所有文件系统设置——隔离要求显式传入 `[]`。结果子类型为 `success` | `error_during_execution` | `error_max_turns` | `error_max_budget_usd` | `error_max_structured_output_retries`。中止时 SDK 自行升级 CLI 子进程:立即关闭 stdin,约 2 秒后若子进程未退出则发送 SIGTERM(已观察到;无残留进程)——无需自定义 kill 回退。`outputFormat: {type: 'json_schema'}` 和 `agents` 选项已存在,为 seam 的 `outputSchema` 能力和命名 subagent 类型提供了未来着陆点;两者均不在本 RFC 范围内。
**`@anthropic-ai/claude-agent-sdk` 0.3.202。** `options.env` 会替换子进程环境(不与 `process.env` 合并),恰好满足清洗需求。`settingSources` 默认加载所有文件系统设置——隔离要求显式传入 `[]`。结果子类型为 `success` | `error_during_execution` | `error_max_turns` | `error_max_budget_usd` | `error_max_structured_output_retries`。中止时 SDK 自行升级 CLI 子进程:立即关闭 stdin,约 2 秒后若子进程未退出则发送 SIGTERM(已观察到;无残留进程)——无需自定义 kill 回退。`outputFormat: {type: 'json_schema'}` 和 `agents` 选项已存在,为 seam 的 `outputSchema` 能力和命名 subagent 类型提供了未来着陆点;两者均不在本 Agent Note 范围内。
**codex CLI 0.142.5,`codex app-server`(v2 词汇)。** LF 分隔的 JSON,JSON-RPC 2.0 形状但省略 `"jsonrpc"` 头。
@@ -33,11 +33,11 @@ Status: proposed
## 隔离与凭证
认证方式仅限 API key。每次运行使用一个全新的配置目录(Claude Code 用 `CLAUDE_CONFIG_DIR` 配合 `settingSources: []`,Codex 用 `CODEX_HOME`),dispose 时尽力删除;配置也可以选择一个持久目录。共享的子进程环境辅助函数转发 `PATH`、`HOME`、`TMPDIR`、locale 和代理设置等普通值,移除凭证形态的名称,并叠加显式的 `config.env`。Claude Code 通过该叠加接收 API key,而 Codex 通过 `account/login/start` 接收,而非手写认证文件。
部署只使用 API key 认证,子进程不得看到宿主用户的 Claude Code / Codex 配置:行为必须只由 `cordis.yml` 决定。每次运行获得一个全新的 `mkdtemp` 配置目录——Claude Code 使用 `CLAUDE_CONFIG_DIR`(并显式设置 `settingSources: []`),Codex 使用 `CODEX_HOME`——dispose 时尽力删除;配置字段也可以固定一个持久目录。子进程环境通过提取逐字复用 ACP 后端的 `buildChildEnv` 语义:转发环境变量,但移除凭证形态的变量(`/KEY|SECRET|TOKEN/i`),再叠加 `config.env`——因此 `PATH`、`HOME`、`TMPDIR`、locale 和代理变量保留,CLI 正常运行;只有环境中的凭证形态变量被清洗(Claude Code 的 `ANTHROPIC_API_KEY` 通过 `config.env` 显式进入),Codex key 则通过 `account/login/start` RPC 进入隔离的 `CODEX_HOME`,而非手写 `auth.json`。
## 权限与审批策略
每个后端暴露其引擎原生的策略词汇。Claude Code 默认 `permissionMode: default` 配合 `permission: reject`;Codex 默认 `sandboxMode: read-only`、`approvalPolicy: never`,以及相同的拒绝回退。示例可选择启用 `acceptEdits` 或 `workspace-write`。已知的审批、用户输入和 elicitation 请求接收配置的应答;未知方法接收 method-not-found,未知通知被消费。没有 prompt 到达人类,子进程也不会因等待不可用的输入而无限挂起。
每个后端不压缩为 ACP 单一的 `permission: allow|reject` 旋钮,而把引擎原生词汇作为配置暴露,并采用保守默认值:Claude Code 获得 `permissionMode`(默认 `default`)以及 `permission: allow|reject`(默认 `reject`),后者作为所有漏过请求的 `canUseTool` 自动应答;Codex 获得 `sandboxMode`(默认 `read-only`)和 `approvalPolicy`(默认 `never`),以及同一个 `permission` 后备值,用来应答仍然到达的审批请求。默认值刻意做到不造成损害(开箱即用的子进程无法写文件);示例演示如何开放权限(`acceptEdits` / `workspace-write`)。机械规则是:每一个服务端发起的请求都由程序迅速结算——枚举出的审批/用户输入/elicitation 请求按配置策略应答,未知请求方法用 JSON-RPC method-not-found 错误响应(绝不保持 pending),未知通知被消费——因此任何子进程请求都不会因等待永远不会到来的应答而卡住轮次。这一版中 prompt 不会到达人类,与 ACP 一致。
## StopReason 映射
@@ -47,11 +47,11 @@ Claude Code:`success` → `completed`;`error_max_turns`、`error_during_exec
## 测试
每个适用层级都要求覆盖:
依照根 AGENTS.md 规则在每个层级明确命名,并预先消除风险:
- **无密钥单元/集成测试:** 通过真实 SDK 驱动一个假 Claude CLI,通过真实协议客户端驱动一个脚本化的 Codex app-server。在逐文件 100% 覆盖率下,验证往返、每种 stop 映射、两条取消路径及预中止、权限策略、未知消息、spawn 失败、reload 清理、导出形状、清洗后的环境、临时目录删除,以及 Codex 认证预检失败。
- **有密钥 e2e 测试:** 每个真实引擎在 `acceptEdits` 或 `workspace-write` 下执行文件操作;跳过时命名缺失的二进制或密钥,并断言无残留子进程。
- **快照测试:** 标记为 `TODO(claude-code-subagent-replay)` 和 `TODO(codex-subagent-replay)` 推迟,等待 [subagent 回放 RFC](../../implemented/testing/2026-06-22-subagent-snapshot-replay.md) 描述的进程特定回放形状。
- **无密钥单元/集成测试**:每个后端都镜像 ACP spec 清单(往返和输出累积、每种 stop 映射、两条取消路径、已中止、两种策略下的权限自动应答、未知消息容错、错误命令的 spawn 失败、HMR 提供方清理、导出形状、子进程环境隔离断言和临时目录删除;Codex 另加认证预检失败路径)。Claude Code harness 是通过 `pathToClaudeCodeExecutable` 接入真实 SDK 的脚本化假 `claude` 可执行文件——一个 spike 已在 24ms 内完成端到端无密钥验证(假 CLI 应答一次 `control_request/initialize`,并讲 plain stream-json,约 40 行)。Codex harness 是讲已验证协议格式的脚本化 mock app-server 子进程,沿用 `mock-acp-server.ts` 形状。
- **有密钥 e2e 测试**:每个后端的真实引擎执行并由磁盘验证真实文件工作,固定使用开放后的配置,以免验收与不造成损害的默认值冲突——Claude Code 使用 `permissionMode: 'acceptEdits'`,Codex 使用 `sandboxMode: 'workspace-write'` + `approvalPolicy: 'never'`;自跳过会准确报告缺失的是二进制还是 key。CI 没有密钥,因此依照有密钥策略在本地运行。
- **快照测试**:以 `TODO(claude-code-subagent-replay)` / `TODO(codex-subagent-replay)` 推迟——即 ACP 后端也推迟的独立回放形状([按会话回放 Agent Note](../../implemented/testing/2026-06-22-subagent-snapshot-replay.md));在此期间由无密钥套件提供确定性覆盖。
## 曾考虑的替代方案
@@ -61,7 +61,7 @@ dispose 阶梯和环境清洗要求拥有子进程(spawn 参数、env、信号
### 为什么不用模型可见的 `subagent_type` 参数(单一 Task 风格工具)?
Claude Code 自身的 Task 工具将 subagent 类型放在模型可见的 schema 中,选择一个 prompt + 工具集人格。这里的选择是在执行引擎之间做出的,而只有部署者知道哪些引擎配置了凭证——因此选择留在部署配置层,保持 `dsh-tool-subagent` 文档中的「一个提供方对应一个工具」契约。人格风格的类型选择器应是针对工具的另一个 RFC,而非针对后端。
Claude Code 自身的 Task 工具将 subagent 类型放在模型可见的 schema 中,选择一个 prompt + 工具集人格。这里的选择是在执行引擎之间做出的,而只有部署者知道哪些引擎配置了凭证——因此选择留在部署配置层,保持 `dsh-tool-subagent` 文档中的「一个提供方对应一个工具」契约。人格风格的类型选择器应是针对工具的另一个 Agent Note,而非针对后端。
### 为什么不用登录态凭证和用户自身的配置?
@@ -73,7 +73,7 @@ Claude Code 自身的 Task 工具将 subagent 类型放在模型可见的 schema
### 为什么不用 ACP 适配器(如 `claude-code-acp`)复用既有后端?
社区 shim 将两个引擎包装为 ACP,这会使它们在 `dsh-subagent-acp` 上变成「仅配置」。但这在 harness 与引擎之间插入了一个非官方的第三方层,抹去了本 RFC 暴露的原生控制面(permissionMode、sandboxMode/approvalPolicy、配置目录隔离、apiKey RPC),并以 shim 的发布节奏替换了第一方协议的稳定性。第一方接口——Agent SDK 和 app-server——才是受支持的集成点。
社区 shim 将两个引擎包装为 ACP,这会使它们在 `dsh-subagent-acp` 上变成「仅配置」。但这在 harness 与引擎之间插入了一个非官方的第三方层,抹去了本 Agent Note 暴露的原生控制面(permissionMode、sandboxMode/approvalPolicy、配置目录隔离、apiKey RPC),并以 shim 的发布节奏替换了第一方协议的稳定性。第一方接口——Agent SDK 和 app-server——才是受支持的集成点。
## 验收标准

View File

@@ -2,5 +2,5 @@
# 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:
# pnpm run verify-translation-pairing --write
2026-07-08-interactive-side-sessions.md: ac29f80b31492ce79512cc4d08e33480e0ac6258
2026-07-08-interactive-side-sessions.zh.md: 5d0ef101dc23febefec881b12fcbb5ba4dcf8be9
2026-07-08-interactive-side-sessions.md: dfd325babe215782c9c1cbec3fd9f874783af7ab
2026-07-08-interactive-side-sessions.zh.md: d545f65a821c6057b062300f31c415a2d7f63860

View File

@@ -1,4 +1,4 @@
# RFC: 交互式侧会话与合并回写
# Agent Note: 交互式侧会话与合并回写
Status: proposed
@@ -15,9 +15,9 @@ Status: proposed
- **Fork 并绑定:** 以父会话的平衡已完成轮次前缀创建子会话,并在其元数据中标记 `parentSession` 与 `seedLength`。这组合了 `ctx.agents.create({ seed, meta })`;不新增核心服务或 session-store 方法。
- **顾问定位:** 创建后注入一条插件来源的 `context/message`,告知子会话只做解释,不执行变更或继续任务。保持系统提示词逐字节一致,可在继承的历史上保留提供方的前缀缓存。
- **合并回写:** 向子会话请求一条有长度上限的 handback,然后向父会话注入一条插件来源的 `context/message`。父会话的下一次请求在其日志位置看到该消息,保持回放与[请求可重建性](../../implemented/architecture/2026-07-05-reconstructable-requests.md),无需新增会话事件。
- **呈现:** 调用方式、会话切换与 handback 渲染属于首个客户端拥有的界面。本 RFC 仅规定与界面无关的机制。
- **呈现:** 调用方式、会话切换与 handback 渲染属于首个客户端拥有的界面。本 Agent Note 仅规定与界面无关的机制。
回退产品化、会话树视图、面向模型的侧会话工具,以及 `forkName`/`mergedInto` 元数据均不在本 RFC 范围内。一次 live-adapter spike 已验证了源日志隔离、继承上下文、多轮子会话交互,以及合并回写在父会话下一轮次中的可见性。
回退产品化、会话树视图、面向模型的侧会话工具,以及 `forkName`/`mergedInto` 元数据均不在本 Agent Note 范围内。一次 live-adapter spike 已验证了源日志隔离、继承上下文、多轮子会话交互,以及合并回写在父会话下一轮次中的可见性。
## 曾考虑的替代方案

View File

@@ -2,5 +2,5 @@
# 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:
# pnpm run verify-translation-pairing --write
2026-07-10-sqlite-session-query-provider.md: d1901ab0e37e8f92af322facac0cb48988d1ffe7
2026-07-10-sqlite-session-query-provider.zh.md: 7a86d7294183eec57c3495a182f1403f84c27363
2026-07-10-sqlite-session-query-provider.md: 828d73938e8b9de1a69d021cb10f86aa7b5cd576
2026-07-10-sqlite-session-query-provider.zh.md: 2cb32238d388e3a1751451fe0bb2036dbac57809

View File

@@ -1,4 +1,4 @@
# RFC: SQLite FTS5 会话搜索
# Agent Note: SQLite FTS5 会话搜索
Status: proposed
@@ -6,7 +6,7 @@ Status: proposed
## 问题
精确读取的 `ctx.sessionQuery` 服务有意不维护派生索引。大规模持久化的历史记录需要全文搜索,而不是每次查询都扫描全部事件;当前的活跃会话则需要一个比上一次持久性检查点更新的覆盖层。搜索还需要具体的排序、摘要片段、过滤、分页、取消以及重建行为。
精确读取的 `ctx.sessionQuery` 服务有意不维护派生索引。大规模持久化的历史记录需要全文搜索,而不是每次查询都扫描全部事件;当前的活跃会话则需要一个比上一次持久性检查点更新的覆盖层。搜索还需要具体的排序、摘要片段、分页、取消以及重建行为。
如果把这些关注点拆分到一个推测性的 provider 协调器和一个数据库实现之间,会产生两个耦合的协调状态机。第一个真实实现应当将源观察、提取、SQLite 事务、generation 管理和查询作为一个完整的生命周期来拥有。
@@ -22,7 +22,7 @@ Status: proposed
实现必须从可执行的用例出发定义跨会话和会话内两种搜索范围。每个可搜索事件是一个文档,包含会话元数据、事件元数据、surface 分类、归一化的语义文本和有界的纯文本摘要片段。会话级结果按其最强匹配事件分组;数值化的后端分数保持私有。
过滤器在排序之前编译为参数化 SQL。查询语法被视为数据。排序包含稳定的平局字段。不透明游标绑定到归一化的请求形状和最小相关 generation;不相关的会话变更不应使会话内游标失效。取消操作必须停止调用方等待,并在运行时允许的范围内中断 SQLite 工作。
搜索返回承载内容的结果记录,而非仅含元数据的 header。可链式过滤器作用于这一精确结果形状,并与搜索 API 一同设计和实现,而不会变成 provider 特有的预排序契约。查询语法被视为数据。排序包含稳定的平局字段。不透明游标绑定到归一化的请求形状和最小相关 generation;不相关的会话变更不应使会话内游标失效。取消操作必须停止调用方等待,并在运行时允许的范围内中断 SQLite 工作。
分词器选择仍是实现层面的实验。FTS5 trigram 支持子串召回,但会拒绝短于三个字符的有用词项并增大索引体积;提案在将其写入契约之前,必须对该权衡与默认 Unicode 分词器进行基准测试。
@@ -43,10 +43,10 @@ Status: proposed
- 重启测试覆盖未变更、新增、变更和删除的持久化会话,且不重建整个索引。
- 重新打开时保留持久化行并移除活跃行;活跃行先遮蔽、后显露其持久化基础。
- 测试覆盖两种搜索范围、元数据过滤、surface 默认值、摘要片段、转义、确定性平局、分页、范围内的陈旧游标、取消、动态持久化挂载/卸载,以及事务失败后的恢复。
- 测试覆盖两种搜索范围、承载内容的结果、可链式结果过滤器、surface 默认值、摘要片段、转义、确定性平局、分页、范围内的陈旧游标、取消、动态持久化挂载/卸载,以及事务失败后的恢复。
- schema 不匹配只重置派生数据库。
- 一个 keyless 的端到端测试将真实的持久化后端与真实的 SQLite 搜索包组合使用。
- 在移至 `implemented/` 之前,本 RFC 须修订为实际实现的分词器和公开 API。
- 在移至 `implemented/` 之前,本 Agent Note 须修订为实际实现的分词器和公开 API。
## 风险

View File

@@ -2,5 +2,5 @@
# 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:
# pnpm run verify-translation-pairing --write
2026-07-13-stream-workflow-progress-through-tool-calls.md: c4fe68974bf774306038e3bd2ba3e29e06de3492
2026-07-13-stream-workflow-progress-through-tool-calls.zh.md: b6a3ccbc89bebaae92641a10aea9a9a05b38a293
2026-07-13-stream-workflow-progress-through-tool-calls.md: 668d38b38cab90f6ee9b613d1101d4d44302aa8a
2026-07-13-stream-workflow-progress-through-tool-calls.zh.md: a6d9540eb8c20d785d1395951a131feecd8ba39f

View File

@@ -1,4 +1,4 @@
# RFC: 通过工具调用流式传输工作流进度
# Agent Note: 通过工具调用流式传输工作流进度
Status: proposed

View File

@@ -2,5 +2,5 @@
# 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:
# pnpm run verify-translation-pairing --write
2026-06-11-api-extractor-reports.md: 26562267d188ab2427075c6fccf0ee4b24d63d99
2026-06-11-api-extractor-reports.zh.md: 33e80abc6e9689cf90f3c851039144a53418137e
2026-06-11-api-extractor-reports.md: f110bfe3353e65442f218336aca3e9d492ac2341
2026-06-11-api-extractor-reports.zh.md: 8cb7353e10a8811b20ccd539de15f8e06b76e6ae

View File

@@ -2,6 +2,8 @@
Status: proposed
English | [中文](2026-06-11-api-extractor-reports.zh.md)
> Split out from the original "Doc-sync and API reports" Agent Note (2026-06-11). Parts 1-2 (doc-block typechecking, event-taxonomy verification) shipped — see [doc-sync enforcement](../../implemented/process/2026-06-11-doc-sync-enforcement.md). This is the deferred part 3, kept as a standalone proposal.
## Problem

View File

@@ -1,10 +1,10 @@
# RFC: API extractor 报告
# Agent Note: API extractor 报告
Status: proposed
[English](2026-06-11-api-extractor-reports.md) | 中文
> 从最初的「Doc-sync 与 API 报告」RFC(2026-06-11)中拆出。第 1–2 部分(文档块类型检查、事件分类体系校验)已交付,见 [doc-sync 强制](../../implemented/process/2026-06-11-doc-sync-enforcement.md)。本文是被推迟的第 3 部分,作为独立提案保留。
> 从最初的「doc-sync(文档同步门禁)与 API 报告」Agent Note(agent 决策记录)中拆出(首次提出于 2026-06-11)。第 1 至第 2 部分(文档块类型检查、事件分类体系校验)已交付,见 [doc-sync 强制](../../implemented/process/2026-06-11-doc-sync-enforcement.md)。本文是被推迟的第 3 部分,作为独立提案保留。
## 问题

View File

@@ -2,5 +2,5 @@
# 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:
# pnpm run verify-translation-pairing --write
2026-06-11-architectural-conformance.md: aad11b9e4bcbd31465cb0c4a507654971e24e843
2026-06-11-architectural-conformance.zh.md: 59684bd01a133755a3d7efd90832f6f268037920
2026-06-11-architectural-conformance.md: f7cb0d7397d4e03df225f68417da43b1fec8de62
2026-06-11-architectural-conformance.zh.md: aa25ef6d2772642885ef268bd548fc6dad40d3cf

View File

@@ -1,4 +1,4 @@
# RFC: 架构一致性——依赖规则与适配器套件
# Agent Note: 架构一致性——依赖规则与适配器套件
Status: proposed
@@ -6,19 +6,19 @@ Status: proposed
## 问题
目前有两项架构保证仅存在于行文中:(1)没有任何东西依赖具体的 loop 包([微内核承诺](../../implemented/architecture/2026-06-11-microkernel-event-taxonomy.md));(2)每个 LlmAdapter 都正确地遵循 chunk 协议。二者都应当是机械化的([质量门禁原则](../../implemented/process/2026-06-11-quality-gates.md))。
目前有两项架构保证仅存在于行文中:(1)没有任何组件依赖具体的 loop 包(package)([微内核承诺](../../implemented/architecture/2026-06-11-microkernel-event-taxonomy.md));(2)每个 LlmAdapter 都正确遵循分片协议。二者都应由机制强制执行([质量门禁原则](../../implemented/process/2026-06-11-quality-gates.md))。
## 提案
**dependency-cruiser** 配合以下规则:
- `packages/*`(除 agent-loop 自身的 tests 和 examples/ 外)禁止导入 `@deepseek-ai/dsh-agent-loop`。
- `packages/*`(除 agent-loop(智能体循环)自身的 tests 和 examples/ 外)禁止导入 `@deepseek-ai/dsh-agent-loop`。
- 禁止跨包深层导入(`@deepseek-ai/dsh-*/src/...` 路径)——只允许使用公开入口点。
- packages/ 内禁止导入循环。
- `vendor/*` 禁止从 `packages/*` 导入。
- 分层:dsh-llm 不导入其他 dsh 包;dsh-session 仅导入 dsh-llm;以此类推(packages/README.md 中的依赖表,强制执行)。
**适配器一致性套件**位于 dsh-llm(`@deepseek-ai/dsh-llm/conformance`):一个可复用的 vitest 套件,以适配器工厂为参数,断言 chunk 协议契约——每个 block 内 index 单调递增、`block-end` 之后该 index 不再有 delta、恰好一个 `finish`、usage 至多出现一次、每个 `tool-call-delta` 携带 call id、abort 被及时响应。当前对 mock 运行;DeepSeek V4 适配器从第一天起继承该套件。可选地提供一个 dev 模式的 `strictAdapter()` 包装层,在 debug flag 下于运行时强制执行相同规则(与 [dev 模式不变式](../../implemented/architecture/2026-06-11-dev-invariants-over-deep-readonly.md) 配对)。
**适配器一致性套件**位于 dsh-llm(`@deepseek-ai/dsh-llm/conformance`):一个以适配器工厂为参数的可复用 vitest 套件,用于断言分片协议契约,包括每个块内的索引单调递增、某个索引出现 `block-end` 后不再接收增量、恰好出现一个 `finish`、用量至多出现一次、每个 `tool-call-delta` 都携带调用 id,并且及时响应 abort。当前先对 mock 运行;DeepSeek V4 适配器从第一天起继承该套件。还可以选择提供开发模式下的 `strictAdapter()` 包装层,在调试标志开启时于运行时强制执行相同规则(与 [开发模式不变式](../../implemented/architecture/2026-06-11-dev-invariants-over-deep-readonly.md) 配对)。
## 计划
@@ -33,4 +33,4 @@ Status: proposed
随着包的增加,dep-cruiser 规则需要维护——规则应基于模式(`dsh-*`)而非逐一枚举。
<!-- rfc-format: alternatives-not-recorded (pre-format RFC) -->
<!-- agent-note-format: alternatives-not-recorded (pre-format Agent Note) -->

View File

@@ -2,5 +2,5 @@
# 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:
# pnpm run verify-translation-pairing --write
2026-06-11-supply-chain-and-vendor-drift.md: 97f5a3f999936faf81a67fe69c91f773400cb447
2026-06-11-supply-chain-and-vendor-drift.zh.md: 0a8441c104ca4779a79b36361ea5d84f5cd09aca
2026-06-11-supply-chain-and-vendor-drift.md: a27ae64556dc7366279824f1480e5681b1e86bf1
2026-06-11-supply-chain-and-vendor-drift.zh.md: 25c27650709faf1a462ce9779ee6f0a909746311

View File

@@ -1,4 +1,4 @@
# RFC: 供应链检查与 vendor 漂移验证
# Agent Note: 供应链检查与 vendor 漂移验证
Status: proposed
@@ -10,10 +10,10 @@ vendor manifest(元数据清单)(见[引入 vendor 的决策](../../implem
## 提案
1. **Vendor 漂移检查**(夜间 CI):以 manifest 中记录的 SHA 浅克隆上游仓库,复制对应的 package 源码,与 `vendor/*/src` 做 diff。除非 diff 与已记录的本地修改一致(每项修改以签入的 patch 文件保存——日志条目从行文描述变为可验证的产物),否则任务失败。
2. **依赖安全公告**:对 lockfile 运行 osv-scanner(或 `pnpm audit`),按计划定期执行,并在涉及 lockfile 变更的 PR 上触发。
1. **Vendor 漂移检查**(夜间 CI):以 manifest 中记录的 SHA 浅克隆上游仓库,复制对应的包(package)源码,与 `vendor/*/src` 做 diff。除非 diff 与已记录的本地修改一致(每项修改以签入的 patch 文件保存——日志条目从行文描述变为可验证的产物),否则任务失败。
2. **依赖安全公告**:对 lockfile 运行 osv-scanner(或 `pnpm audit`),按计划定期执行,并在涉及 lockfile 变更的 PR(Pull Request)上触发。
3. **许可证清单**:一个脚本断言每个 vendor 包都携带其 LICENSE 文件,且 package.json 的 `license` 字段与 vendor/README.md 中的清单一致(我们混合了 vendor 的 MIT 与自有的 BSD-3)——作为 CI 步骤运行。
4. **Renovate**(或定时 agent 任务)以小 PR 的形式提议 npm 依赖更新,这些 PR 走完整门禁套件;vendor 包不在其列(它们的更新遵循 manifest 同步流程,理想情况下是半自动化的 agent 工作流:拉取上游、重新应用 patch、运行门禁、以更新后的 manifest 表格开 PR)。
4. **Renovate**(或定时 agent(智能体)任务)以小 PR 的形式提议 npm 依赖更新,这些 PR 走完整门禁套件;vendor 包不在其列(它们的更新遵循 manifest 同步流程,理想情况下是半自动化的 agent 工作流:拉取上游、重新应用 patch、运行门禁、以更新后的 manifest 表格开 PR)。
## 计划

View File

@@ -2,5 +2,5 @@
# 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:
# pnpm run verify-translation-pairing --write
2026-06-20-discover-package-inventory.md: 6729b8ea4386b1595139a2827a95cf3b072e8c94
2026-06-20-discover-package-inventory.zh.md: 71a6fdf97255932dcff11b574ef7c67ef5a39313
2026-06-20-discover-package-inventory.md: f2724a9512f88fccd7026db12de0a76f4d815048
2026-06-20-discover-package-inventory.zh.md: d1a67849fd617cab0712a7c14d18ad7c7190f3c4

View File

@@ -1,4 +1,4 @@
# RFC: 通过发现机制获取包清单,而非维护静态列表
# Agent Note: 通过发现机制获取包清单,而非维护静态列表
Status: proposed
@@ -6,7 +6,7 @@ Status: proposed
## 问题
包(package)与门禁清单在 TypeScript project references、包文档、CI 描述、Knip 覆盖项以及快照场景元数据中反复出现。大多数只是重述包布局、manifest 数据、聚合命令内容或 fixture(测试前置数据)文件。因此每新增一个包或场景都会产生本可避免的同步点。
包(package)与门禁清单在 TypeScript project references、包文档、CI 描述和 Knip 覆盖项中反复出现。大多数只是重述包布局、manifest(元数据清单)数据或聚合命令内容。因此每新增一个包都会产生本可避免的同步点。
[包层级结构](../../implemented/architecture/2026-06-20-package-hierarchy.md)已经手动消除了其中若干:`scripts/publint-all.ts` 现在从 `packages/<group>/<pkg>` 布局推导列表,两份 `tsconfig` 的 `paths` 映射也合并为一个 `@deepseek-ai/dsh-*` 通配符。剩下的是无法用 glob 消除的清单,主要是 `tsconfig.build.json` 的 project `references`——TypeScript 要求它是显式数组(没有通配符形式)。
@@ -14,11 +14,11 @@ Status: proposed
## 提案
让剩余的包/门禁清单可被发现。一个唯一的权威来源——`packages/<group>/<pkg>` 层级结构加上包 manifest(元数据清单)——应当驱动 `tsconfig.build.json` 的 `references`、模块图以及任何全量包列表,并配合一个生成加校验步骤(沿用现有的 `gen-module-graph` / `gen-cordis-catalog` 模式:生成器写出产物,`hygiene`/`doc-sync`(文档同步门禁)中的 `--check` 模式在提交副本陈旧时报错)。模块图生成已经在读取包 manifest。`doc-sync` 应当成为定义并打印其子门禁的唯一命令,文档链接到该命令而非重述第二份列表。
让剩余的包与门禁清单可被发现。一个唯一的权威来源,即 `packages/<group>/<pkg>` 层级结构加上包 manifest,应当驱动 `tsconfig.build.json` 的 `references`、模块图以及任何全量包列表,并配合一个生成加校验步骤(沿用现有的 `gen-module-graph` / `gen-cordis-catalog` 模式:生成器写出产物,`--check` 模式在 `hygiene` / `doc-sync`(文档同步门禁)中发现已提交副本陈旧时失败)。模块图生成已经在读取包 manifest。`doc-sync` 应当成为定义并打印其子门禁的唯一命令,文档链接到该命令,而非重述第二份列表。
层级结构不需要编码关于包的所有事实,但应当编码宽泛的维护策略:core/product 包、集成包、能力 seam 包与 support/test/example 包不应在脚本能区分它们之前先要求一份手工维护的例外列表。
有两类编目项根本不需要生成器:将 e2e 入口 glob 折入 knip 的默认配置段即可直接删除逐包的重复声明;`childSessions` 可从每个场景的 fixture 目录发现,使场景表只需声明策略(`recorded`、`hasModelTurn`、`comparesLog`)。而且即便是这些策略字段,今天也在追踪可从 fixture 推导的事实(`comparesLog` ⟺ 已提交的日志在头行之后还有条目;`recorded` ⟺ `hasModelTurn` 且没有 `replay.override.json` 兄弟文件),因此每个新场景类都在不断添加 fixture 目录本身已经能回答的开关。
有一项已编目的内容根本不需要生成器:将 e2e 入口 glob 折入 Knip 的默认配置段,即可直接删除逐包的重复声明。
## 验收标准
@@ -27,10 +27,9 @@ Status: proposed
- 文档描述真源,而非重复生成的清单。
- CI 调用聚合命令,由这些命令自行管理其子门禁列表。
- `knip.json` 仅在编码真实信息(额外入口文件、被忽略的依赖)时才携带逐包覆盖项,绝不重述默认配置段。
- 快照场景只声明策略,不声明可从其 fixture 目录发现的事实。
## 风险
发现脚本可能变得过于精巧。实现应当保持朴素:读取 manifest、按显式字段过滤、打印解析后的列表、出错时大声报错。收益在于消除手工清单的漂移,而非发明一套构建系统。
发现脚本可能变得过于精巧。实现应当保持朴素:读取 manifest、按显式字段过滤、打印解析后的列表,并在出错时明确失败。收益在于消除手工清单的漂移,而非发明一套构建系统。
<!-- rfc-format: alternatives-not-recorded (pre-format RFC) -->
<!-- agent-note-format: alternatives-not-recorded (pre-format Agent Note) -->

View File

@@ -2,5 +2,5 @@
# 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:
# pnpm run verify-translation-pairing --write
2026-07-04-prune-dead-core-spine-surface.md: 59bbfa181a08b998c52ef63afbc63fd5226294a6
2026-07-04-prune-dead-core-spine-surface.zh.md: 953cb3bc7b30affd505564ac427632732dd9374e
2026-07-04-prune-dead-core-spine-surface.md: 432aaa848540fe7d1db3039399345a02afed5bd3
2026-07-04-prune-dead-core-spine-surface.zh.md: 6352e2ea1ec6ef4459fc07472e5dbdb6cfb648a5

View File

@@ -1,4 +1,4 @@
# RFC: 裁剪无用的公开与结果接口
# Agent Note: 裁剪无用的公开与结果接口
Status: proposed
@@ -6,29 +6,30 @@ Status: proposed
## 问题
若干包根导出、结果字段和便利方法没有生产消费方。它们之所以存活,要么是因为测试通过公开入口导入了内部实现,要么是因为某个类型预期了一个从未出现的调用者。每一项单独看都很小,但合在一起,它们扩大了 SDK 契约、生成的 catalog、文档和回归矩阵,却没有支撑任何已交付的路径。
若干包(package)根导出、结果字段和便利方法没有生产消费方。它们之所以存活,要么是因为测试通过公开入口导入了内部实现,要么是因为某个类型预期了一个从未出现的调用者。每一项单独看都很小,但合在一起,它们扩大了 SDK 契约、生成的 catalog、文档和回归矩阵,却没有支撑任何已交付的路径。
生产语料库是 `packages/*/*/src`、示例源码/配置和运行时脚本。测试、包(package) README 和 RFC 行文是发布的证据,但不是固定调用者。`cordis_inspect` 使 `packages/cordis/tool-cordis/src/api-catalog.ts` 对模型可见,`cordis_mount` 可以通过受保护的真实服务代理调用注入的服务,因此 catalog 中的服务方法和返回形状是真正的动态产品接口。下表因此区分「没有固定的仓库调用者」与「不可达」:涉及 catalog 词汇的行有意收缩模型编写的 mount 能发现和调用的内容,而包根实现辅助函数并不通过该服务门面可达。精确符号搜索得出以下清单:
生产语料库是 `packages/*/*/src`、示例源码/配置和运行时脚本。测试、包 README 和 Agent Note(agent 决策记录)行文是发布的证据,但不是固定调用者。`cordis_inspect` 使 `packages/cordis/tool-cordis/src/api-catalog.ts` 对模型可见,`cordis_mount` 可以通过受保护的真实服务代理调用注入的服务,因此 catalog 中的服务方法和返回形状是真正的动态产品接口。下表因此区分「没有固定的仓库调用者」与「不可达」:涉及 catalog 词汇的行有意收缩模型编写的 mount 能发现和调用的内容,而包根实现辅助函数并不通过该服务门面可达。精确符号搜索得出以下清单:
| 接口 | 生产证据 | 简化方式 |
| --- | --- | --- |
| `SurfaceManager.invalidate()` | 只有其单元测试调用它;seeding 在惰性创建的 manager 存在之前就已完成,且会话从不替换其日志引用。 | 删除它及其不可能触发的整体替换契约。 |
| `ToolExecutionResult.callId` | 每个钩子已经接收不可变的 `ToolExecution`;循环和 ACP(Agent Client Protocol)通过 call/session 事件关联。没有消费方读取这个重复的结果字段。 | 移除该字段、复制/不匹配守卫,以及证明该重复不可能不一致的测试。 |
| `ReactLoopAgent` 根导出 | 包外的命名导入都是测试;生产代码面向 `Agent` 编程,通过 `ctx.agents` 创建/恢复。 | 返回/接口类型为 `Agent`,将具体循环类改为包内部;保留有意设计的同步、仅配置的 `AgentLoop.create()` 路径。 |
| `workflow-workerthread` 的 protocol/runtime/session 再导出与命名的 `WorkerWorkflowEngine` | 每个包名消费方都使用默认引擎;workflow RFC 已将 worker 协议格式(wire format)定义为私有。 | 保留默认插件类/配置契约;移除重复的命名类导出,将协议模块保持为源码私有。 |
| `workflow-workerthread` 的 protocol/runtime/session 再导出与命名的 `WorkerWorkflowEngine` | 每个包名消费方都使用默认引擎;workflow Agent Note 已将 worker 协议格式(wire format)定义为私有。 | 保留默认插件类/配置契约;移除重复的命名类导出,将协议模块保持为源码私有。 |
| `code-runtime-worker` 的 protocol/bootstrap 再导出 | 包外的生产/e2e 消费方使用 `WorkerCodeRuntime` 和配置,而非 `BootstrapPort`、`PatchableStream` 或 worker 消息/启动类型。 | 保留运行时类/配置契约,将其协议格式/bootstrap 词汇改为源码私有。 |
| ACP 的 translation/presenter 根导出 | `agentOptions`、`streamSessionEventUpdate`、`todosToPlan`、`ToolPresenter`、`nullToolPresenter` 和 `TerminalRendering` 只有同文件或 ACP 测试消费方;唯一的包外生产消费方挂载的是插件命名空间。 | 保留 `name`、`inject`、`Config`、`AcpConfig` 和 `apply`;将 translation/presentation 辅助函数改为源码私有,在包内测试。 |
| `providerWording` 与 `completedTurnPrefix` 根导出 | 各有一个同包生产调用者;只有 balanced-prefix 辅助函数有一个同包白盒测试。 | 改为源码私有,测试 provider 行为。 |
| `providerWording` 与 `completedTurnPrefix` 根导出 | 各有一个同包生产调用者;只有 balanced-prefix 辅助函数有一个同包白盒测试。 | 改为源码私有,测试提供方行为。 |
| `depthOf`、`SubagentDepthError`、`SENSITIVE_ENV_PATTERN`、`waitForExit` 与 `exitsWithin` 根导出 | 生产 subagent 后端消费的是进程内 runner 和子进程构造/dispose(资源释放)辅助函数,而非这些强制/测试内部实现。 | 保留深度/环境/退出行为,但将辅助函数和 error/regex 改为源码私有;通过 spawn 和 dispose 测试。 |
| `PersistenceCoordinator.inits`、后端 `inits` 访问器、`seedCoversPrefix` 与 `assertSerializable` | 访问器为白盒测试而存在;`seedCoversPrefix` 没有包外生产导入者;`assertSerializable` 没有生产调用者,且与 coordinator append 边界的无损快照重复。 | 通过 `session/flush` 观察初始化,将 `seedCoversPrefix` 改为源码私有,删除 `assertSerializable`。保留两个后端、`SessionHeader` 和 SQLite 的版本契约。 |
| `LlmError.status` 与 replay status | 适配器/replay 填充它,但生产分支基于稳定的 error code/message 判断,从不读取原始 status。 | 移除未读字段和 replay 管道,保留错误分类。 |
| `BlockAssembler.push()` 返回值 | 两个生产调用者都忽略返回的已完成块。 | 返回 `void`;保留有意公开的 `blocks()`/`message()` 契约。 |
| `compactRegion` 的独立 `session` 参数 | 固定调用者传入的对象与 `agent.session` 上已有的是同一个;模型可见的 mount API 也能调用该方法,但接受两个身份允许挂载的插件提供不一致的配对。 | 保留手动 region seam,同时有意将其收窄为以 `agent.session` 为唯一真源。 |
| `CompactionResult.startSeq`、`summarySeq`、`endSeq` 与 `summary` | 生产消费方只读取 shadowed range/seq/token 统计;持久日志拥有 summary 和事件标识。 | 移除四个结果回显,保留两个共享的 transcript(文本记录)渲染器。 |
| `BasicCompactService` 的 estimation/summarization 可见性 | 没有包外生产调用者调用这五个方法;已实现的 RFC 只将 `estimateContentTokens()` 和 `summarize()` 命名为子类钩子。 | 将这两个方法改为 `protected`,其余三个编排专用的估算器改为 private。 |
| `BasicCompactService` 的 estimation/summarization 可见性 | 没有包外生产调用者调用这五个方法;已实现的 Agent Note 只将 `estimateContentTokens()` 和 `summarize()` 命名为子类钩子。 | 将这两个方法改为 `protected`,其余三个编排专用的估算器改为 private。 |
| `CodeLogEntry.source`/`level` 与 `RunCodeMeta.dispatches` | 每个生产消费方都将日志映射为文本;没有 presenter/模型路径读取其他字段或持久化的 dispatch 计数。 | 将 code-runtime 日志改为字符串(或纯文本条目),移除 result-meta 的 dispatch 管道;保留用于生成确定性 dispatch id 的本地计数器。 |
| `CodeRuntime.language` 与 `CodeRuntime.isolation` | worker 后端提供唯一的生产值,而 Code Mode 及其他所有生产调用方只调用 `run()`。 | 移除未读描述符,同时保留 worker 的语言、隔离、预算、取消与资源释放行为。 |
| `ToolNotFoundError.toolName`、`SystemPrompt.config` 与 `BashTask.command` | 每个存储的公开值都没有生产读取者。 | 移除未读字段,保留错误消息、已解析的配置行为和任务生命周期。 |
| 后端包根实现辅助函数 | 下方精确清单仅通过相对路径的同包导入调用。生产命名空间导入挂载的是保留的插件契约,不读取这些属性;命名根消费方都是测试。 | 保留每个适配器/provider/服务及其配置/错误契约;停止在包根导出所列辅助函数/常量。 |
| 后端包根实现辅助函数 | 下方精确清单仅通过相对路径的同包导入调用。生产命名空间导入挂载的是保留的插件契约,不读取这些属性;命名根消费方都是测试。 | 保留每个适配器/提供方/服务及其配置/错误契约;停止在包根导出所列辅助函数/常量。 |
| 消费方包根实现辅助函数 | 下方精确清单只有同包生产调用者。生产命名空间导入挂载的是插件契约,不读取辅助属性;命名根消费方都是测试。 | 保留插件契约和稳定的错误码;将测试迁移到包内模块或公开行为,停止在包根导出所列辅助函数。 |
### 分组辅助导出清单
@@ -48,14 +49,14 @@ Status: proposed
**保留测试便利函数和自包含的结果字段为公开。** 公开辅助函数可以让白盒测试更方便,自包含的结果字段看起来更符合人体工学,未来的嵌入者可能需要具体循环类或枚举方法。这些好处是假设性的;当前它们让每处实现和文档都要解释没有已交付调用者能观察到的状态。真正的消费方可以引入它所需的最小契约,其所有权和失败语义明确。
**保留所有 catalog 成员以供模型编写的 mount 使用。** 自引用工具集是一条真实的通用消费路径,而非生成文档的噪音。然而,它的价值来自准确、可组合的服务接口,而非无限期保留重复字段或不一致的参数对;上述每一项 catalog 收缩都移除了在同一 execution、agent 或 result 上其他位置已可获得的事实,并在同一变更中更新 API 参考。
**保留所有 catalog 成员以供模型编写的 mount 使用。** 自引用工具集是一条真实的通用消费路径,而非生成文档的噪音。然而,它的价值来自准确、可组合的服务接口,而非无限期保留重复字段或不一致的参数对;上述每一项 catalog 收缩都移除了在同一 execution、agent(智能体)或 result 上其他位置已可获得的事实,并在同一变更中更新 API 参考。
## 验收标准
- 精确符号搜索显示:在本 RFC 及任何已实现 RFC 修正之外,没有被移除的接口。
- 本 RFC 列出的每个接口均按指定方式缺失或降级;清单之外有意保留的扩展/测试契约不变。
- 精确符号搜索显示:在本 Agent Note 及任何已实现 Agent Note 修正之外,没有被移除的接口。
- 本 Agent Note 列出的每个接口均按指定方式缺失或降级;清单之外有意保留的扩展/测试契约不变。
- 工具执行、上下文压缩(context compaction)、两个 LLM 适配器、两个持久化后端、workflow 隔离以及 agent 创建/恢复保持其已交付行为。
- 类型检查、覆盖率、快照、doc-sync、module-graph 校验、构建和 hygiene 通过。
- 类型检查、覆盖率、快照、doc-sync(文档同步门禁)、module-graph 校验、构建和 hygiene 通过。
## 风险

View File

@@ -2,5 +2,5 @@
# 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:
# pnpm run verify-translation-pairing --write
2026-06-11-deterministic-and-stress-testing.md: c629567fd16158a0bd081e7e7850fb3e6d5f1631
2026-06-11-deterministic-and-stress-testing.zh.md: aaa170725f9d3d2457d6f418dce7bc53feda2ab7
2026-06-11-deterministic-and-stress-testing.md: d9977be835af05f9ee303b63ec6015bc9e153170
2026-06-11-deterministic-and-stress-testing.zh.md: 8aed4ad6f277c1e567d91e3a8e2fef1c350ab89c

View File

@@ -1,4 +1,4 @@
# RFC: 确定性测试、回放不变式 fixture 与竞态压力测试
# Agent Note: 确定性测试、回放不变式 fixture 与竞态压力测试
Status: proposed
@@ -12,7 +12,7 @@ Status: proposed
三项措施:
1. **测试中禁止挂钟睡眠。** 将 `setTimeout(N)` 等待替换为事件驱动等待(既有的 `waitForIdle` 模式,扩展为 `waitForStatus`、`waitForEvent(n)`),或在需要测试时间本身时使用 vitest 的 fake timer。通过 lint 规则强制执行:禁止在 `packages/*/tests` 中使用 `setTimeout`,白名单辅助模块除外。
1. **测试中禁止挂钟睡眠。** 将 `setTimeout(N)` 等待替换为事件驱动等待(既有的 `waitForIdle` 模式,扩展为 `waitForStatus`、`waitForEvent(n)`),或在需要测试时间本身时使用 vitest 的 fake timer。通过 lint 规则禁止 `setTimeout`,适用范围是 `packages/*/tests`,白名单辅助模块除外。
2. **通用回放 fixture(测试前置数据)。** 一个共享测试辅助函数包装 agent loop harness,使每个测试结束后,agent 的会话日志被回放到一个全新的 Session 中,并自动断言 `deriveMessages()` 相等。这样该不变式在每次 CI 运行中会被套件产生的所有场景检查数百次,而非仅两次。
3. **夜间竞态压力测试。** 一个 CI job 以 `vitest --repeat=200`(加 `--shuffle`)运行 agent-loop 和 inbox 套件,以暴露调度依赖的失败;发现的任何不稳定测试都视为 bug 修复,绝不靠重试掩盖。
@@ -22,7 +22,7 @@ Status: proposed
## 验收标准
- `packages/*/tests` 中不再有 `setTimeout`(白名单辅助模块除外),由 lint 规则强制执行。
- 不再使用 `setTimeout`;lint 规则在 `packages/*/tests` 中强制执行,白名单辅助模块除外。
- 共享 harness 将每个测试的会话日志回放到全新的 `Session` 中,并自动断言 `deriveMessages()` 相等,覆盖整个套件。
- 夜间 job 以 `--repeat` 和 `--shuffle` 运行 agent-loop 和 inbox 套件;发现的不稳定测试作为 bug 分诊,绝不靠重试掩盖。
@@ -30,4 +30,4 @@ Status: proposed
Fake timer 与 agent loop 中的 Promise 调度存在微妙交互——优先使用事件驱动等待;仅在测试 timer 服务行为本身时才使用 fake timer。
<!-- rfc-format: alternatives-not-recorded (pre-format RFC) -->
<!-- agent-note-format: alternatives-not-recorded (pre-format Agent Note) -->

View File

@@ -2,5 +2,5 @@
# 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:
# pnpm run verify-translation-pairing --write
2026-06-11-mutation-testing.md: 20b24de385b944c27f4bdc0fc70f335d827f50a0
2026-06-11-mutation-testing.zh.md: 780d3417cce48ee19ac8e3dc3b74d78b8e2a4c0f
2026-06-11-mutation-testing.md: 591d9012644a19ee2c67a916b63092d79f78db1f
2026-06-11-mutation-testing.zh.md: 9c22ed2f42e5c44e6be98f132614886bbdb188fd

View File

@@ -1,4 +1,4 @@
# RFC: 变异测试作为覆盖率的制衡手段
# Agent Note: 变异测试作为覆盖率的制衡手段
Status: proposed
@@ -10,16 +10,16 @@ Status: proposed
## 提案
在 `packages/*/src` 上运行 Stryker(`@stryker-mutator/vitest-runner`):
使用 Stryker(`@stryker-mutator/vitest-runner`)对 `packages/*/src` 运行变异测试:
- **PR 范围的增量运行**(仅变更文件),作为一个 CI job。调优后速度足以作为合并门禁。
- **PR(Pull Request)范围的增量运行**(仅变更文件),作为一个 CI job。调优后速度足以作为合并门禁。
- **每夜全量运行**,跟踪变异分数;先记录基线,再将阈值设为观测到的基线并只升不降(与覆盖率策略一致:阈值只收紧)。
- 存活的变异体是待办项:agent 选取一个存活体、编写杀死它的测试、循环往复——一个形态良好的自主循环。
- 等价变异体(可证明不改变行为的)加注释排除并附理由,与 `/* v8 ignore */` 策略一致。
## 计划
1. 添加 Stryker 配置,范围限定在一个包(llm:最小、最具算法性),测量运行时间。
1. 添加 Stryker 配置,范围限定在一个包(package),即 llm(最小、最具算法性),并测量运行时间。
2. 扩展到所有包;在配置中记录基线分数。
3. 接入每夜 job;运行时间可接受后再添加 PR 范围的增量 job。
@@ -33,4 +33,4 @@ Status: proposed
运行时间:变异测试开销大;逐文件 100% 覆盖率有所帮助(每个变异体至少会被执行到)。如果 PR 范围的运行始终过慢,则保持仅每夜运行,依赖分数只升不降的机制。
<!-- rfc-format: alternatives-not-recorded (pre-format RFC) -->
<!-- agent-note-format: alternatives-not-recorded (pre-format Agent Note) -->