Skip to content

fix(workflows): bound settled run retention - #239

Open
smf-h wants to merge 5 commits into
openpi-dev:mainfrom
smf-h:fix/workflow-retention-178-pr
Open

fix(workflows): bound settled run retention#239
smf-h wants to merge 5 commits into
openpi-dev:mainfrom
smf-h:fix/workflow-retention-178-pr

Conversation

@smf-h

@smf-h smf-h commented Aug 28, 2026

Copy link
Copy Markdown

问题

settled workflow details 原先没有明确的内存数量和聚合 UTF-8 字节上限。驱逐和重启行为还可能影响磁盘 artifacts 以及显式 run lookup。

改动

  • 增加数量和 UTF-8 字节双重限制。
  • 非法 retention 配置立即报错。
  • 支持 0 作为不保留内存 projection。
  • 保证 canonical workflow、result、transcript、journal artifacts 不被破坏。
  • 保留精确的 artifact 和恢复引用。
  • session start 时重置 retention 统计。
  • replacement 失败恢复旧 projection 时不重复统计 eviction。
  • 暴露 settledRunsEvicted 及当前会话统计范围。
  • 修复 active 和磁盘恢复 run 的显式状态查询。
  • delivery receipt 更新不会覆盖 canonical workflow details。
  • 增加 retention 回归测试。

验证

  • bun run lint 通过。
  • bun run typecheck 通过,仅有既有 Effect 警告。
  • workflow 聚焦测试通过 33 项;3 项 replay 测试因当前 Node runtime 不支持 --permission 失败。
  • 全仓库检查仍受 Windows CRLF、子进程和文件模式基线问题影响。
    Related to workflows: settled run details 缺少独立 retention bound #178

@somewan820 somewan820 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Must-Fix:extensions/workflows/retention.ts:274 使用 raw JSON.stringify(details) 计算 bytes,但现有 serialization.ts:429-448 的 safeStringify 明确支持 BigInt/cycle。含 BigInt 或 cycle 的合法 workflow result 会在 recordTerminalRun() 中抛错,background path 可能在 resultDelivery.defer() 前失败,blocking path 也可能让已完成 workflow 失败。请使用仓库既有 serializer 或只测量有界字段,并补充 BigInt/cycle delivery 测试。

P2 Should-Fix:dashboardDetails 只返回 activeDetails,而 dashboard.ts:563-577 在 persistence 暂不可用时跳过 non-live run,导致内存中保留的刚完成 projection 从 dashboard 消失。请合并 retained projections,并增加 persistence failure dashboard 测试。静态审查,未运行测试。

@agnitum2009 agnitum2009 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

结论:Request Changes

针对"是否缺乏复现测试过程"的问题:是,已确认。并且我做了对抗测试(真实 sandbox 子进程的 E2E),对上一条审查意见给出实测裁决:P1 根因属实但端到端不可达(严重级应降为 Should-Fix),P2 实测成立。

1. 缺乏复现测试(核心问题)

  • 新增的 7 个测试全部只对新的 retention.ts 类做良性字符串数据的单元测试。测试文件 import 的 retention.tspersistWorkflowDeliveryState 均为本 PR 新增,没有任何测试能在 main 上运行并失败——即没有复现缺陷的测试。
  • Issue #178 明确要求"压力测试连续完成大量包含 agents/logs 的 runs,证明内存集合有界",本 PR 没有提供扩展层级的压力/回归测试,也没有 main 修复前后的内存增长对比数据。
  • Validation 一节承认 3 项 replay 测试在作者环境失败、全仓库有环境基线问题,验证是不完整的。
  • 这个缺口不是假设:下面的 P2 就是该缺口的直接产物。

2. P1(jsonBytes 裸 JSON.stringify):根因属实,端到端不可达 —— 降为 Should-Fix

对抗测试结果(基于本 PR 分支,node --test --experimental-strip-types):

  • 单元级确认:projectWorkflowDetails 对含 BigInt 的 result 抛 TypeError: Do not know how to serialize a BigInt;WorkflowSettledRunRetention.set 对循环引用 result 抛 circular 错误。审查员对根因的静态分析正确。
  • 端到端反驳:script 返回 9007199254740993n + 循环引用对象的真实 sandbox workflow 在本 PR 分支上——阻塞路径正常 completed(工具返回成功、workflow.json 落盘 completed),后台路径 workflow-result 投递消息照常到达。"已完成 workflow 被报为失败"/"后台投递丢失"两个用户可见影响均未发生

