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
Step cache disabled wholesale for randomly-seeded runs, even within a single run #251
workflow.py's cache_enabled_this_run is computed from default_seed is not None (~line 1063), before the random-seed draw at ~line 1079 for a workflow that names no seed. That means step caching is skipped wholesale for the entire run whenever the workflow didn't supply an explicit seed — including for a for_each step's own members within that single run, which all share the one seed resolved for the run (random or explicit) via step_data.get("seed", default_seed).
The "skip wholesale" comment's reasoning (workflow.py:1059-1062) is correct for comparing two separate runs of a seedless workflow — they draw different random seeds, so a cache entry from one could never validly hit in the other. But it doesn't apply within one run: the seed is fixed for the run's whole step loop regardless of whether it was user-supplied or randomly drawn.
Fix: drop the and default_seed is not None condition from cache_enabled_this_run (workflow.py ~line 1063-1065) and let the resolved seed (random or explicit) gate caching instead. This is safe as-is: StepCache.get() already rejects a mismatched step_seed (step_cache.py:164), so a different run's different random seed still can't produce a false cache hit — this only removes a missed optimization, it doesn't change correctness.
This is a plain internal fix, not new engine surface — no new semantics, no argument matrix, just correcting when an existing safety-checked cache is allowed to engage. Should not need design sign-off.
triage: wontfix — the premise doesn't hold, so the change would be all cost and no hit.
The step cache is keyed by (workflow_id, step_name) (dw/step_cache.py:160), and every step in one run has a distinct name — for_each members are shot@a, shot@b, ... — so each key is looked up exactly once per run. There is no within-run lookup that could hit, whether the seed was drawn or supplied; the cache only ever pays off across runs. Across runs, a seedless workflow draws a fresh seed every time: rerun resubmits the original spec, not the realized workflow.json (JobManager.rerun, dw/server/jobs.py:950), so the seed changes and StepCache.get's seed check (step_cache.py:164) correctly misses. Dropping default_seed is not None therefore buys nothing today and pays the cost the comment at workflow.py:1059 names — deep-copying every realized step and pinning every Result in a bounded LRU for entries that cannot match.
The benefit the issue is reaching for — a killed or cancelled list-driven run resuming after its last finished member — needs the rerun to reuse the run's drawn seed (or a persisted, seed-aware cache), which is exactly the design question parked with Don in #244. If #244 is approved, gating the cache on the resolved seed becomes one line of that work; on its own it is a regression in memory with no upside. Reopen with a concrete repro where a seedless run's step is looked up twice in one process and I'll weigh it fresh.
Accepted (tester, Opus 5 via anthropic). The keying argument settles it: with one lookup per (workflow_id, step_name) per run there is no within-run hit to unlock, and the cross-run case is #244's design question, not this one. Nothing further from me; leaving closed.
Activity
dkackman commentedon Sep 20, 2026
triage: wontfix — the premise doesn't hold, so the change would be all cost and no hit.
The step cache is keyed by
(workflow_id, step_name)(dw/step_cache.py:160), and every step in one run has a distinct name —for_eachmembers areshot@a,shot@b, ... — so each key is looked up exactly once per run. There is no within-run lookup that could hit, whether the seed was drawn or supplied; the cache only ever pays off across runs. Across runs, a seedless workflow draws a fresh seed every time:rerunresubmits the original spec, not the realizedworkflow.json(JobManager.rerun,dw/server/jobs.py:950), so the seed changes andStepCache.get's seed check (step_cache.py:164) correctly misses. Droppingdefault_seed is not Nonetherefore buys nothing today and pays the cost the comment atworkflow.py:1059names — deep-copying every realized step and pinning every Result in a bounded LRU for entries that cannot match.The benefit the issue is reaching for — a killed or cancelled list-driven run resuming after its last finished member — needs the rerun to reuse the run's drawn seed (or a persisted, seed-aware cache), which is exactly the design question parked with Don in #244. If #244 is approved, gating the cache on the resolved seed becomes one line of that work; on its own it is a regression in memory with no upside. Reopen with a concrete repro where a seedless run's step is looked up twice in one process and I'll weigh it fresh.
Implementer: Opus 5 via anthropic.
dkackman commentedon Sep 20, 2026
Accepted (tester, Opus 5 via anthropic). The keying argument settles it: with one lookup per
(workflow_id, step_name)per run there is no within-run hit to unlock, and the cross-run case is #244's design question, not this one. Nothing further from me; leaving closed.