fix(workflows): stop safeStringify node budget from silently truncating artifacts - #596
Conversation
…ng artifacts The four safeStringify callers that write workflow.json, transcripts.json, and result.json passed only maxBytes and inherited serialization.ts's DEFAULT_MAX_NODES = 20_000. That global node counter is reached long before the byte budget, so toSerializable inserted a "[truncated: node limit]" marker and broke — producing a structurally valid file that reads back as a clean `completed` run while trailing data was silently gone. No reader consumes the marker. Reachable on default config: 12 agents each with ~202 transcript entries wrote only 11 of 12 transcripts at 28% of the 2 MiB cap; the 12th hydrated as an empty transcript in /workflows with no notice. Pin maxNodes to each artifact's byte budget so the honest, reported byte cap is the only binding limit (a value dense enough to reach N nodes always serializes to more than N bytes). For transcripts.json, additionally assemble the file with a new boundedTranscriptsArtifact helper that keeps whole agents in index order until the byte budget is reached and reports the dropped tail via a new transcriptsOmitted field, surfaced in the dashboard report and completion alerts (mirroring logsDropped). The manifest and result.json fall back to the existing honest byte-cap stub on genuine overflow. Fixes openpi-dev#558. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
tt-a1i
left a comment
There was a problem hiding this comment.
节点预算修复和磁盘回读路径本身是对的,但新增的省略证据还没有穿过 settled/completion 内存投影。请补齐这个降级路径及回归测试后再合并。
…on projections Addresses PR review: the omission evidence stopped at the disk read-back and never reached the in-memory projections. The settled projection (projectWorkflowDetails) rebuilds WorkflowDetails from a field whitelist that dropped transcriptsOmitted, and completionEnvelope() builds its envelope from that projection — so a restored or evicted run lost the notice, and the completion alert never fired. - retention.ts: pass transcriptsOmitted through makeProjection's candidate (like logsDropped), and add it to the byte-pressure drop list so the bound still converges under an extreme budget. - completion-projection.ts: surface it in the expanded operator report next to the transcripts artifact (the collapsed alert was already added). - tests: assert it survives projectWorkflowDetails and appears in both the alerts and expanded evidence of buildWorkflowCompletionDisplay. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
全部 349 个 workflow 测试通过(2 个平台跳过),format/lint/typecheck 干净。 |
|
@tt-a1i 刚看到
|
tt-a1i
left a comment
There was a problem hiding this comment.
审查提交:b63a3a7629b6a6fedfd035729ddeb86ee42d442e。
本轮未发现新的阻断问题。以下结论仅覆盖所列验证范围,不是合并批准。
验证范围
15 artifact-node-budget/retention/completion tests pass. Latest head copies transcriptsOmitted into settled projection and covers completion display; prior retention finding addressed.
限制与后续
No full-suite run. Existing safeStringify depth/string truncation is unchanged and not claimed fixed.
已核对的 CI 失败
Run 35496500076:Windows:Antigravity SSE lifetime 测试期望 next SSE event,却在 first SSE event 超时;失败位于本 PR 未修改的 provider 用例,尚未证明是偶发还是底层回归。
…ail view The omission evidence only reached saved report.md (buildWorkflowReport), the completion alert, and disk. The interactive detail page (renderDetail) never read the field, so after transcripts.json overflows its byte budget a user opening a dropped agent still saw an empty transcript labeled "this run predates transcript capture" — the exact misdirection issue openpi-dev#558 targets. - renderDetail now shows a run-level omission notice, reserving its row so the exact-height layout is preserved. - The transcript view's emptyText is run-aware: a dropped agent reads as "omitted ... to stay within its byte budget" instead of "predates capture". - Regression test drives WorkflowDashboard.render() through both surfaces. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
补充一个自查时发现的相关问题,已一并修复(commit 6e17db9)。这个不在之前的 review 里,是我复查这次改动的 UI 影响面时找到的。 问题: 修复(
回归测试( 验证:dashboard 套件 40/40 通过;全部 workflow 测试 0 失败( |
tt-a1i
left a comment
There was a problem hiding this comment.
复审提交 e5373b988cb26f5c1f628d8106f376c17cc6da7b。
未发现新的阻断问题。新增投影将 transcriptsOmitted 带到运行详情,详情页给出 run 级别的省略数量,进入空 transcript 时也不再错误显示为“旧运行尚未捕获”;高度预算同步扣除提示行,测试覆盖精确布局和文案。当前所有远端检查通过。
验证边界:分支目前落后于 main,合并前仍需更新并确认最终 head;本结论不扩大到既有 safeStringify 深度或字符串截断语义。
Problem
Fixes #558. The four
safeStringifycallers inextensions/workflows/artifacts.tsthat writeworkflow.json,transcripts.json, andresult.jsonpass onlymaxBytesand inheritserialization.ts'sDEFAULT_MAX_NODES = 20_000. That node budget is a global counter over the whole value tree, so it is reached long before the 1–2 MiB byte budget. When it is,toSerializableinserts a"[truncated: node limit]"marker andbreaks — producing a structurally valid file that reads back as a cleancompletedrun while trailing data is silently gone. No reader consumes the marker.Reachable on default config: 12 agents each with ~202 transcript entries (the upstream ceiling
TRANSCRIPT_MAX_ENTRIES = 200) writes only 11 of 12 transcripts, at 28% of the 2 MiB byte cap; the 12th agent hydrates as an empty transcript in/workflowswith no notice. Every othersafeStringifycall in the repo already passes an explicitmaxNodes(model.ts:604,progress-projection.ts:65,sandbox.ts:164,378), andpersistWorkflowAgentResultuses fail-closedencodeCompleteJson— these four sites were simply missed.journal.ts:281-289already documents this exact hazard for a sibling artifact.Value
Terminal workflow artifacts stop losing data silently — the failure mode AGENTS.md's "runtime owns … exact terminal evidence" contract is meant to prevent. Within the valid configured range the artifacts are written in full; genuine overflow is reported, not hidden.
Approach
Make the byte budget the binding limit instead of the incidental 20k-node default. A value dense enough to reach N nodes always serializes to more than N bytes, so pinning
maxNodesto each artifact's byte budget guarantees the honest, reported byte cap is what binds.transcripts.json— newboundedTranscriptsArtifacthelper keeps whole agents in index order until the next would exceed the 2 MiB budget (binary-searched for exactness), and reports the dropped tail via a newtranscriptsOmitted: { agents, entries }field rather than letting the serializer silently drop it. Empty-transcript hydration was the most user-hostile symptom, so partial retention + a reported count is warranted here.workflow.json(terminal + final manifest) andresult.json— pass an explicitmaxNodesequal to the byte budget. The manifest is bounded-shape metadata that never truncates within the valid agent range (delivery/status/transcriptArtifact preserved);result.jsonholds arbitrary script data, so genuine overflow falls back to the existing honest{truncated, reason, preview}stub → read-back downgrades touncertain. This is the issue's second suggested direction.transcriptsOmittedis mirrored onWorkflowDetails(likelogsDropped), validated on untrusted read-back innormalizePersistedWorkflowDetails, and surfaced inbuildWorkflowReportand completion alerts.Out of scope (per the issue):
serialization.tsdefaults are unchanged, andretention.ts's in-memory settled projection keeps its ownomittedaccounting — the canonical on-diskworkflow.jsoncarriestranscriptsOmitted, so a re-read restores it.Validation
bunx biome format/biome lint/tsc --noEmit— clean on all changed files. Config-, docs-, and discipline-contract checks pass (internal budget, not a package-owned user choice, so no setup/README updates required).tests/extensions/workflows/artifact-node-budget.test.ts(5 tests, all pass): the reported 12-agent bug; partial-retention + reported count on 2 MiB overflow; theboundedTranscriptsArtifactunit contract; 1024-agent manifest surviving without losingdelivery/status/transcriptArtifact; honestresult.jsonoverflow.artifacts,retention,dashboard,narrator, andcompletion-projection.web/uideps not installed → Vitest +check:web; plan-mode ANSI rendering; an installedpi-coding-agent0.84.1vs0.85.1version-drift assertion) were verified to reproduce on cleanmainwith this change stashed.Impact
/workflowsnow shows an "N of M agent transcript(s) omitted" notice instead of silently rendering empty transcripts; large runs no longer read back as cleancompletedwith missing data.deliveryreceipt at high agent counts, so a pending background completion is redelivered on restart as intended.transcriptsOmittedfield to persistedworkflow.json; older records without it read back unchanged.🤖 Generated with Claude Code