The defect is the false success, not the byte count. http_get returns carry=0 with http_status=200 after consuming a body tens of kilobytes short of the Content-Length it was given. A caller cannot distinguish a complete body from a 51% one, which makes HTTPS_BODY_TO_REU fetches untrustworthy rather than merely slow.
What this evidence can and cannot support. Every figure below is http_body_total, a 24-bit consumed count on the C64 side. None of them observes what the server sent, except in the offline runs where the listener's Content-Length is known by construction. They establish that the fetch is short and whether the shortfall is stable; they cannot by themselves distinguish "we stopped asking" from "we dropped what arrived".
Offline reproduction — local TLS listener, Content-Length, no chunking, no internet. PRG 1c828c4adbb7f9f58efa538045a170ebeb9b345d47eb4c4f01b2cd7247425807 (e4fda6d, BACKEND=uci USE_NISTCURVES_ONCHIP=1 HTTPS_SNI=www.foo.invalid), U64E @ 48 MHz:
| served |
http_body_total |
delta |
net_last_error |
carry |
| 40,000 |
40,000 |
0 |
$00 |
0 |
| 160,000 |
78,293 |
81,707 |
$86 |
0 |
| 160,000 |
not captured |
— |
$00 |
0 |
| 160,000 |
160,000 |
0 |
$00 |
0 |
It is intermittent, not size-dependent. The same 160,000 B body on the same PRG both failed and succeeded within minutes. An earlier reading of these numbers as a size threshold was wrong: the two runs differed in time as well as size, and time was the variable that mattered. A size threshold inferred from two runs at different sizes is unfalsifiable until one of them is repeated.
Intermittent silent truncation is worse than deterministic truncation, because no fixed-size test catches it reliably.
Live (wikipedia, chunked) — same-day curl anchor 125,703 B at 06:16:
| tree |
PRG |
consumed |
master 48657f5 |
012c7848… |
117,192 B |
e4fda6d |
0712def5… |
89,526 B |
e4fda6d (n=2) |
0712def5… |
73,720 B |
The anchor is a moving target — the article is user-editable — which is why it is quoted with its capture time. It does not weaken the finding: a 36 KB shortfall is not an edit, though a few-hundred-byte difference between runs may be. Two runs of the same PRG differ by 15,806 B, so these live counts characterise variance, not any difference between trees.
Both trees. The live wikipedia fetch truncated on master 48657f5 (PRG 012c7848…, 117,192 B against a same-day 125,703 B anchor) and on e4fda6d (PRG 0712def5…, 89,526 B and 73,720 B). The offline reproduction was run on e4fda6d only. Because the failure is intermittent, a single offline run on master would not have discriminated — a complete run there would not show absence — so it was deliberately not used as evidence. The supported statement is: the defect is observed on both trees, it is intermittent, and there is no evidence the bump introduced it.
Leads, recorded as leads — no cause is attributed:
- Chunked desync above 64 KB (CLAUDE.md,
http_recv_response). Excluded for the offline case: the local listener sends Content-Length, so no chunking is involved. Negative evidence for the live case too — neither live plateau sits at the boundary (89,526 is 24,010 past 65,536; 117,192 is 51,656 past).
- The
net_poll ring clamp that d9cd021 fixed. tcp_recv_overflow ($A248) is the one-byte discriminator: src/net/uci/net.s:387 sets it when the ring fills and then drops the delivery, commenting "The clamp at @do_poll makes this unreachable in practice … If it latches, the request/free-space arithmetic has regressed — that is the wikipedia-stall bug's signature." Not captured on any run above; instrumented now.
net_last_error = $86 correlates with the one captured short run and $00 with every complete one, but $86 is documented as appearing on passing runs (CLAUDE.md, github.com 2026-08-28), and the one $00 run at 160,000 B is the run whose count was not captured. Candidate, not cause.
State at the stall on the short run: tcp_recv_head == tcp_recv_tail == $0ACB (ring empty), uci_read_hdr = $FFFF (no-data sentinel), uci_status = '02,NO DATA: 11', tls_state=$00, tls_recv_progress=$05, tls_recv_sub_progress=$02.
Why this survived: the only rig exercising this path could not go red on it — see the companion issue on rig_https_banner.py.
Two method traps for whoever picks this up:
- Reading
http_body_total after the rig returns is unreliable. A post-hoc read returned all zeros including http_status, on a run the rig itself had read as 200. Wrap the rig's _dump_diag so the read happens while the transport is live.
- The listener's post-send
time.sleep(1.0) is not the cause. Checked and cleared: TCP orders FIN after queued data and sendall blocks on the C64's window.
The defect is the false success, not the byte count.
http_getreturnscarry=0withhttp_status=200after consuming a body tens of kilobytes short of theContent-Lengthit was given. A caller cannot distinguish a complete body from a 51% one, which makesHTTPS_BODY_TO_REUfetches untrustworthy rather than merely slow.What this evidence can and cannot support. Every figure below is
http_body_total, a 24-bit consumed count on the C64 side. None of them observes what the server sent, except in the offline runs where the listener'sContent-Lengthis known by construction. They establish that the fetch is short and whether the shortfall is stable; they cannot by themselves distinguish "we stopped asking" from "we dropped what arrived".Offline reproduction — local TLS listener,
Content-Length, no chunking, no internet. PRG1c828c4adbb7f9f58efa538045a170ebeb9b345d47eb4c4f01b2cd7247425807(e4fda6d,BACKEND=uci USE_NISTCURVES_ONCHIP=1 HTTPS_SNI=www.foo.invalid), U64E @ 48 MHz:http_body_totalnet_last_errorcarry$00$86$00$00It is intermittent, not size-dependent. The same 160,000 B body on the same PRG both failed and succeeded within minutes. An earlier reading of these numbers as a size threshold was wrong: the two runs differed in time as well as size, and time was the variable that mattered. A size threshold inferred from two runs at different sizes is unfalsifiable until one of them is repeated.
Intermittent silent truncation is worse than deterministic truncation, because no fixed-size test catches it reliably.
Live (wikipedia, chunked) — same-day
curlanchor 125,703 B at 06:16:48657f5012c7848…e4fda6d0712def5…e4fda6d(n=2)0712def5…The anchor is a moving target — the article is user-editable — which is why it is quoted with its capture time. It does not weaken the finding: a 36 KB shortfall is not an edit, though a few-hundred-byte difference between runs may be. Two runs of the same PRG differ by 15,806 B, so these live counts characterise variance, not any difference between trees.
Both trees. The live wikipedia fetch truncated on master
48657f5(PRG012c7848…, 117,192 B against a same-day 125,703 B anchor) and one4fda6d(PRG0712def5…, 89,526 B and 73,720 B). The offline reproduction was run one4fda6donly. Because the failure is intermittent, a single offline run on master would not have discriminated — a complete run there would not show absence — so it was deliberately not used as evidence. The supported statement is: the defect is observed on both trees, it is intermittent, and there is no evidence the bump introduced it.Leads, recorded as leads — no cause is attributed:
http_recv_response). Excluded for the offline case: the local listener sendsContent-Length, so no chunking is involved. Negative evidence for the live case too — neither live plateau sits at the boundary (89,526 is 24,010 past 65,536; 117,192 is 51,656 past).net_pollring clamp thatd9cd021fixed.tcp_recv_overflow($A248) is the one-byte discriminator:src/net/uci/net.s:387sets it when the ring fills and then drops the delivery, commenting "The clamp at @do_poll makes this unreachable in practice … If it latches, the request/free-space arithmetic has regressed — that is the wikipedia-stall bug's signature." Not captured on any run above; instrumented now.net_last_error = $86correlates with the one captured short run and$00with every complete one, but$86is documented as appearing on passing runs (CLAUDE.md, github.com 2026-08-28), and the one$00run at 160,000 B is the run whose count was not captured. Candidate, not cause.State at the stall on the short run:
tcp_recv_head == tcp_recv_tail == $0ACB(ring empty),uci_read_hdr = $FFFF(no-data sentinel),uci_status = '02,NO DATA: 11',tls_state=$00,tls_recv_progress=$05,tls_recv_sub_progress=$02.Why this survived: the only rig exercising this path could not go red on it — see the companion issue on
rig_https_banner.py.Two method traps for whoever picks this up:
http_body_totalafter the rig returns is unreliable. A post-hoc read returned all zeros includinghttp_status, on a run the rig itself had read as 200. Wrap the rig's_dump_diagso the read happens while the transport is live.time.sleep(1.0)is not the cause. Checked and cleared: TCP orders FIN after queued data andsendallblocks on the C64's window.