[Bug] workflow tool clips its result to 50,000 chars BEFORE the spill policy runs; the spill file labelled "Full formatted result" is truncated and the rest is lost
#6866
kevinchiha
started this conversation in
General
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Versions: dsh 0.1.5-rc.1,
@deepseek-ai/dsh-tool-workflow0.1.5-rc.2,@deepseek-ai/dsh-spill-policy0.1.5-rc.2. Same code indsh-tool-workflow@0.1.6-alpha.1(lib/index.js:130-134).What happens
A
workflowscript that returns more thanmaxResultChars(default 50,000) loses the excess for good. The tool's ownrender()clips the JSON and appends… [truncated: N more characters]. The spill policy runs later, ontools/post-execute, and only ever sees that clipped text. It saves it to the spill file and labels the file "Full formatted result stored at: …". The file is not full, and nothing in the message says so.Nothing else keeps a copy. The
tool/resultsession event stores the same clipped text, and the inlineworkflowtool writes onlytool-workflow/run-startandrun-endevents, neither of which carries the value. Theworkflow_managerun records belong to a different package (@dsh-external/workflow), not to this tool.On 2026-09-16 a 39-agent workflow here returned ~226 KB. 78% of the findings were gone before anything reached disk; the run had to be repeated with hand-capped return values.
Repro
Any script with a return value over 50,000 characters, zero agents needed:
Result: a spill file of 50,098 bytes that ends with
… [truncated: 10042 more characters].tailnever reaches disk. The inline notice reads(Omitted 292 bytes. Full formatted result stored at: /tmp/dsh-spill-…/…-workflow.txt. …).Where
dsh-tool-workflow/lib/index.js:130-134—renderResult()slices tomaxCharsand returns the clipped string.dsh-tool-workflow/lib/index.js:226-229—output.rendercalls it withmaxResultChars.dsh-tools/lib/index.js:3415-3445—createSuccessResult()renders before post-execute; the full value is still onresult.value.dsh-spill-policy/lib/index.js:155-172— the post-execute arm readsdecision.content ?? result.content, the already-clipped text, and callsformatSpillNotice()(:21-23), which always says "Full formatted result".Two defects
render()first defeats that: the spill file holds a copy of the truncation, not of the result.Why the cap can't be raised from user config
tool-workflowregisters no settings namespace, so atool-workflow:section insettings.yamlis ignored (verified: same 50,000 cut after adding one). The live row is inside thestandardagent preset (dsh-agent-presets/presets/standard/agent.cordis.yml:227), which nocordis.patch.ymllayer reaches. The base-bundle row atdsh-base/cordis.patch.yml:374is disabled bydsh-web-app/cordis.patch.yml:458. And a bigger cap only widens the window; it does not fix the ordering.Suggested fix
In
dsh-tool-workflow, letrender()emit the full JSON and leave bounding to the spill policy, which already does head/tail preview plus a locator. If a cap must stay in the tool for deployments without a spill store, spill first and clip second, or hand the policy a "partial" flag soformatSpillNotice()can say so.Work-around we run
A ~40-line local plugin on
tools/post-executethat, for a successfulworkflowcall, re-rendersresult.valuewhole and replaces the content, so the policy spills the real thing. It is pushed (not prepended), so it always runs inside the policy'snext()whichever row loads first. With it, the 60 KB repro lands whole in the spill file with no truncation marker. Happy to share it if useful.All reactions