Skip to content

Commit e6c3f68

Browse files
maxmilianclaude
authored andcommitted
fix(cookbook): recover completed downloads from DOWNLOAD_OK in background reconciler (odysseus-dev#4000)
The dashboard background status reconciler (_pollBackgroundStatus) only recovered "done" for dependency installs when the backend reported a finished task as "stopped". A real model download whose tmux pane is gone after DOWNLOAD_OK (so the dead-session check misses the landed snapshot) fell through to `task.type === 'download' ? 'crashed'`, so a completed download was shown as crashed (and stalled on the Serve tab). Recover "done" from the terminal DOWNLOAD_OK sentinel, mirroring the dep-install recovery already present. The background poll runs blind, so it keys off the conclusive exit-0 sentinel only — not the `/snapshots/` path, which can be printed mid-stream for multi-file downloads and would risk marking an incomplete download done. Fixes odysseus-dev#3897 Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 3228b0d commit e6c3f68

2 files changed

Lines changed: 28 additions & 2 deletions

File tree

static/js/cookbookRunning.js

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3532,12 +3532,22 @@ async function _pollBackgroundStatus() {
35323532
// dead-session check inspects). Recover "done" from the retained output's
35333533
// exit-0 sentinel so a clean install isn't downgraded to crashed.
35343534
const depDone = !!task.payload?._dep && _depInstallSucceeded(task.output);
3535+
// A finished model download whose tmux pane is gone is also reported
3536+
// "stopped" (the dead-session check can miss the landed snapshot).
3537+
// Recover "done" from the terminal `DOWNLOAD_OK` sentinel — emitted
3538+
// only after the runner exits 0 — so a completed download isn't
3539+
// downgraded to crashed. This background poll runs blind (no live
3540+
// stream to debounce against), so unlike the reconnect loop it keys
3541+
// off the conclusive exit sentinel only, never the `/snapshots/` path,
3542+
// which can be printed mid-stream for multi-file downloads.
3543+
const downloadDone = task.type === 'download'
3544+
&& String(task.output || '').includes('DOWNLOAD_OK');
35353545
const nextStatus = live.status === 'completed'
35363546
? 'done'
35373547
: (live.status === 'error'
35383548
? 'error'
35393549
: (live.status === 'stopped'
3540-
? (depDone ? 'done' : (task.type === 'download' ? 'crashed' : 'stopped'))
3550+
? ((depDone || downloadDone) ? 'done' : (task.type === 'download' ? 'crashed' : 'stopped'))
35413551
: null));
35423552
if (nextStatus && task.status !== nextStatus) {
35433553
updates.status = nextStatus;

tests/test_cookbook_dependency_completion_regression.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,23 @@ def test_background_poll_recovers_done_for_stopped_dependency_install():
7474
source = _read("static/js/cookbookRunning.js")
7575

7676
assert "const depDone = !!task.payload?._dep && _depInstallSucceeded(task.output);" in source
77-
assert "depDone ? 'done' : (task.type === 'download' ? 'crashed' : 'stopped')" in source
77+
assert "(depDone || downloadDone) ? 'done' : (task.type === 'download' ? 'crashed' : 'stopped')" in source
78+
79+
80+
def test_background_poll_recovers_done_for_completed_download():
81+
"""When the backend reports a finished model download as "stopped" (its
82+
tmux pane is gone after DOWNLOAD_OK, so the dead-session check can miss the
83+
landed snapshot), the reconciler must recover "done" from the terminal
84+
DOWNLOAD_OK sentinel instead of downgrading the card to crashed. The
85+
background poll keys off DOWNLOAD_OK only (not the "/snapshots/" path, which
86+
can appear mid-stream for multi-file downloads)."""
87+
source = _read("static/js/cookbookRunning.js")
88+
89+
normalized = " ".join(source.split())
90+
assert (
91+
"const downloadDone = task.type === 'download' "
92+
"&& String(task.output || '').includes('DOWNLOAD_OK');"
93+
) in normalized
7894

7995

8096
def test_dependency_install_payload_keeps_env_path_for_refresh():

0 commit comments

Comments
 (0)