Skip to content

The log leaves the machine while the boot is still running - #439

Merged
Japabu merged 12 commits into
mainfrom
logstream
Sep 8, 2026
Merged

The log leaves the machine while the boot is still running#439
Japabu merged 12 commits into
mainfrom
logstream

Conversation

@Japabu

@Japabu Japabu commented Sep 8, 2026

Copy link
Copy Markdown
Collaborator

The ThinkPad on the bench answers only through a USB stick and a reboot into
Ubuntu. logd gains a second sink beside its file: a TCP connection through
netd to an address the boot parameter line names, carrying every record as the
exact text line /log gets, in order, the instant it is written, with the
connection's close meaning the boot ended.

How the address gets there. The kernel command line reaches no process, so
params::init copies logstream='s value out of it — before mm::init may
hand that memory out, into a fixed buffer, because there is no allocator yet —
and spawn_init puts it in /system/bin/init's environment. init passes its
own environment to the daemons it starts at boot and clears it for anything its
launcher starts, so logd reads it and a program a user runs does not. It is
information and not authority: the netd connector on logd's manifest row is
what stands between the address and a peer, and no other program in the image
asks for one. No ABI change.

The file stays the sink of record. A line goes to the volume first and is
offered to the stream after, and the offer cannot fail: it either enters a
bounded queue or is counted. The connection is opened and every byte written on
a thread of logd's own, because opening one waits on a DHCP lease, a SYN and a
peer. What the stream could not do — no netd, a refused SYN, a malformed
address, records the queue would not take — goes into the log itself, once, and
is never offered back to the queue: a report a starved queue refuses owes
another report, for the life of the boot.

toyos-logstream is the crate the three parties share (the kernel reads the
parameter and parses no address; logd parses the address and never sees the
parameter; the build system checks the name and boots no guest), and it holds
the queue's whole decision, pure and host-tested. Its bound is derived from the
constants it must hold — BATCH lines at MAX_RECORD_MESSAGE plus what
Tagged renders around a message — with a const assertion and a host test
that fills it with a whole batch of the widest lines. blackbox= and
logstream= are VALUED_PARAMS in src/build.rs: matched as prefixes,
selecting no kernel build, cleared by name before an image is flashed and — for
the one this build can read — parsed there, so a typo is refused by the gate and
not by a machine on the bench.

The two checks

  • Negative control, the sink — the whole sink reverted onto this tree
    (Stream::start answering None, nothing else touched): all five
    registrations red, on five distinct signatures. log_stream and log_stream_e1000e on "Boot: complete" never arrived on the stream in 90s: 0 connection(s), 0 line(s),
    log_stream_no_listener on the file carries 0 line(s) about a log stream that never opened, log_stream_unreachable on cost it no record it admits to; the file has 4272 line(s), log_stream_stalled_peer_delivers_whole_records on the stream was still open 20s after the guest went down; 0 line(s) received. The
    parameter path the control does not revert has its own two host tests:
    the_pre_flash_gate_parses_the_address_it_clears over every kind of typo, and
    a_valued_parameter_is_one_the_kernel_claims_by_prefix, which reads
    params::claims's own body.
  • Negative control, the netd send path — the bridge_piped hunk reverted
    alone, three runs and six guests counting the harness's re-run-alone: every
    one red on a cut record with a whole one behind it (the stream carries "[…] logstorm t=3 i=739 k=… zabcdefghi…", which /log does not carry after its line N). log_stream_stalled_peer_delivers_whole_records releases the stalled peer while the
    machine is still up, so a byte the machine consumed and did not deliver is
    visible wherever it happened rather than only at the connection's end.
  • MutationBacklog::admit no longer counting what it refuses: reds
    log_stream_unreachable and log_stream_stalled_peer_delivers_whole_records (a log storm offered to a stream that could not take it cost it no record it admits to) and three of toyos-logstream's ten host tests
    (a_starved_queue_drops_the_newest_and_counts_every_one,
    a_drop_report_is_never_a_line_the_queue_is_offered,
    one_impossible_line_does_not_evict_the_boot).
  • Independent oracle — the guest's own log volume, every file this boot
    wrote, read off the FAT partition behind the guest's back and compared with
    what arrived over the wire: 230 lines on virtio and 232 on the e1000e, equal
    in content and in order; on the stalled arm, 636 received lines each /log's
    own in /log's own order. The storm arms' second oracle is the report's own
    arithmetic — the per-run counts must sum to the boot total the last line
    states.

