fix(tools): bound the shaped-append side channel; total error containment; recorded spill snapshot

Responding to ds-review-bot round 2 on #661:

- logWork is bounded: past maxParallelSubCalls pending shaped-append tasks
  the ordered commit lane holds (Promise.race drains one), so a slow spill
  backend backpressures the run instead of accumulating unbounded pending
  I/O and retained results. Tasks self-remove on settlement; run
  settlement still drains every task inside the open turn. New spill test
  drives three oversized reads against a hung backend at cap 1 and proves
  the third dispatch cannot start until a save drains.
- shapeDispatchLog's catch uses errorMessage() (total), so a thrown value
  with a throwing toString cannot escape the containment and lose the
  settle event.
- CodeDispatchLog.content documented as the RENDERED result projection
  (native tool/result vocabulary), not what the program received — the
  program gets the structured value; doc pair + type-equiv re-synced.
- New RECORDED tui-agent snapshot scenario code-mode-dispatch-spill: the
  real Loader-visible composition (worker runtime + spill-local + policy)
  drives an oversized bash sub-call end-to-end; replay proves the durable
  dispatch copy is bounded to preview + locator while the program value
  stays whole (the outer result carries just the line count).

Agent Note updated (both languages).
This commit is contained in:
Tianyi Cui
2026-07-26 18:29:09 +08:00
parent 442a3dd884
commit c3c10820ba
14 changed files with 384 additions and 23 deletions
@@ -14,7 +14,7 @@ Status: implemented
**在注册表上增设一个日志整形 waterfall(瀑布式事件),spill 策略作为其第一个监听器。**
- **Seam**`tools/code-dispatch-log`,一个按作用域过滤的 waterfall,由桥接层在追加 `tool/code-dispatch` 之前对每个已结算的子分发运行(经由 `registry.shapeDispatchLog`,且故障被兜住:监听器抛出异常时回退到未整形的内容)。载荷(`CodeDispatchLog`)携带外层执行、提升出来的 `agent` 路由键、子调用标识与默认内容。可整形的只有持久副本:程序已经跨 worker 边界收到了完整的值,而模型两者都看不到
- **Seam**`tools/code-dispatch-log`,一个按作用域过滤的 waterfall,由桥接层在追加 `tool/code-dispatch` 之前对每个已结算的子分发运行(经由 `registry.shapeDispatchLog`,且故障被兜住:监听器抛出异常时回退到未整形的内容,并用全防御的错误格式化确保恶意抛出值无法逃出兜底)。载荷(`CodeDispatchLog`)携带外层执行、提升出来的 `agent` 路由键、子调用标识与默认内容——即原生 `tool/result` 所载的渲染后结果投影(程序本身收到的是结构化 `value`)。可整形的只有持久副本;模型两者都看不到。整形作为被跟踪的旁路工作在程序路径之外运行,但有界:待处理日志任务超过 `maxParallelSubCalls` 时有序提交车道会暂停,因此慢速 spill 后端会对整个 run 施加背压,而不是无限累积待完成 I/O;run 结算仍会在开放轮次内排空全部任务
- **策略**`dsh-spill-policy` 在新 seam 上注册第二个分支,与其面向模型的分支共用一模一样的替换流水线(同样的 `maxInlineBytes` 上限、同样的预览 + 定位符 + 不超上限不变式、同样的尽力而为回退),产物以 `dispatch` 为标签,记在子调用 id 名下。UI 与回放通过 spill 产物读取全文,方式与读取被 spill 的原生结果完全相同,因此与原生同等保真的渲染在施加边界之后依然成立。
- **一处有意的不对称**:面向模型的分支跳过 `read`(避免 `read → spill → read again` 循环);分发日志分支则连 `read` 子调用也施加边界:日志副本不是模型上下文,该循环因此不可能发生,而 `read` 恰恰是会产生巨大日志的那个工具。