Skip to content

v0.10.4

Choose a tag to compare

@wdhwg001 wdhwg001 released this 04 Sep 05:04
· 31 commits to main since this release

A patch release for one reader defect the 0.10.3 ledger work exposed but did not fix.

A live transcript must never be memory-mapped by a poll loop

A memory map of a file that another process truncates faults with SIGBUS on the first page touched past the new end, and no error value catches it. The ledger's trace of Claude Code's transcript writer (claim MISC-020) shows two paths that rewrite a transcript in place: a rewind tombstone truncates the file and rewrites its tail, present since at least 2.1.191, and the local garbage collection rewrites the file on compaction when it is armed. wait mapped the whole transcript on every poll to read the few bytes appended since the last one, and the tail window that status and wait classify was mapped the same way. A rewrite landing inside one of those windows would have killed the process without a message. The hazard was measured before the fix: a one megabyte file mapped read-only, truncated to four kilobytes by a second writer, read once past the new end, and the reading process dies with signal 10 on macOS (signal 7 on Linux).

Both repeated readers now use plain positional reads. A shrink returns fewer bytes and nothing faults. wait also detects a transcript that is shorter than its baseline between two polls, moves the baseline to the new end and reports it in the activity line (transcript shrank N time(s) ... rewritten in place, baseline moved) and in JSON (activity.shrinks). Before, a shrunk file was skipped in silence. The one-shot full scans (search, stats, the background scan of status) keep the memory map the performance contract rests on, and the specification now states the hazard as it is: a sub-second map bounds the exposure, a polling map does not.

Verification

The suite runs 1042 unit tests and 550 end-to-end tests on every commit through the pre-commit gate, which also runs the ledger gate by name. Two new end-to-end tests drive wait against a transcript that is truncated below its baseline and then appended to (the pulse fires and the shrink is disclosed with its byte count) and against two appends landing in separate polls (both read once, no false shrink). A unit test pins the positional readers on a file another writer shrinks. Line coverage measured 95.79 percent (888 of 21092 lines missed), above the 0.10.3 baseline. A scoped mutation run over the four files touched tested 149 mutants: 116 caught, 18 killed by timeout, 3 unviable, 12 survived. Three of the survivors were real gaps in the new poll code (the shrink byte count and the cursor advance) and were killed by the tests above (a targeted re-run confirms). The other nine sit in previously classified equivalent zones (window alignment on a leading empty line, the adaptive poll cadence, a constant and a capacity hint). Ledger claim MISC-020 carries the SIGBUS probe as its instrument and records the fix.

Eight targets were built at the tag and ran the full suite on their own platform. The four Linux lanes (glibc and musl, x86_64 and arm64) ran in the official rust:1.89 containers, 1042 unit and 550 end-to-end tests each. The two Windows lanes (arm64 native, x64 emulated) ran on a Windows 11 ARM64 guest, 1042 and 549, the one missing test being the unix-only pid probe. The two macOS binaries were built with path remapping and smoke-tested. Every artifact was audited for machine-path strings and the Windows executables for a dynamic C runtime, all clean. The packaged documentation is byte-equal to the tag.