Commit ba7bf2e
fix(1370): stat the partial instead of asserting it (#1392)
* wip(1370): claim the unverified resumable-partial claim
Refs #1370
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(#1370): stat the partial instead of asserting it
`download_model action:"status"` told users:
cancelled — the partial was left on disk and can be resumed by re-issuing
the download (it picks up where it left off)
Nothing ever checked. The string was selected from `status === "cancelled"` plus
two flags; no code stat'd the file. The reporter paused a 33 GB download BECAUSE
of that sentence, scanned their whole install tree, found no partial of any kind,
and restarted from zero.
The message was not stale. It was never an observation.
What makes it worse is that the neighbouring branches were already careful. A
ComfyUI-Manager dispatch correctly says there is NO local partial. A
reclaimed-dead writer correctly says one MAY exist and was left untouched.
`afterCancelAdvice("local")` correctly hedges with "resumes any .partial the dead
writer left, or restarts cleanly". Only the ordinary cancel stated a fact about
the filesystem without consulting it — surrounded by code that knew better.
Both cancelled branches now report what is on disk: the partial's SIZE when there
is one, so "resumable" is something the user can weigh against restarting, or its
absence, so they can decide before spending the bandwidth rather than after. A
cancel that leaves nothing is not an error; telling someone it left something is.
SEVERAL CANDIDATE NAMES, not one. The staged file is `.<basename>.partial` in the
download cache, and a cancelled job knows its destination by routes of differing
reliability — `filename`, the landed path, the destination key, the URL's last
segment. A few stats are cheap, and guessing one wrong would reintroduce the same
false claim inverted: reporting "nothing to resume" over a 30 GB partial that is
sitting right there. A zero-byte partial reports as absent, because resuming from
it saves nothing and calling it resumable is the original bug in miniature.
Only rows that could HAVE a local partial are checked — a Manager dispatch never
writes one and already says so.
DELIBERATELY NOT IN SCOPE: why the reporter's bytes were missing. Candidates
include a Manager-side dispatch whose `via_manager` flag was stripped (#1197) and
local downloads being routed through Manager at all (#1374). Those are separate
issues, and this message has to be honest under every one of them rather than
depending on which is true.
Mutation-tested: counting a zero-byte file as resumable, and dropping the
basename extraction, each kill a test.
Fixes #1370
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(#1370): derive the partial path the way the WRITER does, not the way it sounds
Caught while writing my own review prompt, before anyone else saw it. The fix I
had just committed was wrong in the same shape as the bug it fixes, pointing the
other way.
I looked for `.<destination filename>.partial` in the cache dir, because that is
what "the partial for this download" sounds like. The writer stages under
`.<sha256(cacheIdentity)[0:32]><ext>.partial` — keyed by the CACHE identity
(cachePathForUrl), not the destination. So the lookup would have missed every
real partial and told users "NO partial was found on disk" for downloads that
had one.
That is worse than the original defect. The old message at least erred toward
"your bytes are safe"; mine would have told someone with a 30 GB resumable
partial to start over.
MY TESTS COULD NOT HAVE CAUGHT IT, because the fixtures created files under the
name I was searching for. They encoded my belief about the naming rather than the
naming — the third time that exact shape has cost me today, after an index-based
removeWidget double and an empty-graph object_info fixture. The fixtures now
build their files from `stagedPartialPathForUrl`, the same function production
uses, so the two cannot disagree; one test pins that the staged name is hashed
and does NOT contain the destination filename.
SCOPE, stated in the message rather than papered over: cacheIdentity folds in
representation-affecting request headers and cloud credentials, which a job
record deliberately does not keep. So this reproduces the staged path exactly for
an unauthenticated public download — the reporter's case, and the common one —
and cannot for an authenticated variant. A miss is therefore reported as "none
found under this URL's staged name", explicitly not as "none exists". Having
just shipped one over-claim I am not going to hide a smaller one.
Also drops the multi-candidate search: with the path derived correctly there is
exactly one place to look, and trying several names could only ever match a
DIFFERENT download's partial and report its bytes as resumable.
Mutation-tested: reverting to the filename-based guess kills a test.
Refs #1370
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(#1370): the staged partial is HIDDEN, and there is now one definition of its path
Codex round 2, and this is the second time I got the same path wrong.
Round 1: searched `.<destination filename>.partial` — wrong KEY, since the
writer stages by cache identity.
Round 2 (my "fix"): derived the cache path correctly and DROPPED THE LEADING
DOT, so it looked for `hash.ext.partial` while the writer writes
`.hash.ext.partial`. Still missed every real partial. Still would have told a
user holding 30 GB of resumable bytes to start over — the inverted false claim
this issue is about, shipped in the commit that claimed to prevent it.
BOTH TIMES MY TESTS AGREED WITH ME, because the fixtures were built by calling
the same helper under test. A fixture derived from the code under test cannot
falsify it. The previous version even pinned the name with
`/[0-9a-f]{32}\.safetensors\.partial$/`, which matches equally well with and
without the dot.
So the fix is structural rather than another careful correction: ONE exported
`stagedPartialPathForTarget`, and the WRITER now calls it. Not two derivations
that agree today — the same function, so they cannot disagree. A test asserts the
writer routes through it and that the hand-rolled expression exists in exactly
one place; another asserts the leading dot on its own, separately from the hash,
because the combined regex is what hid it.
Mutation-tested: dropping the dot fails 2 tests, and re-inlining the path at the
writer fails the wiring test.
Refs #1370
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>1 parent faa4876 commit ba7bf2e
3 files changed
Lines changed: 267 additions & 3 deletions
File tree
- src
- __tests__/tools
- services
- tools
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
2347 | 2347 | | |
2348 | 2348 | | |
2349 | 2349 | | |
2350 | | - | |
| 2350 | + | |
2351 | 2351 | | |
2352 | 2352 | | |
2353 | 2353 | | |
| |||
3302 | 3302 | | |
3303 | 3303 | | |
3304 | 3304 | | |
| 3305 | + | |
| 3306 | + | |
| 3307 | + | |
| 3308 | + | |
| 3309 | + | |
| 3310 | + | |
| 3311 | + | |
| 3312 | + | |
| 3313 | + | |
| 3314 | + | |
| 3315 | + | |
| 3316 | + | |
| 3317 | + | |
| 3318 | + | |
| 3319 | + | |
| 3320 | + | |
| 3321 | + | |
| 3322 | + | |
| 3323 | + | |
| 3324 | + | |
| 3325 | + | |
| 3326 | + | |
| 3327 | + | |
| 3328 | + | |
| 3329 | + | |
| 3330 | + | |
| 3331 | + | |
| 3332 | + | |
| 3333 | + | |
| 3334 | + | |
| 3335 | + | |
| 3336 | + | |
| 3337 | + | |
| 3338 | + | |
| 3339 | + | |
| 3340 | + | |
| 3341 | + | |
| 3342 | + | |
| 3343 | + | |
| 3344 | + | |
| 3345 | + | |
| 3346 | + | |
| 3347 | + | |
| 3348 | + | |
| 3349 | + | |
| 3350 | + | |
| 3351 | + | |
| 3352 | + | |
| 3353 | + | |
| 3354 | + | |
| 3355 | + | |
| 3356 | + | |
| 3357 | + | |
| 3358 | + | |
| 3359 | + | |
| 3360 | + | |
| 3361 | + | |
| 3362 | + | |
| 3363 | + | |
| 3364 | + | |
| 3365 | + | |
| 3366 | + | |
| 3367 | + | |
| 3368 | + | |
| 3369 | + | |
| 3370 | + | |
| 3371 | + | |
| 3372 | + | |
| 3373 | + | |
| 3374 | + | |
| 3375 | + | |
| 3376 | + | |
| 3377 | + | |
| 3378 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
26 | 26 | | |
27 | 27 | | |
28 | 28 | | |
| 29 | + | |
29 | 30 | | |
30 | 31 | | |
31 | 32 | | |
| |||
222 | 223 | | |
223 | 224 | | |
224 | 225 | | |
| 226 | + | |
| 227 | + | |
| 228 | + | |
| 229 | + | |
| 230 | + | |
| 231 | + | |
| 232 | + | |
| 233 | + | |
| 234 | + | |
| 235 | + | |
| 236 | + | |
| 237 | + | |
| 238 | + | |
| 239 | + | |
| 240 | + | |
| 241 | + | |
| 242 | + | |
| 243 | + | |
| 244 | + | |
| 245 | + | |
| 246 | + | |
| 247 | + | |
| 248 | + | |
| 249 | + | |
| 250 | + | |
| 251 | + | |
| 252 | + | |
| 253 | + | |
| 254 | + | |
| 255 | + | |
| 256 | + | |
| 257 | + | |
| 258 | + | |
225 | 259 | | |
226 | 260 | | |
227 | 261 | | |
| |||
968 | 1002 | | |
969 | 1003 | | |
970 | 1004 | | |
| 1005 | + | |
| 1006 | + | |
| 1007 | + | |
| 1008 | + | |
| 1009 | + | |
| 1010 | + | |
| 1011 | + | |
| 1012 | + | |
| 1013 | + | |
| 1014 | + | |
| 1015 | + | |
| 1016 | + | |
| 1017 | + | |
| 1018 | + | |
| 1019 | + | |
| 1020 | + | |
| 1021 | + | |
| 1022 | + | |
| 1023 | + | |
| 1024 | + | |
| 1025 | + | |
| 1026 | + | |
| 1027 | + | |
| 1028 | + | |
| 1029 | + | |
971 | 1030 | | |
972 | 1031 | | |
973 | 1032 | | |
| |||
1027 | 1086 | | |
1028 | 1087 | | |
1029 | 1088 | | |
1030 | | - | |
| 1089 | + | |
1031 | 1090 | | |
1032 | 1091 | | |
1033 | | - | |
| 1092 | + | |
1034 | 1093 | | |
1035 | 1094 | | |
1036 | 1095 | | |
| |||
0 commit comments