原因:sandbox 子进程在 IPC 边界前已将结果归一化(sandbox-child.cjs:238-248:bigint→"123n" 字符串、循环→"[circular]"),父侧再次 toSerializable(sandbox.ts:382),磁盘恢复走 JSON.parsedetails.result 在现有所有生产路径上不可能携带活的 BigInt/cycle。

仍然建议修改(成本低、消除隐患):jsonBytes 依赖了一个没有任何代码强制执行的隐藏前置条件"details 是 JSON-safe 的",未来任何绕过 sandbox 序列化的生产者都会重新引爆 P1 描述的崩溃链。请改用仓库既有 safeStringify/toSerializable 或仅测量有界字段,并补 BigInt/cycle 单元守卫测试——但这是防御性加固,不是用户可触发的 Must-Fix。

3. P2(dashboard 丢失刚完成的 run):实测成立

对抗测试复现:foreground run 完成后,损坏其 workflow.json(模拟持久化暂不可用),dashboard.tsloadRunEntries 对非活跃 run if (!details) continue 直接跳过——run 从 dashboard 数据源消失;而本 PR 将 dashboardDetails() 改为仅 activeDetails(),retention 中保留(或可构造)的内存投影不参与兜底。实测向 loadRunEntries 合并该投影即可恢复显示。这与 issue #178 "operator 能看到 omitted/evicted,而不是把缺失误报为不存在"的要求相悖。请合并 retained projections 作为 fallback,并补 persistence-failure 的 dashboard 测试。

4. 值得肯定

有界投影设计、canonical artifacts 保护测试(persistWorkflowDeliveryState 不会覆盖 result.json)、session 级统计与 evicted 可观测性、UTF-8 安全截断、非法限额 fail-fast、replacement 失败不重复计数——方向与 issue #178 对齐,实现质量整体不错。补上复现测试与 P2 兜底后应可合入。

@agnitum2009

Copy link
Copy Markdown
Collaborator

建议的拆分方案(逐层裁决)

本 PR 约 85% 的新增行是纯增量代码,只有 index.ts 接线部分有行为风险且需要返工。建议按以下三层拆分,第 1、2 层可先行合入:

第 1 层(可立即 approve):retention 模块本体

  • extensions/workflows/retention.ts(新增)
  • extensions/workflows/model.ts(WorkflowMemoryProjection 类型,纯增量)
  • tests/extensions/workflows/retention.test.ts

投影构建器 + 有界存储,自包含、有单元测试。此层合入时还没有任何生产代码消费它,行为零风险。合入前请顺手修一处:jsonBytes 使用裸 JSON.stringify(retention.ts:53-58),请改用仓库既有 safeStringify/toSerializable 或仅测量有界字段,并补 BigInt/cycle 单元守卫测试。对抗测试已确认该缺陷在当前生产路径上不可达(sandbox 在 IPC 边界前已归一化,sandbox-child.cjs:238-248),但模块不应携带一个无人强制执行的隐藏前置条件。

第 2 层(可立即 approve):delivery receipt 帮助函数

  • extensions/workflows/artifacts.tspersistWorkflowDeliveryState(+20 行)
  • 其 canonical artifact 保护测试

外科手术式只更新 delivery 状态、不重写整个 workflow.json,与 retention 无依赖,独立成立。

第 3 层(需返工):index.ts 接线

唯一包含行为变更、也是 P2 唯一所在地的部分。内部实为 4 件可再拆的事:

  1. settledRuns Map → retention 存储(核心修复本体,依赖第 1 层);
  2. dashboardDetails() 改动 —— P2 回归来源:对抗测试实测,损坏已完成 run 的 workflow.json 后,loadRunEntries 会跳过该 run,且内存投影不再参与兜底,run 从 dashboard 消失。需合并 retained projections 作 fallback 并补 persistence-failure 测试;
  3. resolveWorkflowRunDetails 磁盘优先重排 —— 独立行为修复,可单独成 PR;
  4. delivery 时磁盘 hydrate + receipt 持久化 —— 依赖第 2 层。

