You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
validate_workflow: composing a template through a workflow step loses its measured cost basis #268
Both hysteresis and hysteresis-ltx2.5 (workflows that compose other templates as steps) answer basis: "unknown", partial: true from validate_workflow — even though the templates they compose each carry their own observed_minutes (6.6 min over 2 runs for one; 2.76 min over 11 runs for the other).
Naming/wrapping a template as a step in a composing workflow makes the whole thing permanently unpriced, regardless of how well-measured the piece is on its own.
Ask: a composing step should be able to inherit the composed workflow's basis (and roll its observed timing into the composing workflow's own estimate), rather than the composition itself resetting to unknown every time.
Related: #255, #267 — the same plan.estimate surface not reflecting what's actually knowable about the plan.
triage: batch with #255, #267 — same dw/plan.py estimate code; #255's session does the work. The docstring on estimate says a composed child is deliberately not added when the parent has an observed run; the gap is when the parent has no figure and the child does — that is the case to fill. (triage by implementer agent, model opus via provider anthropic; lem is at develop head c5d4ac5, so nothing here is fixed-but-undeployed)
Fixed. A composing parent workflow (no cost of its own, every declared step a workflow step assembling children) now rolls up a fully-observed child's history into the parent's estimate with basis: "observed", instead of reporting basis: "unknown", partial: true.
Two parts in dw/plan.py/dw/server/app.py:
dw/server/app.py: added an observed_for_child closure wired into the /api/validate route's build_plan() call, so a composed child's own observed figure (keyed by the catalog name it resolves to under resolve_sub_workflow) is available to the estimator.
Deployed to lem: pulled develop (fast-forward to 859e43c), restarted the dw.serve --mcp process running in the don-owned screen session there, confirmed healthy via GET /api/health before handing off.
Suggested regression case (for the tester to add once verified): in regression-suite-complete.md (composes-workflows trait, edge-case-y), validate a parent workflow whose only steps are workflow sub-workflow steps, no cost block of its own, calling a child this box has solid observed history for — confirm plan.estimate.basis == "observed" and partial == false. And separately, confirm a parent with a genuine uncosted step of its own (e.g. a for_each task step with no cost) plus a priced/observed child still reports partial: true (the #242 case, unchanged).
Inline pure-composition parent (id: qa-268-inline, one workflow step → templates/minimax/video-with-audio, no cost) → basis: "observed", partial: false, minutes: 6.6. So the roll-up works for an inline definition too, not only stored ones.
Matches expected:. One small gap, not blocking this: the rolled-up parent reports runs: null and measured_on: null while the child it inherited from reports runs: 2 / measured_on: "NVIDIA GeForce RTX 3090" — filing that separately.
Adding the proposed regression case to regression-suite-complete.md next.
Activity
dkackman commentedon Sep 21, 2026
triage: batch with #255, #267 — same
dw/plan.pyestimate code; #255's session does the work. The docstring onestimatesays a composed child is deliberately not added when the parent has an observed run; the gap is when the parent has no figure and the child does — that is the case to fill. (triage by implementer agent, model opus via provider anthropic; lem is at develop head c5d4ac5, so nothing here is fixed-but-undeployed)fix(mcp): #255, #267, #268 - plan.estimate pricing fixes
dkackman commentedon Sep 21, 2026
Fixed. A composing parent workflow (no
costof its own, every declared step aworkflowstep assembling children) now rolls up a fully-observed child's history into the parent's estimate withbasis: "observed", instead of reportingbasis: "unknown", partial: true.Two parts in
dw/plan.py/dw/server/app.py:dw/server/app.py: added anobserved_for_childclosure wired into the/api/validateroute'sbuild_plan()call, so a composed child's own observed figure (keyed by the catalog name it resolves to underresolve_sub_workflow) is available to the estimator.dw/plan.py: added_only_composes_children(definition)— checked against the raw, pre-elision, pre-expansiondefinition(notexpanded), because a step that saves nothing and that nothing reads is elided from the run (Proposal: a step nothing references does not run (from #109's discarded portraits) #122) and an elided own step is still real declared work, not pure composition. This distinguishes an uncosted parent that is pure composition (this issue — the children's figures are the whole story) from one with real uncosted work of its own (validate_workflow's cost estimate is confidently wrong for composed workflows with no cost block #242 — e.g. afor_eachtask step with nocostblock, where a priced/observed child still leaves a genuine gap and must staypartial: true). Fixed a bug in an earlier attempt that usedexpandedand wrongly un-partialed a genuinely-partial validate_workflow's cost estimate is confidently wrong for composed workflows with no cost block #242-style case.Shipped as part of a batch with #255 and #267 (all three are fixes to
dw/plan.py's pricing logic), one branch, one deploy.fix/255-267-268-plan-estimate, merged todevelopat commit859e43c(git logmessage: "fix(mcp): validate_workflow: plan.estimate.minutes ignores plan.cached_steps — 14.2 min quoted for a 285 s run with one shot cached, and still 14.2 with every step cached #255, validate_workflow: overriding a declared cost_driver silently returns basis: catalog with the wrong (unmatched) figure #267, validate_workflow: composing a template through a workflow step loses its measured cost basis #268 - plan.estimate pricing fixes")lem: pulleddevelop(fast-forward to859e43c), restarted thedw.serve --mcpprocess running in thedon-owned screen session there, confirmed healthy viaGET /api/healthbefore handing off.Suggested regression case (for the tester to add once verified): in
regression-suite-complete.md(composes-workflows trait, edge-case-y), validate a parent workflow whose only steps areworkflowsub-workflow steps, nocostblock of its own, calling a child this box has solid observed history for — confirmplan.estimate.basis == "observed"andpartial == false. And separately, confirm a parent with a genuine uncosted step of its own (e.g. afor_eachtask step with nocost) plus a priced/observed child still reportspartial: true(the #242 case, unchanged).dkackman commentedon Sep 21, 2026
Verified over MCP (tester agent, model opus via provider anthropic).
Ran, all with
workspace: "hysteresis"(the two stored parents live there, not indefault— the issue body didn't say, for the record):validate_workflow(name="hysteresis")→plan.estimate = {minutes: 6.6, basis: "observed", partial: false, unpriced: []}(wasunknown/partial: true).validate_workflow(name="hysteresis-ltx2.5")→{minutes: 2.5, basis: "observed", partial: false, unpriced: []}.id: qa-268-inline, oneworkflowstep →templates/minimax/video-with-audio, nocost) →basis: "observed", partial: false, minutes: 6.6. So the roll-up works for an inline definition too, not only stored ones.qa-268-mixed: same composing step plus an ownextract_framestask step with nocost→basis: "unknown", partial: true, unpriced: ["qa-268-mixed"], minutes 6.6 still carried. Distinction preserved as described.validate_workflow(name="templates/minimax/video-with-audio")→basis: "observed", minutes: 6.6, runs: 2, measured_on: "NVIDIA GeForce RTX 3090".Matches
expected:. One small gap, not blocking this: the rolled-up parent reportsruns: nullandmeasured_on: nullwhile the child it inherited from reportsruns: 2/measured_on: "NVIDIA GeForce RTX 3090"— filing that separately.Adding the proposed regression case to
regression-suite-complete.mdnext.