Measured on the dev host

  • Boot: complete reached the host 27 ms (virtio) and 35 ms (e1000e) into the
    listener's life, with the guest still running; a job's exit: record 29–32 ms
    after the job ended.
  • A peer that accepts and stops reading is what the writer's own backpressure
    needs, and a narrow storm does not reach it: a 2 MiB kernel pipe and netd's
    64 KiB send buffer absorb the 674 KiB (4,213 lines) a log-storm at --smp 8
    produces. log-storm-wide widens each record to nearly a whole message.
  • How many lines a stall costs is not a number any arm asserts. It is the
    pipe's size, netd's, and whatever QEMU's user-mode networking holds between
    them; two hosted rounds refused two different estimates of it.
    log_stream_stalled_peer_delivers_whole_records asserts what it forces on any
    host instead: the connection opened, the peer took a storm's worth of records
    while reading nothing and then read again, and every line it received is a
    whole record /log carries in /log's own order, at every position including
    the last. Where the boot does say it refused something, the report is still
    checked against itself. The drop accounting is toyos-logstream's host tests,
    which red under the drop-count mutation with no guest at all, and
    log_stream_unreachable, whose address answers nothing and so must lose
    lines.
  • log_stream_unreachable, on the same footing: ~4,075 storm records in /log
    and 3,732–3,762 lines refused across runs, said in 5 lines whose per-run
    counts sum to the last line's boot total.
  • cargo test: 337 passed, 337 total. cargo test --lib: 261 passed, 1
    ignored. cargo test --workspace --exclude toyos-build: 1,295 passed, green.
    cargo run -- --clippy: 5 invocations clean.

What this does not do

  • The metal half — a listener on the Mac while the T14 boots — is left to build;
    the track file carries what a reader would otherwise re-derive.
  • Four of the five are priced and relegated. Run 34185896772's
    test-durations-merged puts log_stream at 24,865 ms, log_stream_e1000e at
    25,219, log_stream_no_listener at 25,182 and log_stream_unreachable at
    25,707 — every one over FAST_CEILING_MS, so each is a Why::Cost row in
    src/tiers.rs and Tier::Nightly in its registration, with the row saying
    what stops being gated per pull request. What costs the price is the image:
    the host picks the listener's port and the port is on the parameter line, so
    each arm builds a boot image nothing can memoize — about 20 s of a 25 s run
    with no assertion behind it, and that is what would return them. The fifth,
    log_stream_stalled_peer_delivers_whole_records, is priced at 34,591 ms from
    run 34189903124 and joins them.
  • Compromises found and filed rather than fixed here:
    issues/design-debt/a-boot-with-no-log-volume-streams-nothing.md,
    issues/design-debt/logd-holds-a-netd-connector-on-every-boot.md,
    issues/design-debt/the-parameter-lines-value-is-read-in-two-crates.md, and
    the receive half of
    issues/kernel/netd-drops-what-the-clients-pipe-would-not-take.md. The send
    half of that last one is fixed here: log_stream_stalled_peer_delivers_whole_records
    reproduced it as a record cut in half on the wire — the record stream's own
    oracle failing — and reverting the fix reds that arm.

🤖 Generated with Claude Code

The ThinkPad on the bench answers only through a USB stick and a reboot into
Ubuntu; nothing a program prints reaches the harness. `logd` gains a second sink
beside its file: a TCP connection through netd to an address the boot parameter
line names, carrying every record as the exact text line `/log` gets, in order,
the instant it is written, with the connection's close meaning the boot ended.

The address travels the one channel that exists. The kernel command line reaches
no process, so `params::init` copies `logstream=`'s value out of it — before
`mm::init` may hand that memory out, into a fixed buffer, because there is no
allocator yet — and `spawn_init` puts it in `/system/bin/init`'s environment,
which every program inherits. It is information and not authority: the `netd`
connector on `logd`'s manifest row is what stands between the address and a
peer, and no other program in the image asks for one.

The file stays the sink of record and nothing here may cost it one. A line goes
to the volume first and is offered to the stream after, and the offer cannot
fail: it either enters a bounded queue or is counted. The connection is opened
and every byte written on a thread of `logd`'s own, because opening one waits on
a DHCP lease, a SYN and a peer, and the loop's next act is a batch of records
going to a file. What the stream could not do — no netd, a refused SYN, a
malformed address, records the queue would not take — is written into the log
itself, once, because a `say!` reaches the console and no record and would be a
failure only somebody watching the wire could see.