合并顺序

第 1 层(retention 模块 + serializer 修复)──┐
第 2 层(artifacts 帮助函数)──────────────┼──> 第 3 层(接线 + P2 兜底 + 复现/压力测试)

第 3 层同时请补 issue #178 要求的扩展层级压力/回归测试(连续完成大量含 agents/logs 的 runs,断言内存有界、operator 可见 evicted 数量),以及能在 main 上失败的复现测试。

第 1、2 层拆出后我会立即 approve;第 3 层按上述要求修完即可快速 re-review。

@github-actions github-actions Bot added the area:workflows Workflow engine, capability, skills, or tests label Aug 29, 2026
@smf-h

smf-h commented Aug 29, 2026

Copy link
Copy Markdown
Author

已根据审查意见完成修复、补齐测试,并同步最新 main 解决冲突。原 PR #239 已更新,请重新审查。

P1:BigInt / 循环引用的 byte accounting

extensions/workflows/retention.tsjsonBytes() 不再直接调用裸 JSON.stringify(),现在先复用仓库已有的 toSerializable(),再计算实际 UTF-8 JSON 字节数。

因此:

  • BigInt 和循环引用不会再导致 projection/retention 路径抛异常;
  • memoryProjection.bytes、aggregate retained bytes 和 eviction accounting 仍基于最终序列化结果;
  • arbitrary result 仍不会被复制进 bounded projection;
  • canonical workflow.jsonresult.json、transcript artifacts 和精确 artifact 引用行为保持不变。

新增测试覆盖:

  • BigInt/cyclic result 的 projection 和 retention accounting;
  • BigInt/cyclic result 经过 bounded completion projection,并进入 resultDelivery.defer() / flush。

P2:canonical workflow.json 不可读时的 dashboard fallback

dashboard 现在合并 persisted run ids 和 retained projection ids,读取优先级为:

  1. active details;
  2. canonical workflow.json
  3. 仅当 canonical state 不可读时,使用 retained projection。

相关行为保持如下:

  • active run 始终优先;
  • retained fallback 始终为 live: false
  • stop/cancel 仍然只操作 active run;
  • startedSince 过滤仍然生效;
  • canonical state 的 session/reference 过滤保持不变;
  • canonical state 可读时不会被 retained projection 覆盖。

另外处理了一个边界:极小 byte budget 下,minimal retained projection 可能不包含 sessionId。这种 fallback 不会因为缺失该字段而被错误隐藏,但仍然执行 startedSince 过滤。

新增测试覆盖:

  • 已完成 run 的 workflow.json 损坏时,retained projection 仍能在 dashboard 显示;
  • retained projection 缺少 session metadata 时仍能显示;
  • fallback entry 不会被标记为 live。

Issue #178:扩展层压力测试

新增 extension-level E2E,连续完成 16 个真实 workflow,每个都包含 agent 和 log,并验证:

  • retained run 数量保持在配置上限内;
  • aggregate retained bytes 不超过配置上限;
  • eviction 数量和 bytes 正确统计;
  • workflow_status 向 operator 暴露 retention/eviction 信息;
  • canonical artifacts 仍保留完整的 agent 和 log 数据。

与最新 main 的冲突处理

最新 main 包含另一版 count-only settled retention。合并时:

  • 保留本 PR 的 WorkflowSettledRunRetention 作为唯一 settled state,避免同时维护两个容器;
  • 统一从 settledRuns.stats 获取 count/byte retention 和 eviction 统计;
  • 保留上游新增的 workflow lifecycle、late worktree cleanup 行为及测试;
  • 保留双方的 retention、artifact lookup 和 pressure E2E;
  • 更新上游旧的 count-only 文案断言,使其验证当前更完整的 count、bytes 和 canonical artifact 提示。

验证结果

  • retention/dashboard/delivery 定向及 E2E:8 passed, 0 failed
  • bun run typecheck:通过
  • 指定文件 Biome lint:通过
  • git diff --check:通过
  • conflict marker 扫描:通过

最新合并提交:

c878fdd Merge origin/main into fix/workflow-retention-178-pr

原 PR #239 分支已更新,请重新审查。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:workflows Workflow engine, capability, skills, or tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants