diff --git a/docs/rfc/proposed/feature/2026-07-07-plan-mode.i18n.yaml b/docs/rfc/proposed/feature/2026-07-07-plan-mode.i18n.yaml index 15112a3cfa..1ca0e1ca49 100644 --- a/docs/rfc/proposed/feature/2026-07-07-plan-mode.i18n.yaml +++ b/docs/rfc/proposed/feature/2026-07-07-plan-mode.i18n.yaml @@ -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-plan-mode.md: 0897f66b7bdaa789c0cae2bc348690da77c6f42c -2026-07-07-plan-mode.zh.md: 9c7b1fe6e9cb4416e67fcf242bd80d00a3e0cc45 +2026-07-07-plan-mode.md: b28984bfad7e9a1adb8bbaaa4daa79a5c00fdf65 +2026-07-07-plan-mode.zh.md: 2b9be1cea702362ec3852c3da48909e6acdf25cf diff --git a/docs/rfc/proposed/feature/2026-07-07-plan-mode.md b/docs/rfc/proposed/feature/2026-07-07-plan-mode.md index 0897f66b7b..b28984bfad 100644 --- a/docs/rfc/proposed/feature/2026-07-07-plan-mode.md +++ b/docs/rfc/proposed/feature/2026-07-07-plan-mode.md @@ -72,7 +72,7 @@ The allowlist is deliberately the degenerate form of a future per-tool decision ### The fold, the service, and the flush -`foldMode(events)` is pure (exported for reconstructors and tests); the service tracks it per session with a lazy cursor in a `WeakMap` — O(new events) per read, never invalidated, because the log is append-only and `mode/set` is not a surface node (compaction cannot rewrite it). `ctx.modes` (a cordis Service, key `modes`) exposes `list()` — the synthetic `default` entry plus the configured definitions, for pickers — `get(agent): { current, pending? }`, and `set(agent, mode)`, which validates the name against config, drops a no-op (target equals pending ?? current), and otherwise records the intent in a `WeakMap`. A contained `session/event` listener ([defensive patterns](../../../defensive-patterns.md): a policy plugin must not kill the feed) flushes the pending intent as a `mode/set` append on the next `turn/start` or `step/end` — both sit outside the step's tool-execution window, so the executions of a step always run under the mode its assembly folded — and, when the flushed mode differs from the fold at the last `request/header`, appends the one coalesced `context/message` notice in the same frame. Seeding rides `agent/created`: a declaration-merged `AgentOptions.mode` becomes a pending intent, so explicit options beat the logged baseline on create AND resume — the same precedence the call-config seed follows — while a fork child needs no mechanism at all (the parent's `mode/set` is inside the seeded prefix). +`foldMode(events)` is pure (exported for reconstructors and tests); the service tracks it per session with a lazy cursor in a `WeakMap` — O(new events) per read, never invalidated, because the log is append-only and `mode/set` is not a surface node (compaction cannot rewrite it). `ctx.modes` (a cordis Service, key `modes`) exposes `list()` — the synthetic `default` entry plus the configured definitions, for pickers — `get(agent): { current, pending? }`, and `set(agent, mode)`, which validates the name against config, drops a no-op (target equals pending ?? current), and otherwise records the intent in a `WeakMap`. A contained `session/event` listener ([defensive patterns](../../../defensive-patterns.md): a policy plugin must not kill the feed) flushes the pending intent as a `mode/set` append on the next `turn/start` or `step/end` — both sit outside the step's tool-execution window, so the executions of a step always run under the mode its assembly folded — and, when the flushed mode differs from the fold at the last `request/header`, appends the one coalesced `context/message` notice in the same frame. Seeding rides `agent/created`: a declaration-merged `AgentOptions.mode` becomes a pending intent, so explicit options beat the logged baseline on create AND resume — the same precedence the call-config seed follows — while a fork child needs no mechanism at all (the parent's `mode/set` is inside the seeded prefix). One read-path rule closes the resume-with-edited-config gap: a folded mode name the current config no longer defines behaves as the default mode plus one boundary notice naming the dropped definition — never a silent substitute restriction, never a bricked session; `set()`'s loud validation covers only the write path, and a resumed log answers to the config it finds. ### The soft layer: a computed section and a post-`next()` filter diff --git a/docs/rfc/proposed/feature/2026-07-07-plan-mode.zh.md b/docs/rfc/proposed/feature/2026-07-07-plan-mode.zh.md index 9c7b1fe6e9..2b9be1cea7 100644 --- a/docs/rfc/proposed/feature/2026-07-07-plan-mode.zh.md +++ b/docs/rfc/proposed/feature/2026-07-07-plan-mode.zh.md @@ -72,7 +72,7 @@ allowlist 刻意是未来按工具决定映射(`allow | deny | ask`)的退 ### fold、服务与落账 -`foldMode(events)` 是纯函数(导出给重构器与测试);服务用 `WeakMap` 里的惰性游标按会话跟踪它——每次读取 O(新事件数),永不失效,因为日志仅追加且 `mode/set` 不是 surface 节点(compaction 改写不了它)。`ctx.modes`(cordis Service,键 `modes`)暴露 `list()`——合成的 `default` 条目加上配置的定义集,给选择器——`get(agent): { current, pending? }`,以及 `set(agent, mode)`:对配置校验名字、丢弃 no-op(目标等于 pending ?? current)、否则把意图记进 `WeakMap`。一个被收容的 `session/event` listener([防御模式](../../../defensive-patterns.md):策略插件不得杀死事件流)在下一个 `turn/start` 或 `step/end` 把待落账意图作为 `mode/set` 追加落账——两处都在 step 的工具执行窗口之外,所以一个 step 的执行永远运行在其组装所 fold 的模式下——并且当落账模式与最后一条 `request/header` 处的 fold 不同时,在同一帧内追加那条合并的 `context/message` 通知。播种骑 `agent/created`:声明合并的 `AgentOptions.mode` 变成待落账意图,于是显式选项在 create 与 resume 上都压过日志基线——与调用配置种子相同的优先级——而 fork 子代理完全不需要机制(父的 `mode/set` 就在种子前缀里)。 +`foldMode(events)` 是纯函数(导出给重构器与测试);服务用 `WeakMap` 里的惰性游标按会话跟踪它——每次读取 O(新事件数),永不失效,因为日志仅追加且 `mode/set` 不是 surface 节点(compaction 改写不了它)。`ctx.modes`(cordis Service,键 `modes`)暴露 `list()`——合成的 `default` 条目加上配置的定义集,给选择器——`get(agent): { current, pending? }`,以及 `set(agent, mode)`:对配置校验名字、丢弃 no-op(目标等于 pending ?? current)、否则把意图记进 `WeakMap`。一个被收容的 `session/event` listener([防御模式](../../../defensive-patterns.md):策略插件不得杀死事件流)在下一个 `turn/start` 或 `step/end` 把待落账意图作为 `mode/set` 追加落账——两处都在 step 的工具执行窗口之外,所以一个 step 的执行永远运行在其组装所 fold 的模式下——并且当落账模式与最后一条 `request/header` 处的 fold 不同时,在同一帧内追加那条合并的 `context/message` 通知。播种骑 `agent/created`:声明合并的 `AgentOptions.mode` 变成待落账意图,于是显式选项在 create 与 resume 上都压过日志基线——与调用配置种子相同的优先级——而 fork 子代理完全不需要机制(父的 `mode/set` 就在种子前缀里)。一条读路径规则补上「resume 时配置已改」的缺口:fold 出的模式名若当前配置已不再定义,按默认模式行事,并在下一个边界追加一条点名被移除定义的通知——绝不静默替换一个别的限制,也绝不让会话变砖;`set()` 的大声校验只覆盖写路径,恢复出来的日志面对的是它所遇到的配置。 ### 软层:计算型 section 与 post-`next()` 过滤器