`toyos-logstream` is the crate the three parties share: the kernel reads the
parameter and parses no address, `logd` parses the address and never sees the
parameter, and the build system checks the name and boots no guest. The queue's
whole decision lives there too, pure and host-tested, so the accounting a
starved peer forces is checked where it costs milliseconds. `blackbox=` and
`logstream=` are now `VALUED_PARAMS` in `src/build.rs`: matched as prefixes,
selecting no kernel build, and cleared by name before an image is flashed —
which is exactly where one is asked for.

Four registrations, on the harness's own listener over slirp: the stream over
the virtio NIC and over the e1000e, the file's one-line refusal when nothing is
listening, and a `log-storm` offered to an address that answers nothing, whose
drop counts the harness adds up and checks against the boot's own total.

The negative control is the whole sink reverted onto this base — `Stream::start`
answering `None` — which reds all four. The mutation is `Backlog::admit` no
longer counting what it refuses, which reds `log_stream_unreachable` and two of
the crate's host tests. The independent oracle is the guest's own log volume,
read off the FAT partition behind the guest's back and compared with what
arrived over the wire line for line: 230 and 232 lines, equal in content and
order, on two different NIC drivers.

Measured on the dev host: `Boot: complete` reached the host 25-60 ms into the
listener's life with the guest still running, and a job's `exit:` record 26-33 ms
after the job ended. A stalled peer's backpressure does not reach the queue — a
2 MiB kernel pipe and netd's 64 KiB send buffer absorb 674 KiB of storm first —
which is why the drop arm stages a peer that answers nothing rather than one
that reads slowly.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GGQ2H2aCwd1jfiNmjsiUvz
@Japabu
Japabu marked this pull request as ready for review September 8, 2026 01:47
@Japabu
Japabu enabled auto-merge September 8, 2026 01:47
@Japabu
Japabu disabled auto-merge September 8, 2026 01:48
Japabu and others added 9 commits September 8, 2026 04:16
`issues/README.md`'s area list is closed and carries no `network`. The defect
is a discarded return value in an OS server, which is what `issues/kernel/`
already holds for `soundd` and the compositor, so it goes there. No file
anywhere cites the old path.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GGQ2H2aCwd1jfiNmjsiUvz
…red back

Three findings from the review of the record stream, all in the queue and its
one caller.

`MAX_BACKLOG_BYTES` claimed to hold one whole `SYS_LOG_READ` batch and did not:
64 KiB against 64 records that render to at least 1,026 bytes each is 128 bytes
short before a `tid=` widens one. It is now the product of the constants it has
to hold — `BATCH` and `MAX_RECORD_MESSAGE` plus what `Tagged` renders around a
message — with a `const` assertion holding the claim and a host test that fills
it with a whole batch of the widest lines. `BATCH` moves into the crate the
bound is in, so the two are one declaration and `logd` reads it from there.

A drop report was written to the file *and* offered to the stream. On a queue
that is refusing it is refused too, which is a drop, which owes another report:
a run that never ends and a log that fills with lines about itself. The offer
and the report are now one function, `Backlog::round`, which is the only place
that mistake can be made; `a_drop_report_is_never_a_line_the_queue_is_offered`
reds on the old behaviour, as do two of the tests beside it. `logd` carries what
the stream owes across the round and clears it only once the file has taken it,
so a refused write leaves the report owed rather than losing it.

`Backlog::take` and `Backlog::dropped` had no caller outside the crate's own
tests and are gone; `admit` is private behind `round`, so there is no discarded
return left at the offer. `write_all` carries its error out instead of
collapsing it to `false`, and the line a stopped stream writes names it.
`say`'s unreachable "do not overwrite" guard is gone with the second writer it
was guarding against.

Deleted, not rewritten: the crate's and `stream.rs`'s copies of the stalled-peer
measurement, the "why a crate" narration, the doc lines restating the four-line
bodies under them, the `Cargo.toml` essay, and the claim that every program
inherits the address — `init` clears the environment for everything its launcher
starts, so it is the boot daemons that inherit it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GGQ2H2aCwd1jfiNmjsiUvz
…'s own list

`flashable_params` accepted `logstream=<anything>` by prefix and never called
`toyos_logstream::endpoint`, although the crate is a direct dependency of this
one and the gate's own doc says a flashed stick is exactly where the parameter
is asked for. A typo passed and was discovered by a machine on the bench that
streamed to nothing — the round trip the record stream exists to remove. It is
parsed here now and refused by name, with a host test over every kind of typo
the parser distinguishes.

`VALUED_PARAMS` was a second declaration of what `kernel/src/params.rs`'s
`claims` matches by prefix, with nothing holding the two together, in the
fail-open direction: a name here that `claims` does not match writes an image
`actuator::init` panics on. Each row now carries the path the kernel names the
constant by, and `prefixes_claimed` reads the kernel's `claims` body so the two
lists are asserted equal — the same shape `params_of` gives the flag table.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GGQ2H2aCwd1jfiNmjsiUvz
`bridge_piped` read 4 KiB out of the client's tx pipe and handed it to
`send_slice`, discarding the count. `send_slice` takes fewer than it is offered
when the send buffer is short of room, and the pipe had already given those
bytes up: the peer's stream was short in the middle with nothing saying so.

The filed defect had never fired, because no arm had put a peer under the 64 KiB
buffer's own bound. `log_stream_stalled_peer_wide_storm` does, and it fired at
once: a record arrived cut off mid-payload with the next record's line beginning
inside it, on a peer that had stopped reading. That is the record stream's whole
oracle — what a listener received is the guest's own log file, line for line —
failing, so it is fixed here rather than filed again.

The read is now bounded by `send_capacity() - send_queue()`, so nothing leaves
the pipe without landing in the socket, and the assertion beside it says so. The
receive direction has the same shape and is not fixed: it needs the room left in
the client's pipe, which no syscall answers. The issue stays open on that half
and names this arm as the reproduction to point at it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GGQ2H2aCwd1jfiNmjsiUvz
The review's point: `log_stream_unreachable` never enters the write loop. Its
address answers nothing, so `open` retries to its bound and returns, and the
queue fills because nothing ever drained it. That is the accounting under a
stream that never opened, and it is real — but the path `stream.rs` is built
around is the other one: a peer that took the connection and stopped reading, a
closed window, netd holding the pipe, the writer blocked inside `write_all`, and
the queue above it refusing.

`log_stream_stalled_peer_wide_storm` stages that. The listener accepts and reads
nothing until the machine is down, then drains what piled up, because a peer
that never reads says nothing about what reached it. A narrow storm does not
get there: 4,213 lines at 674 KiB is less than the 2 MiB pipe alone, which is
what the earlier attempt measured. So `log-storm-wide` widens each record to
nearly a whole message and one boot outweighs the buffers — 556,348 bytes over
the wire in 631 lines before the queue refused 1,093 more, said in 4 report
lines, with every received line /log's own in its own order.

The arm asserts the writer wrote: more bytes reached the peer than the queue
alone can hold, so the refusals are the writer's backpressure and not a stream
that never opened. Its last line may be a prefix of the file's, because the
connection ends wherever the writer was when the machine went down; nothing
before it may be, which is how the netd truncation was caught.

Two things the arms needed. The oracle reads every log file this boot wrote and
not only the newest: a boot that logs enough to starve a stream logs enough to
rotate, and a rotation is not a hole. The drop report's two numbers are read by
the report's own words rather than by position, so the line's shape is not what
the accounting rests on. The self-consistency check both storm arms run is one
function now.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GGQ2H2aCwd1jfiNmjsiUvz
…ses are filed

Deleted rather than rewritten, with the sites that cited them:

- "the address is ambient information every program inherits", in `system.toml`
  and both case manifests, `kernel/src/params.rs` and `kernel/src/loader/mod.rs`.
  `init` calls `env_clear` on everything its launcher starts, so what inherits
  the address is the boot daemons, and only one of them can act on it.
- `[programs.logd]`'s "its whole authority is `logread`", which the `receives`
  row two lines below had already made false.
- The track file's metal-half bullet, which was a design. `issues/README.md`:
  a track does not carry one. The measured constraint stays, as ruled: the
  buffers between `logd` and a peer, the storm that does not outweigh them, and
  which way the arm goes instead. 703 words to 627.
- The harness's "staging it took two tries" investigation, the six lines
  narrating why `empty_dir_stat` was picked for a list, and the doc paragraphs
  restating the constants and functions under them.

Filed rather than left in a handoff:

- `issues/design-debt/a-boot-with-no-log-volume-streams-nothing.md` — the stream
  mirrors the file, so a machine whose stick is dead streams nothing, which is
  the case the cable is worth most in.
- `issues/design-debt/logd-holds-a-netd-connector-on-every-boot.md` — the
  authority is static in the manifest while the feature is a boot parameter.
- `issues/design-debt/the-parameter-lines-value-is-read-in-two-crates.md` —
  `value_in` is `toyos_abi::boot::root_uuid`'s body; the reading belongs there,
  and an ABI commit may not share this branch.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GGQ2H2aCwd1jfiNmjsiUvz
`prefixes_claimed` has no caller in a build — it exists so the two lists of
valued parameters can be asserted equal — so it is behind `cfg(test)` rather
than dead in every shipped binary. The refusal test asks the gate about a slice
of the name it already holds.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GGQ2H2aCwd1jfiNmjsiUvz
… past the cut

The round-2 finding: `90488029` shipped with no check that fails without it.
Reverting it alone left `log_stream_stalled_peer_wide_storm` passing, because
netd's discard lands on the last line the peer receives — the send buffer runs
short of room exactly once, when `can_send` is about to go false — and
`is_subsequence_of` allowed the last line to be a prefix, since a connection
that ends with the machine can end inside a line.

So the arm no longer ends at the stall. It waits for the boot's log to pass
three mebibytes — `logd` rotates every `MAX_LOG_BYTES` and offers each line as
it writes it, so the third rotation is three mebibytes offered against the
2.7 MiB the pipe, netd's send buffer and the host's receive window hold between
them — and then releases the peer and keeps the machine up until the backlog it
was holding has gone past. Every byte the stall cost is then in the middle of a
stream that continues, so `is_subsequence_of` is strict: every received line is
a whole record equal to the file's, at every position including the last.

Negative control, measured: the netd hunk reverted alone, three runs, six guests
counting the harness's re-run-alone, all red on a cut record with a whole one
behind it — `the stream carries "[…] logstorm t=3 i=739 k=… zabcdefghi…", which
/log does not carry after its line N`. With the fix: 3,425,152 bytes in 3,637
whole lines, each `/log`'s own in `/log`'s own order, and 563 lines the queue
refused while the peer was stalled.

Three more from the same review. `send_slice`'s `Err` was collapsed into a short
send by `unwrap_or(0)` at the one site the commit exists to stop swallowing an
error — it panics by name now, because the bytes are already out of the pipe
either way. `want == 0` no longer rests on an unstated smoltcp invariant: asked
for no bytes the loop has nothing to do, and does not read a zero-length answer
as the client hanging up. And `logd` drops the reports the file took one by one
rather than clearing them all or none, so a write refused partway leaves the
rest owed and none of them written twice.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GGQ2H2aCwd1jfiNmjsiUvz
`MAX_BACKLOG_BYTES = BATCH * WIDEST_LINE` and `WIDEST_LINE = MAX_RECORD_MESSAGE
+ AROUND_A_MESSAGE`, so the `const` assertion `MAX_BACKLOG_BYTES >= BATCH *
(MAX_RECORD_MESSAGE + AROUND_A_MESSAGE)` was `x >= x`, and the host test admitted
`BATCH` lines of exactly `WIDEST_LINE` into a queue of exactly `BATCH *
WIDEST_LINE`. Measured: `AROUND_A_MESSAGE = 1` left both green. Any compile-time
claim about a value defined as that product is a tautology, so the assertion is
deleted rather than reworded.

The claim that needed checking is the one about `toyos_abi::log::Tagged`: that
128 is above everything it renders around a message. The test renders it — a
`LogRecord` with `at_ns`, `tid`, `cpu` and `elided` at their types' maxima, the
early flag set, a full-width message, tagged with the wall-clock stamp `logd`
writes — and asserts the line fits `WIDEST_LINE` before filling a whole batch
with it. The true worst case is 92 bytes: the test is green at
`AROUND_A_MESSAGE = 92` and red at 91 and at 1.

Also deleted, in `tests/netcase/system.toml`: "its whole authority is `logread`",
which the `receives = ["netd"]` two lines below had already made false and which
the round-1 answer corrected in `system.toml` alone.

The netd issue's slug says what its body says — the smoltcp half is fixed and
the file is about `write_nonblock` into the client's pipe — renamed in the
commit before this one, where it was carried in by a `git mv` staged with it.
No file cites either name.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GGQ2H2aCwd1jfiNmjsiUvz
@Japabu
Japabu enabled auto-merge September 8, 2026 04:32
Japabu and others added 2 commits September 8, 2026 07:01
…e priced

