2026-07-22 16:18:04 +08:00
|
|
|
|
# Agent Note(agent 决策记录):持久化 subagent 目录与 list_agents
|
|
|
|
|
|
|
2026-07-26 02:32:34 +08:00
|
|
|
|
Status: implemented
|
2026-07-22 16:18:04 +08:00
|
|
|
|
|
|
|
|
|
|
[English](2026-07-22-durable-subagent-catalog-and-list-agents.md) | 中文
|
|
|
|
|
|
|
|
|
|
|
|
## 问题
|
|
|
|
|
|
|
2026-07-24 10:27:05 +08:00
|
|
|
|
可继续的后台 subagent 会公开稳定的 child id,并将重建描述符持久化在该 child 的会话中,因此 `send_message` 无需任何列表查询操作即可恢复已知 child。`list_agents` 的要求不同:parent 重启后,即使调用方不再知道各 child id,也要只枚举该 parent 的直接可继续 child。[可继续 subagent](../../implemented/feature/2026-07-28-continuable-subagent-conversations.md)负责持久化 Session 与 Activation 设计;本记录负责枚举及其面向模型的查询。
|
2026-07-22 16:18:04 +08:00
|
|
|
|
|
2026-07-24 10:27:05 +08:00
|
|
|
|
枚举必须交叉核对不可变的会话谱系、描述符有效性与实时优先的会话语料,而不能仅为展示就加载或恢复 Agent。它还必须定义缺失、损坏、已删除或不受支持的 child 如何影响列表,以及反复加载大量 child 日志是否需要索引。
|
2026-07-22 16:18:04 +08:00
|
|
|
|
|
2026-07-26 02:32:34 +08:00
|
|
|
|
## 决策
|
2026-07-22 16:18:04 +08:00
|
|
|
|
|
2026-07-26 02:32:34 +08:00
|
|
|
|
parent 到 child 的枚举及 `list_agents` 是一个基于持久化 child Session 契约、单独评审的功能。`SubagentService.listChildren(parentSessionId: SessionId)` 执行以下操作:
|
2026-07-22 16:18:04 +08:00
|
|
|
|
|
2026-07-24 16:09:20 +08:00
|
|
|
|
- 使用 `ctx.sessionQuery.traceSession(parentSessionId)` 获取 parent 的直接且实时优先的 child 会话;
|
2026-07-24 10:27:05 +08:00
|
|
|
|
- 读取并校验每个候选会话的 `subagent/descriptor` 事件,但不激活 child;
|
2026-07-24 16:09:20 +08:00
|
|
|
|
- 默默排除不含描述符的候选;如果候选变得不可用,或其描述符损坏或版本不受支持,则排除该候选并产生对应 child 的 diagnostic;
|
|
|
|
|
|
- 公开每个拥有受支持且有效描述符的 child;该描述符必须带有持久化的创建 `label`,而其提供方当前是否已注册不影响公开;
|
2026-07-24 10:27:05 +08:00
|
|
|
|
- 将存活 child 报告为 `running`,只存在于持久化存储中的 child 报告为 `complete`;
|
2026-07-23 10:16:15 +08:00
|
|
|
|
- 按 `createdAt` 升序、再按 child id 升序稳定返回所有结果 child。
|
2026-07-22 16:18:04 +08:00
|
|
|
|
|
2026-07-26 02:32:34 +08:00
|
|
|
|
描述符持久化、按 id 查找、直接 parent 鉴权和不依赖提供方的冷恢复仍归已实现的 Activation 契约负责。该目录会为描述符增加持久化 `label`,并要求列表查询诊断重复的描述符事件;它不会削弱现有事实,也不会发明第二种描述符表示。
|
2026-07-22 16:18:04 +08:00
|
|
|
|
|
|
|
|
|
|
### 枚举决策
|
|
|
|
|
|
|
2026-07-24 16:09:20 +08:00
|
|
|
|
第一版消费 `ctx.sessionQuery.traceSession(parentSessionId)`,并且只考虑追踪结果的第一层后代。目标可以存活,也可以只存在于持久化存储中;追踪逻辑语料不会加载或恢复 Agent。会话查询已经使用实时优先规则合并 `ctx.sessions` 与 `ctx.sessionPersistence`,保持不可变 header 一致性,根据 `SessionHeader.parentSession` 推导直接 child 谱系,并按 `createdAt` 升序、child id 升序排列 sibling。`listChildren()` 不会重复实现这套语料逻辑,也不会检查继续执行管理器的进程内 Activation map。
|
2026-07-22 16:18:04 +08:00
|
|
|
|
|
2026-07-24 10:27:05 +08:00
|
|
|
|
语料构建先于逐 child 描述符检查。构建初始追踪时如果发生持久化列表查询失败、所观测语料中任意位置的存活/持久化 header 冲突或目标谱系无效,整个 `list_agents` 调用都会失败,因为此时不存在可信的候选集。只有初始追踪成功后的失败才会被隔离到单个候选;因此,这项逐 child 契约中的“损坏 child”是指已加载的事件 surface 或描述符数据损坏,而不是语料级 header 冲突。
|
2026-07-22 16:18:04 +08:00
|
|
|
|
|
2026-07-24 16:09:20 +08:00
|
|
|
|
会话谱系涵盖的范围比 subagent 身份更广:普通 `ctx.sessions.fork()` 和一次性 subagent 也会创建直接 child。会话 header 不新增 `kind` 判别字段;每个候选必须改为恰好包含一个有效的 `subagent/descriptor` 事件。Activation 契约只在初始创建期间写入该事件,从持久化存储恢复时不会追加其他描述符;第二个事件属于损坏,而不是另一次 Activation 的证据。该事件是已追踪 child 属于可继续后台 subagent 的唯一证据;其简短创建 `label` 来自委派的 `description`,其余继续执行字段仍是不依赖提供方的冷恢复所使用的重建输入。缺少该事件的候选属于普通 fork、一次性 child 或其他不可继续的会话,系统会将其排除且不产生 diagnostic。
|
2026-07-24 10:27:05 +08:00
|
|
|
|
|
2026-07-24 16:09:20 +08:00
|
|
|
|
已发布的逻辑记录同时也是状态来源:`SessionRecord.live` 表示 `running`,而 `live: false, persisted: true` 表示 `complete`。该状态直接来自追踪结果,不会导致额外加载 child 日志。`complete` 表示当前没有存活的 Activation,既不表示执行成功,也不表示 child 已永久关闭;`send_message` 仍可物化另一次 Activation。反过来,`running` 只表示会话存活:位于继续执行管理器对应 Activation 之外的存活 Agent 仍会显示为 `running`,但 `send_message` 会拒绝,而不会接管它。child 会话发布前不可见,也不会添加进程内 Activation 条目作为第二个候选来源或状态来源。列表查询是一份快照,可能与发布、dispose 或后续消息发生竞态;`send_message` 仍是消息送达时的权威操作。
|
2026-07-24 10:27:05 +08:00
|
|
|
|
|
2026-07-27 13:54:19 +08:00
|
|
|
|
subagent 服务将 `sessionQuery` 保持为可选依赖,因此没有该服务时仍可执行 start 和 follow-up。其公开的 `listChildren(parentSessionId: SessionId)` 方法只在被调用时才会解析这个可选服务,并动态加载可选的会话查询运行时;因此,普通 subagent 导入、start 和 follow-up 都不会触发该包求值。列表查询直接由 `SubagentService` 负责:它解释查询返回的谱系、事件和存活状态,无需解析基于 Activation 的继续执行管理器,也不会查询 Agent 注册信息、Activation 或提供方;因此,仅包含会话、`subagents` 和 `sessionQuery` 的部署即使缺少 `agents` 也能执行列表查询。如果查询服务缺失,该方法会在加载运行时或执行查询工作前抛出 `SubagentError`,并携带稳定错误码 `SUBAGENT_CONTROL_SESSION_QUERY_UNAVAILABLE`。`@deepseek-ai/dsh-tool-subagent-control` 导出可分别加载的工具插件:`send_message` 适配器只要求 `subagents`,而 `list_agents` 适配器在加载时同时要求 `subagents` 和 `sessionQuery`。因此,部署可以在既不安装也不加载会话查询的情况下使用 `send_message`;列表工具会在插件加载时捕获服务未接入的问题,而其他直接服务消费方会收到同一项明确的调用时契约。
|
|
|
|
|
|
|
|
|
|
|
|
`listChildren(parentSessionId, signal?)` 会把调用方的取消信号转发给 `traceSession()` 和条件性精确 `readEvent()` 操作。`listEvents()` 不接受取消参数,因此列表查询路径会在等待该操作的前后,以及每个候选处理完成后检查信号。如果取消信号触发后有查询操作以拒绝结算,服务会将结果归一化为 `SubagentError`,并携带稳定错误码 `CANCELLED`;后端中止错误或可映射为 diagnostic 的查询错误均不会逃逸,也不会使调用以成功的部分列表返回。
|
2026-07-24 10:27:05 +08:00
|
|
|
|
|
2026-07-27 10:09:54 +08:00
|
|
|
|
这条描述符读取路径是正确性基线,并不声称工作量只与直接 child 数量呈线性关系。令 D 为直接 child 候选数量,C 为每次持久化列表查询所扫描的持久化会话数量,L_i 为候选 i 的完整日志大小。一次语料追踪后,每个候选都会执行 `sessionQuery.listEvents(childId)`。没有描述符的候选会被排除;含有多个描述符的候选会直接产生 diagnostic,无需再次读取;只有恰好含有一个描述符的候选才会通过 `sessionQuery.readEvent({ sessionId: childId, seq })` 再次加载。此次读取返回的不可变会话 header 必须与追踪时观测到的相同,包括直接 parent 关系,并且读取目标仍必须是先前定位的描述符事件;任何不一致均视为该 child 损坏。对于只存在于持久化存储中的最坏情况,每次精确读取都会重复执行 `persistence.list()`、加载完整 child 日志并克隆其中的事件,因此忽略常数因子后的工作量为 O(D × C + Σ L_i);恰好含有一个描述符的候选承担两次这类成本,其他候选只承担一次。存活候选同样会对其完整日志取得一份分离的内存快照;读取其描述符时则会取得两份。会话查询通过持久化 seam 的非变更 `inspect()` 读取解析持久化候选:它返回有效的已存储前缀,既不修复撕裂的尾部,也不关闭中断的 turn,因此列表查询是存储只读操作;修复仍是恢复路径的职责。第一版接受这些重复读取,将其作为无索引的正确性基线,但部署必须将语料总量和 child 日志大小,而不仅是直接 child 数量,视为容量约束。列表查询不会创建 Agent,也不会追加任何目录、描述符或修复事件。对模型隐藏的描述符始终位于对话 surface 之外,并且会在压缩后保留,因此经过压缩和未经压缩的 child 必须枚举出相同结果。
|
2026-07-24 10:27:05 +08:00
|
|
|
|
|
|
|
|
|
|
如果实测规模日后需要索引,该索引属于派生状态:会话 header 和 child 描述符仍是权威信息,重建或损坏回退必须复现相同结果。索引不能成为第二个鉴权来源,也不能让尚未发布的 child 变得可见。
|
2026-07-22 16:18:04 +08:00
|
|
|
|
|
|
|
|
|
|
### `list_agents` 契约
|
|
|
|
|
|
|
2026-07-24 16:09:20 +08:00
|
|
|
|
`SubagentService.listChildren(parentSessionId: SessionId)` 返回 `Promise<SubagentListEntry[]>`,其中的单个数组不会将 child 与 diagnostic 分开,而是保留追踪结果中的候选顺序。`SubagentListEntry` 是一个由只读 `kind` 判别的封闭联合类型:
|
2026-07-22 16:18:04 +08:00
|
|
|
|
|
2026-07-24 16:09:20 +08:00
|
|
|
|
- `kind: 'child'` 携带只读的 `id: SessionId`、持久化 `label: string` 和 `status: 'running' | 'complete'`;
|
|
|
|
|
|
- `kind: 'diagnostic'` 携带只读的 `id: SessionId` 和 `reason: 'corrupt' | 'unsupported' | 'unavailable'`。
|
2026-07-22 16:18:04 +08:00
|
|
|
|
|
2026-07-26 02:32:34 +08:00
|
|
|
|
有效描述符产生一个 child 条目,逐 child 检查失败产生一个 diagnostic 条目,缺少描述符的候选不产生条目。child 状态 `running` 表示逻辑记录在 `ctx.sessions` 中存活;`complete` 表示该记录只存在于持久化存储中。这些值既不是 `AgentStatus`,也不是管理器内部的 Activation 状态,结果不公开内部 `createdAt` 排序键。成功完成、失败、取消和停止原因等精确 Activation 状态与持久化结果需要单独的持久化激活记录,不在本功能范围内。
|
2026-07-24 10:27:05 +08:00
|
|
|
|
|
2026-07-24 16:09:20 +08:00
|
|
|
|
面向模型的 `list_agents` 工具不接受参数,从当前正在执行的 Agent 推导 `parentSessionId`,并作为 `@deepseek-ai/dsh-tool-subagent-control` 中的轻量适配器。它按数组顺序将 child 渲染为 `<id> [<status>] — <label>`,将 diagnostic 渲染为 `<id> [diagnostic: <reason>]`;空数组渲染为 `(no subagents)`。
|
|
|
|
|
|
|
|
|
|
|
|
diagnostic 使用三种固定原因。格式错误的事件 surface、精确加载 child 时发现的 header 冲突、读取结果中的不可变 header 与追踪到的候选不一致或不再指向请求的直接 parent、读取目标不再是先前定位的描述符事件、格式错误的描述符内容和多个描述符事件映射为 `corrupt`。未知描述符版本映射为 `unsupported`。逐 child 读取产生的 `SESSION_QUERY_SESSION_NOT_FOUND`、`SESSION_QUERY_EVENT_NOT_FOUND` 和 `SESSION_QUERY_PERSISTENCE_FAILED` 映射为 `unavailable`。这项阶段边界是有意为之:初始追踪期间发生持久化故障会让操作失败,而同一故障如果始于候选读取期间,可能会让每个受影响的 child 分别产生一条相同的 `unavailable` diagnostic;第一版既不合并这些 diagnostic,也不会把它们提升为全局失败。缺少描述符则作为不可继续 child 排除,且不产生 diagnostic。配置错误、窗口错误和未识别的失败不属于 child diagnostic,会作为操作失败继续向上传播。每条 diagnostic 都标识 child id 及原因,不暴露对模型隐藏的描述符内容;系统会排除该候选,而其他健康的 sibling 仍然可见。系统绝不会读取不属于追踪结果直接后代的会话,也不会为它们产生 diagnostic。
|
|
|
|
|
|
|
|
|
|
|
|
diagnostic 是瞬时查询结果,不属于会话事件或目录状态。推导 diagnostic 时,除了产生该结果的 `listEvents()` 或条件性 `readEvent()` 操作外,不会执行额外加载。
|
2026-07-22 16:18:04 +08:00
|
|
|
|
|
2026-07-23 10:16:15 +08:00
|
|
|
|
第一版不提供 child 删除操作。如果后续产品行为会删除 child 会话,持久化列表会自然移除已删除的 child;任何未来的派生索引都必须移除或 tombstone 同一条目,避免 `list_agents` 保留陈旧状态。
|
2026-07-22 16:18:04 +08:00
|
|
|
|
|
|
|
|
|
|
## 已考虑的替代方案
|
|
|
|
|
|
|
|
|
|
|
|
**将列表查询并入激活 RFC。** 按 id 持久化描述符和从持久化存储恢复无需 parent 到 child 的枚举。保持查询独立,可让 `send_message` 落地时不必同时承担列表状态、扫描性能或删除行为。
|
|
|
|
|
|
|
2026-07-24 10:27:05 +08:00
|
|
|
|
**直接通过 `SessionPersistence.list()` 重建谱系。** 这种做法会重复实现会话查询中的实时优先语料合并、不可变 header 一致性检查、直接 child 追踪和确定性排序。列表查询应使用现有可信查询服务,只增加 subagent 特有的描述符校验与渲染。
|
|
|
|
|
|
|
|
|
|
|
|
**列出每个已追踪的 child 会话。** `parentSession` 能证明谱系,却不能证明 child 是可继续的 subagent:普通会话 fork 和一次性 subagent 也使用这个 header 字段。列表查询还必须读取并校验描述符。
|
|
|
|
|
|
|
2026-07-24 16:09:20 +08:00
|
|
|
|
**为 `SessionHeader` 添加 `kind` 判别字段。** header 仍不会携带校验或恢复可继续 subagent 所需的重建数据,因此列表查询无论如何都必须读取描述符。将描述符作为唯一的 subagent 判别信息,可避免引入第二个分类来源。
|
|
|
|
|
|
|
2026-07-24 10:27:05 +08:00
|
|
|
|
**使用存活的 Agent 注册表作为目录。** 系统会在 Activation 结算后有意 dispose 它,而且注册表状态会在重启时消失,因此无法支持持久化发现。
|
2026-07-22 16:18:04 +08:00
|
|
|
|
|
2026-07-24 10:27:05 +08:00
|
|
|
|
**使用进程内 Activation map 作为第二个目录。** 这种做法能公开管理器驻留状态,却会让会话发现查询与物化及结算耦合,引入另一套排序时钟,并让同一个 child 在其生命周期内改变候选来源。第一版只列出已经发布的逻辑会话,并将 `SessionRecord.live` 视为其快照状态。
|
2026-07-22 16:18:04 +08:00
|
|
|
|
|
2026-07-27 13:54:19 +08:00
|
|
|
|
**让列表查询经过基于 Activation 的继续执行管理器。** 管理器负责驻留状态并要求 `agents`,而列表查询只解释会话查询事实。让读取经过该管理器会强制引入无关的运行时服务,并使发现能力随 Activation 控制一同消失,因此列表查询直接由 `SubagentService` 负责。
|
|
|
|
|
|
|
2026-07-24 16:09:20 +08:00
|
|
|
|
**按当前提供方可用性过滤。** 提供方注册状态属于进程本地状态,即使描述符仍然持久存在,该状态也可能发生变化。即使继续执行不依赖提供方,过滤仍可能隐藏持久化或存活 child。因此,列表查询根据描述符确立持久化身份,而 `send_message` 在消息送达时执行权威的鉴权与驻留状态检查。
|
|
|
|
|
|
|
2026-07-22 16:18:04 +08:00
|
|
|
|
**持久化 parent 会话目录事件。** 直接 child header 已经提供持久化枚举种子,child 描述符则是重建的权威信息。第二份 parent 日志会重复状态,并造成跨会话顺序和陈旧条目行为,却无助于按 id 恢复。
|
|
|
|
|
|
|
2026-07-23 10:16:15 +08:00
|
|
|
|
**某个 child 无法加载时让整次列表查询失败。** 这种做法不会让损坏问题被忽略,但一个损坏的 sibling 会让每个健康 child 都不再可见。逐 child diagnostic 在保持每次排除明确可见的同时,也保留了发现能力。
|
|
|
|
|
|
|
2026-07-24 16:09:20 +08:00
|
|
|
|
**分别返回 child 和 diagnostic 数组。** 分离的数组会引入两个排序域,或者要求公开另一个排序键才能重建候选顺序。一个带判别字段的条目数组既能保留追踪顺序,也能保证 child 与 diagnostic 字段的类型安全。
|
|
|
|
|
|
|
2026-07-27 10:09:54 +08:00
|
|
|
|
**通过会触发修复的 `load()` 路径读取候选。** 复用恢复路径的 `load()` 语义可以让发现提前持久化地关闭中断尾部,但会把列表查询变成变更操作,并使其失败模式与写协调耦合。会话查询的语料读取本就使用非变更的 `inspect()` 契约,因此列表查询保持存储只读,尾部修复留给真正需要它的恢复路径。
|
2026-07-23 10:16:15 +08:00
|
|
|
|
|
2026-07-24 16:09:20 +08:00
|
|
|
|
**立即为查询分页或设置上限(暂缓)。** 这可以限制一次结果的大小,但会使模型发现成为有状态操作,而且除非模型继续跟随 cursor,否则可能隐藏更早的 child。第一版没有 cursor、分页参数或候选数量上限配置,而是返回经稳定排序的完整集合;如果实测规模需要限制,服务级限制仍留待后续决策。
|
2026-07-23 10:16:15 +08:00
|
|
|
|
|
2026-07-26 02:32:34 +08:00
|
|
|
|
## 测试
|
2026-07-22 16:18:04 +08:00
|
|
|
|
|
2026-07-27 13:54:19 +08:00
|
|
|
|
- `packages/subagent/subagent/tests/list-children.spec.ts` 先固定一个只有会话、`subagents` 和 `sessionQuery` 而没有 `agents` 的纯查询组合,再以无密钥方式驱动完整真实栈(agent loop、JSONL 持久化、spawn/fork 提供方、subagent 服务,以及一个具体的会话查询服务):通过真实 `startContinuable()` child 的全新发现;只存在于持久化存储中(重启形态)的 parent 目标;带有人工构造并列项的按 `createdAt` 再按 id 排序;排除普通 fork 和 fork seed 中祖先描述符且不产生 diagnostic;存活 `running` 与持久化 `complete` 的对比;重复描述符、载荷格式错误、无效 surface、header 不匹配和读取目标已变化的损坏 diagnostic 均不隐藏健康的 sibling;不受支持版本与逐 child unavailable diagnostic;提供方缺失时不排除 child;压缩与未压缩的孪生 child 列表结果一致;排除孙代会话;追踪阶段失败导致整次调用失败而候选阶段失败只隔离到单个 child;配置/窗口错误和无法识别的失败作为操作失败向上传播;转发 trace/精确读取取消并稳定归一化为 `CANCELLED`;以及 `SUBAGENT_CONTROL_SESSION_QUERY_UNAVAILABLE` 缺服务契约。`packages/subagent/subagent/tests/optional-session-query.spec.ts` 会在导入普通 subagent surface 时拒绝对可选运行时的 eager 求值。
|
2026-07-26 02:32:34 +08:00
|
|
|
|
- `packages/subagent/tool-subagent-control/tests/list-agents.spec.ts` 固定 `list_agents` 的 schema(无参数)、child/diagnostic/空结果的固定文本形式、带持久化 label 的已结束 child 端到端列表、无调用 agent 时的拒绝、加载时的 `sessionQuery` 注入,以及 HMR dispose。
|
|
|
|
|
|
- 无密钥 ACP 快照场景 `subagent-list-agents`(examples/acp-agent)固定模型可见的转写:一次后台委派结束后,`list_agents` 针对 subagent 服务、会话查询和 JSONL 持久化真实执行,渲染 `<id> [complete] — <label>`。
|
2026-07-22 16:18:04 +08:00
|
|
|
|
|
2026-07-26 02:32:34 +08:00
|
|
|
|
## 影响
|
2026-07-22 16:18:04 +08:00
|
|
|
|
|
2026-07-24 16:09:20 +08:00
|
|
|
|
- 会话追踪会观察完整的逻辑语料,随后描述符校验会读取每个直接 child 的日志一次,并对恰好含有一个描述符的候选读取两次。对于只存在于持久化存储中的最坏情况,工作量为 O(D × C + Σ L_i),而不只是 O(D),因为每次精确读取都会重新扫描持久化存储,并加载和克隆候选的完整日志。后续的派生索引必须保持相同的鉴权、逐 child diagnostic 和回退行为。
|
2026-07-26 02:32:34 +08:00
|
|
|
|
- 语料构建是一个全有或全无的信任边界:一处存活/持久化 header 冲突就会导致初始追踪失败,并隐藏原本健康的 sibling。只有初始追踪成功后,逐 child 隔离才会生效。
|
2026-07-27 10:09:54 +08:00
|
|
|
|
- 撕裂的 child 尾部会被呈现而非修复:非变更的 `inspect()` 读取返回有效的已存储前缀,因此写入中途被打断的 child 在恢复路径的修复加载将其关闭之前,可能以较短的日志形式出现在列表中。
|
2026-07-24 10:27:05 +08:00
|
|
|
|
- 第一版没有删除操作,因此只要 child 会话仍保留在持久化存储中,它们就会继续出现在列表里,但存活 Agent 资源仍由驻留 Activation 数量限制。
|
2026-07-24 16:09:20 +08:00
|
|
|
|
- 查询会返回每个直接可继续 child 和 diagnostic,不设服务 cursor 或候选数量上限。稳定排序可使结果确定,但不会限制模型上下文的增长;服务分页或删除仍是后续的产品决策。
|
2026-07-24 10:27:05 +08:00
|
|
|
|
- `running` 和 `complete` 是进程内语料快照,而非消息送达承诺。另一个进程可能在当前进程将某个持久化 child 报告为 `complete` 时激活它;跨进程准确性需要共享租约。
|
2026-07-26 02:32:34 +08:00
|
|
|
|
- 将持久化 `label` 设为描述符必填字段是一次发布前的格式变更:描述符版本保持为 1,加入 label 之前的构建所写入的日志会在描述符校验中判为 `corrupt`(与仓库首个正式版本之前不做兼容承诺的立场一致)。
|