docs(notes): replace the retired ACP-anchoring analogy with the injection-turn fact

The automation-only ACP refactor (#586) removed the bridge's pendingSwitches
idle-preset anchoring the inheritance note cited as precedent. Replace the
dangling analogy with the load-bearing fact a reviewer actually needs: an
injection-triggered first turn (a SessionStart hook racing the prompt)
dispatches no prompt-submit and no model request, so the one-shot stamp
listener still lands before the child's first model request.
This commit is contained in:
kingwl
2026-07-26 16:27:35 +08:00
parent be5f76b6c6
commit ea2241c2a9
3 changed files with 4 additions and 4 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-07-25-subagent-policy-inheritance.md: 42196f9b23b9bf31a1f441e9db900493f08e8094
2026-07-25-subagent-policy-inheritance.zh.md: 3ad2bd3dde913850c0b7726a8b650d4f7f75ceda
2026-07-25-subagent-policy-inheritance.md: 000e59dc9a9208e57db5dd44d34bd6ce540801da
2026-07-25-subagent-policy-inheritance.zh.md: 90738b4c298eea879a0093bf919139c8c70f00ef

View File

@@ -12,7 +12,7 @@ Session policy overrides are per-session log folds: the effective sandbox mode i
The shared in-process driver (`startInProcessRun` in `packages/subagent/subagent-inprocess`) snapshots the parent's policy overrides at delegation and stamps them onto the child as ordinary log events inside the child's FIRST turn:
- **Capture synchronously at delegation, stamp at first `agent/prompt-submit`.** The driver reads `overrideOf(parent.session)` for both knobs BEFORE its first await — the delegation moment is the snapshot point, so a parent switch racing the asynchronous child creation belongs to the parent's future, not the child — and installs a one-shot child-scoped `agent/prompt-submit` listener during the creation transaction's setup window, PREPENDED so a veto-capable listener (a denying UserPromptSubmit hook) cannot close the first turn without the stamp. Prompt-submit runs after `turn/start` and before prompt assembly, so the stamped events are turn-enclosed (durable — a bare between-turn event is crash-tail garbage on reload) and visible to the child's very first request (an inherited `'never'` reaches the child's first system prompt). This is the same anchoring the ACP bridge uses for idle preset switches.
- **Capture synchronously at delegation, stamp at first `agent/prompt-submit`.** The driver reads `overrideOf(parent.session)` for both knobs BEFORE its first await — the delegation moment is the snapshot point, so a parent switch racing the asynchronous child creation belongs to the parent's future, not the child — and installs a one-shot child-scoped `agent/prompt-submit` listener during the creation transaction's setup window, PREPENDED so a veto-capable listener (a denying UserPromptSubmit hook) cannot close the first turn without the stamp. Prompt-submit runs after `turn/start` and before prompt assembly, so the stamped events are turn-enclosed (durable — a bare between-turn event is crash-tail garbage on reload) and visible to the child's very first request (an inherited `'never'` reaches the child's first system prompt). An injection-triggered first turn (a SessionStart hook racing the prompt) cannot starve the listener: injection turns dispatch no prompt-submit and no model request, so the stamp still lands before the child's first model request.
- **Only the override chain is copied, through the canonical write paths.** `overrideOf(session)` is the fold alone — never the deployment/configured default — so an unswitched parent stamps nothing and a resumed child keeps following the LIVE default; `stampOverride(child, value)` appends via `setSandboxMode`/`setApprovalPolicy` unless the child already folds to the value. The driver consumes both services opportunistically (`ctx.get`, type-only imports): compositions without them delegate policy-free, unchanged.
- **Fork stale-seed precedence falls out of log order.** The stamped event lands after any switch the seed carried, so the existing last-event-wins fold resolves the child's mode with no new precedence machinery; an equal seed-carried override is deduplicated instead of re-stamped.
- **Nesting composes by construction.** A grandchild's stamp folds its parent-the-child's log, which already contains the child's stamped (or self-switched) override — the chain collapses one level per delegation, at any depth. One-shot `allowed-once` escalation grants never enter any log, so they can never leak down the chain.

View File

@@ -12,7 +12,7 @@ Status: implemented
共享的进程内驱动器(`packages/subagent/subagent-inprocess` 中的 `startInProcessRun`)在委派时快照父级的策略覆盖项,并在子 agent 的第一个轮次内把它们作为普通日志事件盖章写入子会话:
- **委派时同步捕获,首个 `agent/prompt-submit` 时盖章。**驱动器在自己的第一个 await 之前就为两个策略旋钮读取 `overrideOf(parent.session)`——委派时刻即快照点,因此与异步的子 agent 创建过程赛跑的父级切换属于父级的未来,而非子 agent——并在创建事务的 setup 窗口内安装一个一次性的、限定子 agent 作用域的 `agent/prompt-submit` 监听器,且采用前置安装,使得具备否决能力的监听器(会作出拒绝的 UserPromptSubmit 钩子无法在未盖章的情况下结束第一个轮次。prompt-submit 阶段在 `turn/start` 之后、提示词组装之前运行,因此盖章事件被包围在轮次内(具备持久性:轮次之间的裸事件在重新加载时只是崩溃残留的尾部垃圾),并且对子 agent 的第一次请求可见(继承来的 `'never'` 能进入子 agent 的第一份系统提示词)。ACPAgent Client Protocol桥接器处理空闲时预设切换所用的正是同一种锚定方式
- **委派时同步捕获,首个 `agent/prompt-submit` 时盖章。**驱动器在自己的第一个 await 之前就为两个策略旋钮读取 `overrideOf(parent.session)`——委派时刻即快照点,因此与异步的子 agent 创建过程赛跑的父级切换属于父级的未来,而非子 agent——并在创建事务的 setup 窗口内安装一个一次性的、限定子 agent 作用域的 `agent/prompt-submit` 监听器,且采用前置安装,使得具备否决能力的监听器(会作出拒绝的 UserPromptSubmit 钩子无法在未盖章的情况下结束第一个轮次。prompt-submit 阶段在 `turn/start` 之后、提示词组装之前运行,因此盖章事件被包围在轮次内(具备持久性:轮次之间的裸事件在重新加载时只是崩溃残留的尾部垃圾),并且对子 agent 的第一次请求可见(继承来的 `'never'` 能进入子 agent 的第一份系统提示词)。由注入触发的第一个轮次SessionStart 钩子与提示词赛跑)不会饿死该监听器:注入轮次既不派发 prompt-submit 也不发起模型请求,因此盖章仍会落在子 agent 的第一次模型请求之前
- **只复制覆盖链,且全部走规范写入路径。**`overrideOf(session)` 只是折叠本身——从不包含部署/配置默认值——因此未切换过的父级不盖任何章,恢复后的子 agent 继续跟随实时默认值;`stampOverride(child, value)` 通过 `setSandboxMode`/`setApprovalPolicy` 追加,除非子 agent 已折叠出该值。驱动器以可选方式消费这两个服务(`ctx.get`,仅类型导入):未挂载它们的组合照旧进行无策略委派,行为不变。
- **fork 陈旧种子的优先级由日志顺序自然得出。**盖章事件落在种子携带的任何切换之后,因此既有的「最后一个事件生效」折叠即可解析出子 agent 的模式,无需新增优先级机制;种子已携带相同覆盖项时会去重,而不会重复盖章。
- **嵌套按构造即可组合。**孙代 agent 盖章时折叠的是其父级(即上一层的子 agent的日志而该日志已经包含这个子 agent 被盖章(或自行切换)的覆盖项:这条链在每层委派处收拢一级,任意深度均成立。一次性的 `allowed-once` 升级授权从不进入任何日志,因此永远不可能沿链向下泄漏。