CI shard 5 red the stalled arm twice, both `cost it no record it admits to`. The
buffers below the stream absorbed the whole storm on that host: this arm bounded
the guest's — 2 MiB of pipe and netd's 64 KiB — but not the runner's own receive
window, which a hosted Linux auto-tunes into the megabytes for a peer that is
not reading. A backpressure test that does not own the buffer it fills measures
the runner.

It owns it now. The listener holds its receive buffer to `STALLED_PEER_WINDOW`
with `SO_RCVBUF` on the listening socket, before any connection, which is where
an accepted one inherits it and where an explicit size stops the auto-tuning.
`BELOW_THE_QUEUE` is then every buffer between `logd`'s queue and the peer as a
number this test knows — the pipe, netd's send buffer, twice the window because
Linux charges overhead against it, and half a mebibyte for what QEMU's user-mode
networking holds, which is the one measured term: a boot that reached the mark
having refused 375 records puts all of it at 2.65 MiB. `OFFERED_BY_THE_MARK` is
what one boot has written by `logd`'s third rotation, and a `const` assertion
holds it above `BELOW_THE_QUEUE`, so from the mark on whatever the machine
offers can only be refused. The job runs after the mark rather than before it,
so the window in which refusals pile up is an event and not a sleep: 885, 947
and 949 refusals across three runs where releasing at the mark itself gave 375.

Renamed to say which lever, as ruled: `log_stream_stalled_peer_storm_over_a_
bounded_window`. Its negative control still holds — the netd `bridge_piped` hunk
reverted alone reds it on both runs, each on a cut record with a whole one behind
it.

**Priced from run 34185896772's `test-durations-merged`, and relegated.**
`log_stream` 24,865 ms, `log_stream_e1000e` 25,219, `log_stream_no_listener`
25,182, `log_stream_unreachable` 25,707 — every one over `FAST_CEILING_MS`, so
four `Why::Cost` rows and `Tier::Nightly`, never a re-run. What costs it is the
image: the host picks the listener's port and the port is on the parameter line,
so each arm builds a boot image nothing can memoize, about 20 s of a 25 s run
with no assertion behind it. Each row says what stops being gated per pull
request. The renamed arm carries the `UNMEASURED` marker for one cycle, which is
the documented bootstrap: its own price puts it there too.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GGQ2H2aCwd1jfiNmjsiUvz
Two hosted rounds said the same thing: the buffers between `logd`'s queue and a
peer are not a number this arm can compute. It bounded the guest's and then the
peer's receive window, and a hosted runner still absorbed a whole storm without
the machine refusing a line. A third estimate would be a third guess.

So the arm demands no refusal. What it forces on any host is what it now
asserts: the connection opened, the peer took a storm's worth of records while
reading nothing and then read again, and every line it received is a whole
record — `/log`'s own, in `/log`'s own order, at every position including the
last. That is the claim netd's discarded short send breaks, and reverting that
hunk alone reds this arm on three runs and six guests, each on a record cut in
half with a whole one behind it. How many lines a stall costs is the pipe's
size, netd's, and whatever QEMU's user-mode networking holds between them, and
none of those is this arm's to demand.

The accounting keeps its own gates. `toyos-logstream`'s host tests carry it —
three of them red under the drop-count mutation, with no guest and no host's
buffers — and `log_stream_unreachable` still requires a refusal, because a
stream whose address answers nothing drains never and a boot that lost nothing
there measured nothing. Where this arm's boot does say it refused something, it
is still checked against itself: `refusals_in` splits "how it adds up" from
"whether it happened", and only the second was ever a fact about a host.

Renamed to what it asserts: `log_stream_stalled_peer_delivers_whole_records`.
The buffer arithmetic and the rotation wait went with the demand they existed
for; the peer's held window stays, because a window that closes promptly is what
puts netd's send buffer under its own bound and the writer inside a blocking
write on every host.

Priced from run 34189903124's `test-durations-merged` at 34,591 ms — the run
where the arm was red on the assertion this commit removes, over a boot that did
the same work — which is over `FAST_CEILING_MS`, so it is a `Why::Cost` row and
`Tier::Nightly` beside its four siblings.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GGQ2H2aCwd1jfiNmjsiUvz
@Japabu
Japabu added this pull request to the merge queue Sep 8, 2026
Merged via the queue into main with commit 01f6dd2 Sep 8, 2026
24 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant