Change Ledger
One row per change, not per commit. The pusher declares which versions are live; this walks their ancestry, newest first, and bands each change by what this server can actually read. Nothing here is merged, rewritten, or picked for you.
1 declared head · 1296 changes walked
- the repo owner can decline a proposal: propose::decline beside withdraw, POST /propose/decline, and loot propose --decline <change-id>, the flag named as its siblings on the verb each spell their act. a declination is signed over the repo, the change id and the tip, and tagged Act::ProposalDecline so it can never be read as a bound withdrawal or a read; on the wire it is a bound withdrawal plus the tag byte, and neither format constant moves. the owner is the key require_pusher admits, the rule jobs already asks for an approval; a member reads the proposal and is refused in words naming the author withdrawal as the other route, and a key the read gate conceals the row from gets one sentence whether the row exists or not. may_propose is not asked, so a shut door does not stop a decline; a repeated decline is the same event and is not re-dated, a landed row refuses by name, and a withdrawn row refuses as the withdrawal refuses a declined one. a declination replayed after a revised re-proposal is refused by the tip binding, the same stack re-proposed is declined again, and one signed for another repo of the same owner is refused by the repo binding. the CLI signs the tip the forge reports at the moment of declining and prints it. uncontended, a decline is one close_proposal and no graph read, pinned on the store call log. it touches no job made for the declined tip, which ADR 0091 leaves undecided. ADR 0075 records the build and what a decline does to the bytes, with CONTEXT.md and the forge spec. red under mutation, counts read each time, each 0 passed and 1 failed and then 1 passed restored: the owner check skipped, the visibility gate skipped, the repo binding removed, the tip binding removed, a declined row refusing re-proposal, a landed row declinable, a withdrawn row declinable, the decline walking the stack, the route asking may_propose, the act tag dropped, the tip left out of the signed bytes, and the declination written under its own minor. no migration and no format byte move; the forge binary changes and an older forge answers 404 on /propose/decline, so this owes a forge deploy before the flag works against the live host. the workspace suite is green (4376 passed over 139 binaries, 12 ignored), and green under bash ci/local.sh against Postgres 18 (#2176)
- ↳ supersedes
6ffacfde· not stored here - Kept, not rewritten: a carry replays a suffix as a new version and the one it replaced stays addressable. That is the difference between a carry and a rebase.
nothing readable · 13 sealed · 13 internal - ↳ supersedes
- a merged-tree re-gate that fails stops asserting a cause it cannot know: regate_remedy no longer says the change is green on its own and breaks combined, and says instead what the gate knows, that the merged tree failed it after the tree of the lane itself got through the pre-land pass, naming the combination and a run that failed for a reason outside the tree (a Windows file lock, a timeout under load) as causes it cannot tell apart; its recovery is ordered, re-run what failed alone and read the count, change nothing and re-land if it passes, and only if it fails again the loot edit procedure. the finding stays a finding: a test ran and failed, so the kind was right and the claim was wrong. the cargo test gate now names each failing test libtest reported under the unchanged pre-land cargo test failed heading, through hygiene::failed_tests, and beside a test whose own captured block carries a spelling in hygiene::SHARING_OS_ERRORS it says Windows returns that when another handle holds the file and that a lock and a defect both produce it (#2233 was a defect), deciding nothing on it: no retry and no skip. the plain pre-land finding and the merged finding are pinned whole over the #2079 shape, concurrent_stage_of_same_address_does_not_tear red on os error 5. red under mutation, counts read each time: the old ADR 0055 story restored (2 passed and 1 failed), cargo_test_once naming nothing (0 and 3), the first-run finding without its names (1 and 2), the re-run finding without its names (2 and 1), a lock-shaped failure re-run (1 and 2), the sharing error searched over the whole stdout (hygiene 1 and 1), the block not stopped at the next test header (1 and 1), the code matched without its closing paren (0 and 2), the FAILED line matched mid-line (1 and 1), each restored to green. ADR 0055 gains a #2310 amendment, and workflow.md and the land-change skill carry the ordered recovery and the failed lines. no migration, no format byte and no published wording moves, so this owes no deploy, but a land runs the primary release loot-first, so this is live only once those binaries are rebuilt. the workspace suite is green (4356 passed over 137 binaries, 12 ignored) (#2310)
- ↳ supersedes
a68004ce· not stored here - Kept, not rewritten: a carry replays a suffix as a new version and the one it replaced stays addressable. That is the difference between a carry and a rebase.
nothing readable · 6 sealed · 6 internal - ↳ supersedes
- the review-sweep fix-up over #2303, #2159 and #2172. the wake-up sender: its client is built with no_proxy, so a proxy named in the environment is not used, pinned in a test binary of its own that sets the proxy variables and sees the request reach the pinned address and not the proxy; refused_wake_address now refuses every address that is not globally routable unicast, an ipv6 address carrying an ipv4 one (mapped, compatible, nat64 64:ff9b::/96, 6to4) judged by the one it carries, so 64:ff9b::a9fe:a9fe is refused, with the refused blocks copied from the iana special-purpose registries at the code and a table of example addresses pinning the rule; name resolution runs on a thread waited on for at most WAKE_TIMEOUT_SECS with the request given what is left, and the wake-ups in flight are bounded by MAX_WAKES_IN_FLIGHT, a resolver that outlives its wait keeping its place until it returns. the ingest kinds trailer is now a byte per head in head order, still absent when no head declares a kind, so each head is given jobs for its own kinds only and no job is made for a kind a head has no step of; the pre-2159 decoder still reads the new payload, the one-byte union shape was in no release, and neither format constant moves, pinned against the marker put_version writes rather than its digits. MetadataStore::ingest answers the jobs its commit wrote (store::Ingested) and a push wakes runners for those alone, so a triple a racing request filed first wakes nobody here; the proposal job half answers no error once the proposal is committed and logs instead. the job_cost no-kind pin now states the property, that the job half calls the store for nothing when nothing is declared, instead of a list of methods. the seek busy marker: one that does not parse is believed until its mtime is past the window, so the instant between its creation and its clock no longer admits two holders, and a marker that cannot be created for a reason other than one standing there is retried and then refused naming the state rather than counted as held; the held-marker comment is scoped to a round that claimed it. the census wire half plants each oracle into every payload sent builds and asks each payload for moving bytes, calls the now-public verbs::sync::forge_declaration instead of a copy, records that the fixture declares no kinds, and records the measured flake rate of its minimum reading (200 runs: 40 of 1600 store builds and 15 of 1600 wire builds above the floor, none red). false sentences fixed: jobs.rs says what a false proposal declaration costs the owner and what the trust floor does and does not stop, ADR 0091 section 7 and its 2159 amendment are corrected with a 2307 amendment and the reads-not-writes scope of the job cost measurement, ADR 0090 and ADR 0004 gain corrections, CONTEXT.md names the per-head trailer, the census module doc states the property instead of naming places, and an in_progress rewrap leftover is rewrapped. red under mutation, counts read each time, each restored to green: no_proxy dropped (0 passed and 1 failed), the 2159 refusal rule restored (2 and 2), embedded forms unrecognised (3 and 1), 6to4 unrecognised (3 and 1), the resolver thread not holding its place (0 and 1), the ceiling ignored (0 and 1), the resolve wait unbounded (0 and 1), the trailer encoding the union (25 and 2), the forge unioning kinds across heads (1 and 3), the memory ingest answering every handed job (4 and 2), the push waking for planned jobs (5 and 1), the proposal refusing after a failed job write (3 and 2), the job half reading with nothing declared (3 and 1), an unparseable marker read as idle (1 and 1), a failed create counted as held (1 and 1), the census reading the bundle alone (9 and 4), every payload a copy of the bundle (10 and 3), the ingest payload emptied (11 and 2). no migration, no format byte and no published wording moves; the forge binary changes, so the sender and per-head jobs are live once the forge is redeployed. the workspace suite is green (4360 passed over 138 binaries, 12 ignored), and green under bash ci/local.sh against Postgres 18 (#2307)nothing readable · 25 sealed · 25 internal
- adr 0004 is now asked of what a repo encodes to send as well as of what it persists: plaintext_equality_census gains a wire half, which keeps per build the payloads its sent function has the fixture repo encode through the public Workspace doors loot bundle and loot push call (the full bundle, the /stow batches a push to an empty host sends, closed at one object a batch so the first-batch delta framing runs, and the /ingest payload around the metadata-only declaration bundle), and takes the same value, shape and repetition readings over them between the equal and distinct worlds; nothing was exported for it, since the ticket premise that a bundle is unreachable from the test crate was wrong. a premise test opens every object the payloads carry with the key riding beside it and holds each world to the plaintexts it records, a moving-bytes control refuses payloads that never change between builds, and three oracles are planted on the wire: the deleted identity_hash itself, an address-only grouping and a per-build-salted digest. the wire is clean: nothing plaintext-derived was found. the repetition reading, shared with the store half, now counts occurrences of repeated windows rather than distinct ones, because the payloads repeat content and a chance collision merged two repeated windows and lowered the count the minimum keeps: at the distinct count the wire half read red in 4 of 25 runs, at the occurrence count 60 of 60 green. red under mutation, counts read each time: the bundle producer appending a domain-separated blake3 of each carried plaintext (census 11 passed and 2 failed, while engine bundle_carries_no_plaintext_equality_oracle reports 1 passed and 0 failed beside it), sent building nothing (8 and 5), the push batches carrying no object (12 and 1), the value reading removed (11 and 2), the shape reading removed (11 and 2), the repetition reading removed (11 and 2), each restored to green. the forge question is answered in the new adr 0004 amendment: /stow and /ingest decode the same Frame and /ingest takes encode_ingest_payload, so a forge input is what the wire half reads, while what a forge or relay keeps of it is its own store and is not asked; the engine pin doc scopes it to the one value it searches for, and CONTEXT.md names the wire half. no migration, no format byte, no wire byte and no published wording moves, so this owes no deploy. the workspace suite is green (4351 passed over 138 binaries, 12 ignored) (#2172)nothing readable · 5 sealed · 5 internal
- the kinds trailer on /ingest and /propose and the job creation it drives, with the wake-up sender; the runner-deposit reap is not built. loot_net::forge::HeadDeclaration and ProposalClaim gain kinds: a push declares the kinds of the on main steps of its live heads and a proposal the kinds of the on change steps of its tip, read by pipeline::declared_kinds from the .lootpipeline each version holds, as one trailing byte in Kind::ALL order that is not written when nothing is declared, so a payload declaring nothing is byte-identical to the one an older client signs; a decoder reads the byte iff bytes remain, and a bit this build cannot name is dropped. FORMAT_MINOR is not bumped, per the 2026-09-22 comment on the ticket that overrides step 1 of its body (a minor bump makes persist_codec rewrite every store graph file, the #2180 finding), and adding_the_kinds_moves_neither_format_constant pins both constants; a new client payload decodes on a frozen copy of the old decoder and an old client payload decodes here as declaring nothing. /ingest makes a main job per declared head and declared kind, less those on file, inside the ref-moving transaction (IngestTxn::jobs on both stores, with a conformance case run on memory and on Postgres), and /propose makes a change job per declared kind for the tip once the proposal row stands; member is the author of the version classified by AccessClass::of and approved is a verifying pipeline/approve from a key require_pusher admits, both read once at creation. after the commit each live runner whose row covers a created job kind and carries a URL is woken through jobs::Wake, whose HttpWake calls loot_net::runners::send_wake: the URL checked as registration checks it, the send refused if any resolved address is one registration would refuse (refused_wake_address, which both ask), the checked address pinned, no redirect followed, the request bounded by WAKE_TIMEOUT_SECS. the ticket said the site TypeScript client learns the field, and no code under site or sdk encodes an /ingest or /propose payload, so nothing there changed. the reap is recorded as unbuilt in the ADR 0091 #2159 amendment: grant_inbox is keyed by recipient alone while runner, ref_head, proposal and job are bound to one repo under migration 0017, so a reap run per repo cannot see an address live in another repo the same runner key serves, and how to scope it is a decision left to the reap ticket; nothing writes a runner deposit before #2130. measured: the trailer is one byte when a kind is declared and none otherwise, and ingest::job_cost pins that a push declaring nothing makes no call for jobs and that job creation reads per declared head, not per kind. red under mutation, counts read each time, each 0 passed and 1 failed on a filter selecting its one pin unless said otherwise: the trailer written before the heads, the trailer read unconditionally, the trailer always written, FORMAT_MINOR at 1, every kind made rather than the declared ones (through the push route, the proposal handler and the binary end to end, each), a wake-up sent to every row with a URL, a wake-up sent before the commit, member never set, approved by any attester, the memory ingest dropping jobs, the memory ingest overwriting them, the Postgres ingest dropping them (through bash ci/local.sh, 461 passed and 1 failed in loot-forge), only the first resolved address checked, the binary declaring the kinds of every trigger, the push declaring nothing, approved read once per kind, and the no-kind early return removed; each restored to green. no migration, no format byte and no published wording moves; the forge binary changes, so a forge makes jobs only once it is redeployed, and a forge older than this still accepts a client that declares kinds. the workspace suite is green under bash ci/local.sh against Postgres 18 (4345 passed over 137 binaries, 12 ignored) (#2159)nothing readable · 26 sealed · 26 internal
- the review-sweep fix-up over #2299, #2142 and #2154. a seek cache round now claims the busy marker before it decides anything: LootCache::claim_and_make creates loot-seek-refreshing with create_new before it asks whether the position stands or makes one, so a position another invocation is making carries a marker from before its store exists, and a round that finds a live one neither makes the position over it (refused, naming the state, when no store stands yet) nor removes it when its own refresh fails; a marker past the hour is taken over. before, the marker was written after the make, and a second round inside that window read the half-made store as a leftover and removed it. a first refresh now means no record file at all (Refresh::absent): the record is written through atomic_write rather than truncated in place and read through read_replaced, so an empty or unreadable record is a refresh that finished and a failed refresh leaves that position standing. pinned in seek.rs by a_make_in_progress_is_not_removed_by_another_invocation, which drives a make through claim_and_make and fails a second round inside it, and by a_record_that_does_not_read_is_not_a_first_refresh; red first against the unchanged rule (1 passed and 2 failed, the make extracted into claim_and_make with the marker still after it). red under mutation, counts read each time: the claim moved after the make (1 passed and 3 failed), begin in place of claim (1 and 3), a round that finds a live marker deciding first on the record (2 and 2), absent read as unreadable (3 and 1), the record written with fs::write (3 and 1), no refusal over a claimed unmade position (3 and 1), a stale marker read as live (3 and 1), each restored to green. the seek unit tests now default to a cache home under the temp directory, pinned by the_unit_tests_cache_home_is_never_the_runners (red with the default dropped, 4 passed and 1 failed). stale sentences fixed: the ADR 0090 #2142 amendment is corrected, the CONTEXT.md seek entry names the marker exception and the gc exit, ADR 0023 scopes the refusal-only exit to a question, root_notice and discovered_store_dot say why the function is free rather than counting callers, the in_progress verb is the verb whose stop created the record and another verb is refused, the last-updated header states its import reach by reachedModules, the seek module doc states the exit property, and a CONTEXT.md rewrap leftover is rewrapped. through the lane binary under a scratch LOOT_SEEK_CACHE, a position with an empty record stands after a failed refresh and a marker-only position is refused with its marker left. no migration, no format byte and no published wording moves, so this owes no deploy. the site gate is green (680 passed and 62 skipped over 62 files, 62 surfaces) and the workspace suite is green (4328 passed over 137 binaries, 12 ignored) (#2303)nothing readable · 8 sealed · 8 internal
- loot seek --gc exits 1 when a removal it decided failed, with every row and the H row still written and the error still on stderr: gc::Report now implements Emit::exit_code (#1764), 1 when any decision carries failed and 0 otherwise, 1 being the ADR 0025 row for an I/O error and the code the error path in main.rs exits with, the reasoning gates::EXIT_UNRUNNABLE records; the cap after a refresh is unchanged and a failed removal there does not move the question exit. the ticket named the hook #2127, which is loot pipeline, the verb whose code it cites; the hook is #1764. the ADR 0023 #2125 amendment gains the exit sentence, the ADR 0090 #2125 amendment says the invocation exits 1, and the seek module doc scopes its refusal-only exit rule to a question. pinned in a_failed_removal_is_reported_as_failed_and_stops_nothing: the report exits 1, and 0 with the failure cleared. red first against the unchanged code (0 passed and 1 failed). red under mutation, counts read each time: the code fixed at 0 (0 and 1), fixed at 1 (0 and 1), keyed on a decided removal rather than a failed one (0 and 1), each restored to green. through the binary under a scratch LOOT_SEEK_CACHE with one position held open, the lane binary exits 1 and the primary release binary 0, both printing the removed and failed rows. no migration, no format byte and no published wording moves, so this owes no deploy. the workspace suite is green (4325 passed, 0 failed, 12 ignored) (#2154)nothing readable · 4 sealed · 4 internal
- the seek tests no longer write into the cache home of whoever runs the suite, and a cache position with no refresh record is removed when its refresh fails. every loot the seek smoke test spawns is now built by one closure that sets LOOT_SEEK_CACHE to the test home; the unreachable-host refusal ran under the operator home and was the one call in the suite that could make a 127.0.0.1:1 position there, and the --name refusal made none, since it is read off argv before the target opens, so the premise that it did is corrected. the unit test that resolved git+file:///no/such/dir through Target::resolve now opens GitCache under a home of its own. the verb: a fresh make whose refresh fails was already removed, measured through the binary, but a position standing when a round began was not that round make, so a failed refresh removed nothing and the cap never removes an unrecorded position; LootCache::open_in now treats a position with no readable record and no live busy marker as on its first refresh and removes it whole when that fails, recorded as an ADR 0090 amendment and in CONTEXT.md, and gc::busy_at is the busy rule the open and the gc walk both read. pinned in seek.rs by a_position_whose_first_refresh_fails_is_removed_whole and in the smoke test, which seeds such a position in its own home. red first against the unchanged rule (1 passed and 1 failed, the passing one the edited resolver test). red under mutation, counts read each time: the removal keyed on the make alone (unit 0 passed and 1 failed, smoke 0 and 1), the busy check dropped (0 and 1), the record check dropped (0 and 1), no removal at all (unit 0 and 1, smoke 0 and 1), the smoke closure pointed at another home (0 and 1), each restored to green. the listing of the real cache home is unchanged, mtimes and sizes, across the full suite. no migration, no format byte and no published wording moves, so this owes no deploy. the workspace suite is green (4325 passed over 137 binaries, 12 ignored) (#2142)nothing readable · 5 sealed · 5 internal
- the review-sweep fix-up over #2295, #2096 and #2137. the discovering layout door takes its caller body need: workspace::ambient_store_dot, which fixed BodyNeed::Unread and so would have admitted a body-reading verb that found its store through it to a body-deferred store, is now discovered_store_dot(start, bodies), passing the need through to resolve_store_dot, and loot doctor declares Unread at its call. ambient_local_config keeps a fixed Unread, since it hands back the config path and lane id rather than the store, and its doc says so. pinned in workspace.rs: a Held caller is refused naming the state from the root and from a subdirectory, an Unread caller still finds the store, and the same repo before the record is the control; red first with the need ignored (0 passed and 1 failed), red under mutation with the door fixed at Held (0 and 1), restored to green. the refusal text, the doctor bodies line and the ADR 0093 section 3 heading now say a verb that declares neither that it fetches first nor that it reads none is refused, since Unread passes. set sentences replaced by the property in workspace.rs, CONTEXT.md and ADR 0093: a door that finds a store without reaching BodyNeed::admit is unguarded and whether that is safe is a question for that door, is_relay is recorded as why a relay store needs no guard, and a verb behind an admit door meets the refusal unless a need other than Held is named there. the site marker pin: the hash now covers every module the page reaches by a relative import, so RETENTION_DAYS on /privacy moves it, and the privacy pin is re-pinned with its date unchanged; the scan reads markers with comments stripped, so a comment saying last updated no longer makes a file unreadable; the header defines a dated marker as the words last updated and says why the known-issues Last reviewed date is left to its own pins. red first against the old test, counts read: RETENTION_DAYS moved to 31 stayed green (9 passed), a terms comment saying last updated went red (2 failed and 6 passed); with the change the first is red (1 failed and 10 passed) and the second green (11 passed). red under mutation: the import reach dropped from the pin (1 failed and 10 passed), comment stripping dropped from the marker read (1 and 10), the import walk reading nothing (2 and 9), each restored to green. stale sentences fixed: HUNT-PERF names every other read the open makes rather than the reads in the body of assemble, the in_progress verb is the verb that wrote the record rather than a STOPPING member, the SDK read test no longer calls its shape the one where the head derivation is load-bearing, and repo.ts and the ChangeView doc name the superseded-head half of within_depth_of_heads and say its working-change branch is not mirrored. no migration, no format byte and no published wording moves, so this owes no deploy. the site gate is green (680 passed and 62 skipped over 62 files, 62 surfaces) with no ceiling moved, the sdk build:wasm and type-check are green, and the workspace suite is green (4324 passed over 137 binaries, 12 ignored) (#2299)nothing readable · 11 sealed · 11 internal
- the browser SDK head derivation skips a superseded version: the WASM core ChangeView, the JSON WasmBundle.changesJson renders, gains predecessors as hex ids, an empty list when a change supersedes none, and snapshot() in sdk/src/repo.ts drops any id some returned change names as a predecessor before folding the heads, the engine within_depth_of_heads rule, so heads() and the parents a push builds on no longer name a superseded version under unboundedRead against a host that sends one. the golden relay bundles are regenerated with the first change pushed, amended and pushed again, so the meta bundle holds a superseded version nothing names as a parent; the amend is a loot squash rather than the loot edit the ticket named, because an edit finalize records the superseded version as a parent as well as a predecessor, measured, which the old derivation already skipped. pinned in relay.unit.test.ts: the golden holds that shape, and heads() names the amend alone while list() does not show draft.md, the path only the superseded version holds; the shared parity check_bundle pins the empty list. red first against the old golden (20 passed and 2 failed). red under mutation, counts read each time: the predecessor filter dropped from snapshot (20 passed and 2 failed), the core rendering no predecessors (19 passed and 3 failed), the field left out of the JSON (parity 0 passed and 1 failed), each restored to green. no format byte and no migration moves and no host changes, so this owes no deploy. the SDK suite is green in the lane (139 passed over 13 files), the site gate is green (678 passed and 62 skipped over 62 files, 62 surfaces) with no ceiling moved, and the workspace suite is green (4323 passed over 137 binaries, 12 ignored) (#2137)nothing readable · 8 sealed · 8 internal
- the site dated markers are pinned to the text they date: site/test/last-updated.test.ts scans every file under site/src, and every file a module there imports as ?raw, for a Last updated marker, and holds each to a sha256 of its page taken with the date slotted out, comments stripped by the TypeScript printer and whitespace collapsed, so a page whose text changes without its marker and pin moving is red at the gate while a comment or a reflow is not, a marker in a format the scan cannot read is refused rather than skipped, and the failure prints the entry to write with the local date of the day it runs. a pinned table was chosen over reading git at build time because a land builds the site in a lane, which is not a git checkout. both markers the scan finds were already true and are unchanged: terms.tsx last changed on 13 September local time (ad38475), so the premise that it was stale is corrected, and privacy.tsx on 20 September (928f213). that a deploy is not an edit is written in the test header and in a comment beside each marker; no notice text moved. red under mutation, counts read each time: a word of the terms text changed (1 failed and 7 passed), the privacy date moved without its pin (1 and 7), a marker written as 2026-09-13 (2 and 5), an unpinned marker on /trust (2 and 7), an unpinned marker in a ?raw evidence file (2 and 7), comment stripping dropped (3 and 5), the date slot dropped (3 and 5), the ?raw reach dropped (1 and 8), each restored to green. no published wording moved, so this owes no deploy. the site gate is green (678 passed and 62 skipped over 62 files, 62 surfaces) with no ceiling moved, and the workspace suite is green (4323 passed over 137 binaries, 12 ignored) (#2096)nothing readable · 4 sealed · 4 internal
- the review-sweep fix-up over #2291, #2043 and #2080. loot verify no longer walks past the deferred-bodies refusal: it finds its store by layout through resolve_store_dot and never met the record, so in a store carrying .loot/deferred-bodies it reported a body it had never fetched as missing, and verify --accept-loss wrote that body into .loot/lost as unrecoverable while the remote still held it. the refusal is now decided once, in BodyNeed::admit, which the open (Workspace::assemble) and the layout door both call, so the doors it guards are that function callers: resolve_store_dot takes a BodyNeed, verify declares Held and is refused naming the state, and doctor and config declare the new Unread, since they read no body. the relay doors take none, and loot_net::is_relay records why: each refuses a store without the relay role marker before reading an object, no code that writes the record makes a relay, and gc there removes only objects the store holds; gc --dir, count-objects --dir, relay reap-grants, relay abandon-head and serve --dir were each driven through the binary at a deferred store and each refused it. the refusal now says the verb did not declare it fetches first rather than that it reads a body, since whoami, status and log read none, and doctor, ADR 0093 section 3 and CONTEXT.md say the same; section 3 is restated as the property and records the #2295 correction. false sentences fixed: the counts beside GATES in spawn.rs and orchestrator.rs now point at the rows, the sdk gate doc and SDK_GATE_STEPS say build:wasm can reach the network and refuses rather than degrading there, the in_progress verb and operand docs state the property instead of listing verbs, HUNT-PERF stops counting the reads around the graph load, and the rewrap leftovers in the land-change skill and ADR 0071 are fixed; resolve_store_dot also gets back its own doc, which sat on registered_peer_keys. red first through the binary: verify answered 1 missing (0 passed, 1 failed). red under mutation, counts read each time: verify declaring Unread (1 passed and 1 failed), verify refused but --accept-loss let through, which accepted 1 missing object as lost (1 and 1), the refusal in admit keyed on the wrong need (0 and 2), each restored to green. no migration and no format byte moves, so this owes no deploy. the workspace suite is green (4323 passed over 137 binaries, 12 ignored) (#2295)nothing readable · 16 sealed · 16 internal
- the land now type-checks sdk/: a new sdk gate, right after the site gate in orchestrator::GATES, runs npm ci, npm run build:wasm and npm run typecheck in the landing position sdk/ (hygiene::SDK_GATE_STEPS), refusing on a failed build or a type error, riding the merged-tree re-gate, skipping under --skip-tests as sdk=SKIPPED, and degrading a provably offline npm ci to sdk=SKIPPED-OFFLINE with a block of its own, so the verdict line gains sdk= after site=; the two npm gates now share run_npm_gate and supersede_offline. the land was chosen over ci/test-main.sh because only a land refuses before main moves and GitHub Actions had not run since 2026-09-19, and over the site gate step because site= would then speak for two packages. measured in a fresh lane: npm ci 1.4 s, build:wasm 17.8 s cold but 1.7 s once the site gate has built the same crate, tsc 1.0 s, the whole gate 5.05 s. proved live through the sdk row with real npm, wasm-pack and tsc: clean, red at npm run typecheck with TS2322 for a string assigned to a number in sdk/src/hex.ts, clean again once removed. ADR 0071 gains the #2080 amendment and CONTEXT.md an SDK gate entry, and workflow.md, the land-change skill and sdk/README.md say what runs and that npm test still does not. red under mutation, counts read each time: the typecheck step dropped (7 passed, 2 failed), the sdk row running the site gate (5 and 4), the sdk= field reading the site slot (7 and 2), the supersede rule reading one fixed gate (8 and 1), an offline install refused rather than degraded (7 and 2), each restored to green. a land runs the primary release loot-first, so the gate is live once those binaries are rebuilt; no migration and no format byte moves, so this owes no deploy. the workspace suite is green (4322 passed over 137 binaries, 12 ignored) (#2080)nothing readable · 9 sealed · 9 internal
- loot doctor names the deferred absence and every ordinary open refuses a store in it, the first stage of the body-deferring filter: a store whose bodies are fetched on demand now carries .loot/deferred-bodies (the remote and what asked, read fail-closed so a record that will not parse still counts), the seek cache position writes it whenever it opens a position lacking it, doctor reports it as an info bodies line, and Workspace::assemble refuses such a store naming the state for every open except the new Workspace::open_fetching_bodies_at, which the cache opens through, so a verb that would read an unfetched body as absent or sealed is refused rather than answered; a body fetch that fails in the cache now refuses naming the state and the host. ADR 0093 records the state, the refusal at the open and the offline policy (a refusal, never an absence and never a stale answer, reachability never recorded), ADR 0090 gains a #2043 amendment and CONTEXT.md gains the deferred kind under Absence. what #2043 still owes is the filter on a working position, whose flag name is left open, and verbs that fault a body in instead of refusing. red under mutation, counts read each time: the open refusal dropped (0 passed and 2 failed in the seek pins, 0 and 1 through the binary), the record read fail-open (0 and 1), doctor never naming the state (0 and 2), the fetch refusal unwrapped (0 and 1), the record written only when made (0 and 1), each restored to green. a new plain-text store file, no format byte moves and no migration, so this owes no deploy. the workspace suite is green (4313 passed over 138 binaries, 12 ignored) (#2043)nothing readable · 11 sealed · 11 internal
- the review-sweep fix-up over #2287, #2283 and #1986. a resumed pick now asks again about a path its stop asked about when the record holds no answer for it, rather than handing it to the three-way, which merged over the resolution and put back the edit it rejected: change_delta_merge takes the stopped record whole and takes every question on it out of the three-way, keeping an answer only where the line still holds it, so a resolve that recorded no answer (a loot from before #1986, or a failed answer write, which the workspace comment now truthfully says costs a re-ask) re-stops with nothing applied, keeps the restore point, and lands once the question is answered again; refusing --continue was weighed and rejected in the ADR 0080 #2291 amendment, since nothing would be left to resolve. the replay reads its direction from pick::SPECS, which now pairs each spec with it, and a record naming a version under a verb SPECS does not hold refuses instead of replaying as a cherry-pick. the usage note names no verb, so each stopping verb line is true of its own verb, and its test now fails if the note names a verb that replays. a cherry-pick replay to completion is pinned through the binary on the home position, a pinned primary and a lane. false sentences fixed: the in-progress operand reader, the pick stop comment, the resume test doc link, the continue_clause list, the CONTEXT.md in-progress entry and HUNT-PERF on graph file reads; rewrap leftovers in fixture.rs and engine.rs are fixed, and the replaying tests keep their resolution file outside the working tree. red under mutation, counts read each time: questions taken from the answers alone (16 passed and 1 failed in the loot-core pin, 31 and 2 through the binary), an unknown verb replayed as not inverted (20 and 1), the note naming cherry-pick and revert again (19 and 2, the second the census the usage lines no longer matched), each restored to green. no migration and no format byte moves, so this owes no deploy. the workspace suite is green (4309 passed over 138 binaries, 12 ignored) (#2291)nothing readable · 13 sealed · 13 internal
- loot revert --continue and loot cherry-pick --continue now replay the stopped pick instead of closing the stop, on the answers loot resolve gave to that stop and on nothing else: the in-progress record gains the version the verb applies, the conflicts its stop recorded and the answers, resolve writes an answer there only when the conflict it settles is the one the stop recorded at that path, and change_delta_merge takes only the answers its caller hands in, none from an ordinary run, so the repo settled ledger is still never read by a pick (#744). a path whose content moved after it was answered is asked again rather than merged, a path holding the answer bytes at another address keeps it, a replay that stops again keeps the record restore point, and a record written before this closes as it always did. ADR 0080 gains the #1986 amendment deciding the three questions and recording, measured through the binary, that move, squash and apply-patch stop again after a resolve for reasons that are not a ledger and that apply and merge have nothing to replay; CONTEXT.md, the resume module and the usage note say the same. the resume runs to completion through the binary on the home position, on a pinned primary and in a lane. red under mutation, counts read each time: the repo ledger handed to the resume (0 passed, 1 failed in the stale-entry pin), the repo ledger handed to the three-way (0 and 1 in the loot-core pin, 0 and 1 in the re-run pin), a moved path left to the three-way (0 and 1), the address test alone (0 and 1 in loot-core, 0 and 1 through the binary), an answer keyed on the path alone (0 and 1), the record sentence worded by verb (0 and 1), the replay branch emptied (0 and 4), the re-stop taking a new restore point (0 and 1) and the usage note dropping revert (0 and 1), each restored to green. the record gains a trailing section, no format byte moves and there is no migration, so this owes no deploy. the workspace suite is green (4302 passed over 138 test binaries, 12 ignored) (#1986)nothing readable · 13 sealed · 13 internal
- loot blame and loot log --path read the one path they ask of each change through the new GraphView::address_at, which answers off the manifest frames through the new Manifest::address_of where the frames provably are the map (no backslash key, keys strictly ascending in Path order, checked by the keys_ascend step Manifest::addresses uses) and from the decoded map otherwise, so a history walk no longer decodes every manifest on the lineage to read one entry of each, and blame no longer clones each tree; has_path keeps decoding, since its remaining production caller asks many paths of each of a few changes. the new --path-history opt-in half times address_at over every change of a re-opened chain at 200 paths, asking the path that orders last; paired, one loot-perf-gate --features count built per arm with the frame arm never taken as the before, interleaved, the gate reading load idle at 1 to 4 percent (peak 7): 34.87 to 34.97 ms down to 4.46 to 4.53 ms at depth 256, 138.8 to 139.3 down to 18.01 to 18.03 at 1024, and 279.2 to 280.6 down to 35.9 to 36.2 at 2048; counted by the new path_history_allocations test at width 200, the walk allocates 484,352 times at depth 1024 against 0. through the binary, against the landing position binary over a repo 1,025 changes deep and 203 paths wide, min of 15 over two rounds: loot blame 160.4 to 160.6 ms down to 33.7 to 33.8, loot log --path 164.8 to 165.8 down to 43.7 to 44.0, plain loot log 28.9 to 29.2 on both, and every output compared was byte identical with its exit code and stderr: blame plain, --porcelain, --json, with a selector and on an absent path, log --path on flat, nested, dotted and absent paths, and shortlog --path. new pins: over every golden, a file whose Path order is not its byte order, the legacy separator file and the three #2273 shapes, address_of answers what the eager map answers for every key and for probes that are not keys, decoding nothing exactly where the frames are the map, and it answers off the frames past a holder name that is not UTF-8; a spawned blame and log --path find a/b.txt beside a-b.txt and a.txt. red under mutation, counts read each time: the frame arm never taken (0 passed and 1 failed in the codec pin, 1 and 1 in path_history_allocations), the ascent check dropped (0 and 1, on the answer), the counts check dropped (0 and 1, on the control, since Windows reads a backslash as a separator), byte order in the stop (0 and 1 in the codec pin, 0 and 1 in the spawned pin), each restored to green. riding along: HUNT-PERF names the new half. no migration and no format byte moves, so this owes no deploy. the workspace suite is green (4289 passed over 137 binaries, 12 ignored) (#2283)nothing readable · 11 sealed · 11 internal
- the review-sweep fix-up over #2280, #2281 and #2282. the closure-walk pin stored x/abc after x/ab, which a random address beginning with c completes, so its stored-once control failed 5 runs in 400; it now uses y/abc and read 400 of 400 green. the spliced-node pin claimed the tree a later save writes back, but that save writes the copy the graph file already holds, since the rewrite inserts what it reads back first and ChangeGraph::insert keeps the first node for an id; it now saves into a store with no graph file, the splice comment says why, and a splice that blanks each spliced manifest goes red on that assertion (0 passed, 1 failed). decided and pinned: an ingest past a key or holder name that is not UTF-8 in a node it does not splice returns Ok and the next rewriting save refuses, kept because the open already defers such a file and stepping every pool node reads 13.8 to 14.4 ms beside a 54.4 to 55.0 ms deferred read of this repo graph file (red when the check scans the whole pool and when the save fallback is dropped, 0 passed and 1 failed each). measured with a tracking allocator over that 85.7 MB file: the rewrite union holds 88.2 MB deferred against 222.5 eager and peaks at 193.9 against 382.0, both freed when the save returns; a pool keeps one copy of the file per read that splices, 85.7 to 342.7 MB over one to four reads against 1.2 to 6.7 eager, recorded at the splice. the move-not-clone property lost its count pin to deferred manifests (a clone costs about two allocations per spliced node, 78 against 65 at the pin shape) and is pinned by the pool ids instead (red under the clone, 0 and 1). the closure walk doc names the holder-name case where it answers and the decoding walk panicked, pinned (red when the ascent check also steps holders, 0 and 1). each mutation restored to green. list-class sentences in HUNT-PERF and the loot-perf fixture rows now name what defines the set of graph file reads, and a rewrap leftover and a stale keys_ascend reference are fixed. no migration and no format byte moves, so this owes no deploy. the workspace suite is green (4285 passed over 137 binaries, 11 ignored) (#2287)nothing readable · 7 sealed · 7 internal
- LineagePool::read, the one read of the shared graph file an adopt or ferry pass catches up from, reads it through read_graph_deferred, the door #2280 added, and leaves every manifest undecoded, so an ingest no longer decodes every path of every change in history to splice nodes whose manifests it never reads; before moving any node the splice asks the new Manifest::decodes, which steps the frames for a key or holder name that is not UTF-8, and refuses the ingest naming the change when a node it would splice holds one, which the eager read refused, so the #2275 panic gains no new route. paired --ingest readings, one loot-perf-gate --features count built per arm, interleaved, the gate reading load idle at 1 to 6 percent (peak 9): 162.7 to 164.6 ms down to 12.04 to 12.39 ms at depth 1024 and 331.4 to 331.5 down to 26.9 to 27.6 ms at 2048, the check costing 0.2 to 0.7 ms at 1024; counted at depth 128 and width 200 the region allocates 1,057 times against 61,604, the read band 279 against 60,826 and the splice band 778 on both arms. the memory trade the splice comment left open is measured and taken: over a copy of this repo graph file (85.7 MB) the deferred read peaks at 171.3 MB against 308.5 and holds 87.9 against 222.2 while the pool lives, and once the pool drops a splice of ten nodes keeps the 85.7 MB buffer where the eager read kept 1.2 MB, one buffer per read that splices, for the rest of the verb. two new pins: over canonical, nested, legacy separator and the three #2273 shapes a spliced node stays undecoded and answers the eager read tree, addresses and saved tree, with tier_counts answering as the open does where two backslash-free keys name one path; and a key or holder name that is not UTF-8 refuses the splice and splices nothing. lineage_allocations now pins that the ingest count does not follow the entries. red under mutation, counts read each time: the eager read put back (0 passed and 2 failed in the engine pins, 2 and 1 in lineage_allocations), the splice check dropped (1 and 1), the holder check dropped (1 and 1), restored to 2 passed and 3 passed. no migration and no format byte moves, so this owes no deploy. the workspace suite is green (4282 passed over 136 binaries, 11 ignored) (#2282)nothing readable · 7 sealed · 7 internal
- closure_complete, the walk the implicit-capture door takes in front of each bare mutating verb, reads each ancestor address off its manifest frames through Manifest::addresses, the spelling locally_missing_objects has used since #2240, where it decoded each ancestor manifest to keep only the addresses; a manifest whose frames are not provably its map, a legacy separator key or the #2273 shapes, still answers from the map. paired --closure readings at depth 1024, one loot-perf-gate --features count built per arm, interleaved, the gate reading load idle at 1 to 4 percent (peak 7): 166.1 to 167.9 ms down to 19.6 to 20.0 ms at width 200 over three rounds, and 382.7 to 383.2 down to 42.8 to 42.9 ms at width 400 over two, the gated counters the same on both arms. two new pins: a reopened repo answers both verdicts decoding no manifest, and over canonical, nested, legacy separator and the three #2273 shapes, with no hole and with each object removed, the deferred and the pre-decoded answers equal a walk over the decoded manifests, with a control that each collapsing shape reached the arm where frames and map disagree. red under mutation, counts read each time: the walk put back on values (0 passed and 2 failed), the ascent check dropped from Manifest::addresses (1 and 1, on the answer itself), the frame arm never taken (0 and 2), restored to 2 passed; the backslash condition dropped alone stays green on Windows, where Path reads a backslash as a separator and the ascent check catches the collapse, which the test doc records. no migration and no format byte moves, so this owes no deploy. the workspace suite is green (4280 passed over 137 binaries, 11 ignored) (#2281)nothing readable · 3 sealed · 3 internal
- the graph rewrite a finalize pays reads the graph file back deferred and copies each manifest stored frames where Manifest::frames proves them to be what encoding its map writes (no backslash key, keys strictly ascending in Path order, UTF-8 holder names), and falls back to the eager read otherwise, so save_to no longer decodes every path of every change in history to add one change. the new --graph-rewrite opt-in half times that whole save at 200 paths; paired, three interleaved rounds, the gate reading load idle at 1 to 4 percent: 48.18 to 48.48 ms down to 12.65 to 12.72 ms at depth 256, 183.43 to 183.49 down to 39.14 to 39.47 ms at 1024, and 750.3 to 759.4 down to 179.5 to 182.6 ms at 4096. counted by the new graph_rewrite_allocations test, the save at depth 1024 allocates 6,039 and 6,061 times on two runs against 695,198. the file written is byte-identical to the decoding rewrite, pinned over every golden and a lone y//ab key (copied), the legacy separator and the #2273 shapes (not copied, rebuilt from the eager read), and a key or holder name that is not UTF-8 (not copied, and the save still refuses it). red under mutation, counts read each time: the eager read put back (1 passed and 1 failed), the holder check dropped, the ascent check dropped and the eager fallback dropped (1 and 1 each), the counts check dropped (0 and 2), restored to 2 passed each. riding along: HUNT-PERF names the new half, and the visibility census names the moved and new bindings. no migration and no format byte moves, so this owes no deploy. the workspace suite is green (4278 passed over 137 binaries, 11 ignored) (#2280)nothing readable · 10 sealed · 10 internal
- the install page names v0.4.24 now that the artifacts and the detector have landed, which is the condition the constant own comment states: RELEASE_TAG is deliberately not moved by a release cut because it names what dl.millerbyte.com can serve rather than what the workspace is versioned at, and v0.4.24 reached the public bucket in one finalize pass shipping three of five triples with the two darwin ones carried forward on the signed manifest platform_pins, after which the published detector installed it anonymously through all four surfaces and read back loot 0.4.24 exactly, and a hand download of the windows zip matched the published sha256.sum and printed loot 0.4.24. DARWIN_CARRY_TAG is deliberately left at v0.4.14 because the macOS rows are served by that carried pin and not by this tag. release-pin.net.test.ts ran live against the CDN rather than returning early (3 passed), and the site gate is green from the lane with nothing recorded. no migration, no wire or format byte moves and no host behaviour moves, but the published install page changes, so this owes a site deploy (#2276)nothing readable · 2 sealed · 2 internal
- loot 0.4.24: loot-cli and loot-forge move to 0.4.24 with their two lock entries, the first public release since 0.4.23 and the one that carries the proposal read surface and its database views (#2174, #2175, #2177, #2180), the land gate reading foreign authorship and refusing on a require line (#2178), the forge keeping the attestations a push carries (#2250, migration 0019), the runner routes and loot runners (#2158, migration 0021), the browser sdk transport watchdog (#2070), the perf follow-ups from #2222 to #2247, and every other land since 8fb0716, 35 in all. format major stays at 14 and format.rs has no diff in the range, so ADR 0066 has nothing to sequence. the top forge migration is now 0021_runner_last_seen.sql; 0019 and 0020 are already live from the deploy-only v0.4.23-deploy.1, so 0021 is the one new schema step, and it rides the forge binary, so the forge deploys BEFORE the site. the Known Issues page is re-reviewed by running the 0.4.24 binary in a throwaway home with a local loot-relayd and a loot-forge --dev, not by re-reading source: the one entry reproduces word for word, change lkorrwmk has descendants - v1 edits only a tip (childless) change at exit 1, while loot edit on the tip reopens version f318c57a, so it stays; the eight custody verbs refuse with the ADR 0068 message once loot lock clears the session and lock and unlock still run, a wrong LOOT_PASSPHRASE on a relay pull is reported and ignored, that locked pull files keys and leaves the disk on its old bytes and parks .loot/stale-disk-unverified while rehome after unlock materializes what arrived, a locked forge pull refuses by name, the session file holds loot-unlock v2 and machine and sealed with the passphrase hex in no file under the config directory, loot id phrase refuses a pipe and a redirect, loot undo refuses at the push barrier in the quoted words, and loot burn prints the never-pushed tier and the pushed tier with one line per disclosed host. three probes were first mis-driven by the harness and re-run, which the page records. REVIEWED_AGAINST moves to v0.4.24 and LAST_REVIEWED to 2026-09-23; RELEASE_TAG is deliberately NOT moved, since it names what dl.millerbyte.com can serve and this release has reached nothing yet. cargo build --release --locked validates the lock edit (loot 0.4.24), the published-surfaces pins pass (6 passed) and the site gate is green with nothing recorded (#2276)nothing readable · 5 sealed · 5 internal
- Manifest::addresses reads the frames only after a step over them shows the stored keys strictly ascend in Path order, so a manifest whose frames are not its map, two keys naming one path or keys out of the map order, answers from the map, and a pull over a corrupt local graph file no longer asks for an address the map dropped. tier_counts keeps its backslash test, now documented as not a proof, and the a.txt and a/b ordering it cites is corrected to component order in both places. what the check costs, paired --missing readings, one loot-perf-gate --features count per arm, eight interleaved rounds, load idle at 2 to 10 percent: 0.875 to 0.924 ms without it against 2.09 to 2.18 ms with it at width 200, and 5.76 to 5.88 against 10.50 to 10.63 ms at width 800, where a decode arm read 18.75 to 19.13 and 100.0 to 100.8 ms, so about 9x against the decode where it was about 21x and 17x; the Path comparison is most of it. red under mutation, counts read each time: the check removed, compared by byte, not strict, byte equality only and path equality only (1 passed and 1 failed each), restored to 2 passed. riding along: store.rs and ADR 0075 say the ingest transaction still reads for checks of its own and that the landing decision is what reads nothing inside it, that a push with no proposal open pays 0.28 to 0.43 ms where #2177 asked for nothing measurable, and that the fallback decision rests on one repo size; finish_stage says why any refusal is answered by the address on disk; the dated test counts in format_skew_gate.rs and workflow.md say at the time; two rewraps; the calls.rs runner stubs use the file imports. no migration and no format byte moves, so this owes no deploy. the workspace suite is green (4274 passed over 135 binaries, 10 ignored) (#2273)nothing readable · 11 sealed · 11 internal
- propose::list reads the tip authors its gate needs in one proposal_tip_authors read, taken only when a row cannot be answered without one, where it read change once per row; the read is scoped by the repo proposal rows rather than repo_change, since a tip is held to change_node alone and the Postgres key-arm pin in tests/proposal_read.rs went red on the repo_change join first tried. ingest asks propose::landed_by_declaration, the one landed closure run over the stored graph overlaid with the push, where it carried a second walk of its own, and a new conformance case hands the ingest transaction a decision that disagrees with the graph both ways and holds both stores to applying it and deriving nothing. counted rather than timed through a new call-logging store double, calls.rs: a listing makes the same reads at 1 row as at 40, a push makes the same reads at 1 open proposal as at 40 with the proposal rows and the graph each read once and the transaction last, and a push with no open proposal reads no graph; a push that lands a proposal is answered in the same bytes as the same push to a forge holding none. measured on a throwaway Postgres 18 over a 1,000-change repo with one-change pushes landing nothing, three interleaved passes of 45 pushes per arm: per-pass medians 1.80 to 1.90 ms with the decision removed, 2.18 to 2.23 ms with it and nothing open, 4.52 to 5.94 ms with 1 to 100 open and flat in the count, against a knowingly wrong arm reading the graph per open proposal at 24.7 to 37.1 ms for 10 and 232 to 360 ms for 100, so the recorded fallback is not taken and ADR 0075 now says so. the proposal read still carries no pipeline verdict; that half of this ticket is left. red under mutation, counts read each time: the list reading change per row (0 passed and 1 failed), each store deriving closes from the head set (0 and 1 on memory and on Postgres, while the existing close case stayed at 1 passed), landed following no parent (0 and 1 on the ingest walk pin), the graph read per open proposal, read with none open and a read after the transaction (0 and 1 each), a landing push answered in other bytes (0 and 1), the tip authors answering every held change (0 and 1), and the Postgres read joined through repo_change (0 and 1 in conformance and in proposal_read), restored to 8, 4 and 1 passed. no migration and no format byte moves; the new read is forge SQL, so the list change reaches the live forge with its next deploy. bash ci/local.sh is green against Postgres 18 (4273 passed over 135 binaries, 10 ignored) (#2235)nothing readable · 13 sealed · 13 internal
- the receiver wants walk reads each address off its manifest frames instead of decoding the manifest, so locally_missing_objects no longer materializes the deferred manifest of each change (#1536) to keep only its addresses. Manifest::addresses steps the frames with the non-allocating twins Manifest::walk already uses, and does so only when the deferring walk recorded counts, the condition tier_counts already rests on for frames and map entries to correspond one to one; a legacy separator manifest or an already decoded one answers from its map. the ceiling was re-taken on the landing position first: 17.96 to 17.99 ms at width 200 and depth 128, against 0.026 ms with the walk over the manifests deleted. paired --missing readings, one loot-perf-gate --features count built per arm, three interleaved rounds, the gate reading load idle at 3 to 9 percent: 17.93 to 18.29 ms down to 0.905 to 0.913 ms at width 200, and 96.13 to 96.34 ms down to 5.70 to 5.89 ms at width 800. along depth at width 200, 1.11 / 4.47 / 17.96 / 72.2 ms down to 0.050 / 0.20 / 0.86 / 3.69 ms at depths 8 / 32 / 128 / 512, still linear in depth with the slope fallen, and one walk at 200 x 128 allocates 25 times against 60,569. the gated counters do not move, object_gets 743 and object_disk_reads 200 on both arms. what it gives up is the decode memo: a later walk in the same process steps the frames again, 0.71 to 0.73 ms against 0.60 to 0.62 ms at 200 x 128. walks of the same shape it did not measure still decode. red under mutation, counts read each time: the walk put back on c.tree.values() (1 passed and 1 failed), the frame arm never taken (0 and 2), the collapse guard dropped (1 and 1), the visibility stepped as one byte (1 and 1), each restored to 2 passed. no migration and no format byte moves, so this owes no deploy. the workspace suite is green (4265 passed over 135 binaries, 10 ignored) (#2240)nothing readable · 4 sealed · 4 internal
- a save no longer fails when the rename that finishes a loose object is refused over an address another writer already landed. persist_codec::save_objects_loose probes for the address before it stages, a writer of the same address can land it between that probe and the rename, and the rename is then a replace, which a reader holding the landed copy without FILE_SHARE_DELETE refuses as os error 5 for as long as it holds, past the retry budget. finish_stage now answers a refused rename with the address on disk by dropping the stage, since the name is the content address, and still propagates a refusal with the address absent. this is the second sighting on this ticket, concurrent_stage_of_same_address_does_not_tear red on Access is denied (os error 5): a hold on the destination refuses a rename as 5 and a hold on the stage as 32, and with a reader holding the landed address and a sleep both added temporarily that test went red at the same unwrap with the same message in 8 of 10 runs, and green in 10 of 10 with the fix. who held it in the sighting is not established, since a stripped copy of the race counting every refusal drew none idle or with the CPU saturated. the first sighting, a_scanner_holding_the_staging_file_does_not_fail_a_save red on os error 32, is not reproduced and this does not claim it: under a saturating load a 120 ms sleep, the length of the scanner hold, woke within about 206 ms while the retry budget stretched to about 1.5 s. red under mutation, counts read each time: the fallback disabled (0 passed and 1 failed), the stage left behind (0 and 1), every refusal swallowed (0 and 1), restored to 1 passed. no migration and no format byte moves, so this owes no deploy. the workspace suite is green (4263 passed over 136 binaries, 10 ignored) (#2233)nothing readable · 4 sealed · 4 internal
- the ADR 0073 consequence clause on what the work counters watch of the opt-in halves stops naming its members and names measure::WORK_COUNTERS instead, since #2225 gated graph_sorts and left the enumeration #1662 wrote naming less than the gate counts. the #1662 amendment quotation of that clause is left as written and marked as dated where it stands, and the status block records why for the next amendment: a quotation records what an amendment changed the text to on its date, and rewritten to agree with the present it would state something that amendment never said. the note under the clause says how it moved from #1662 to here. prose only with no behavioural seam, so there is no mutation proof; work_counters reads 12 passed before and after. the workspace suite is green (4262 passed over 136 binaries, 10 ignored) (#2238)nothing readable · 2 sealed · 2 internal
- shared_version_ids is deleted from DagRepo and Workspace, since LineagePool::ids answers the same whole-store id set out of the pool the adopt resolver already holds and the door had no caller left outside a test; the #829 reason it was added now sits on LineagePool::ids, and the adopt test that used it as a precondition asks the pool instead. the two #2224 counter pins stay two tests, and the adopt pin now says why at the code: what they share is the relation and its non-vacuity check, while what gives each its meaning is a fixture building a different position, which is where #2247 differs, its guards being one question put to one ingest timed and counted. the verb tier module doc now states 390 methods, the width its derived pin reads after the deletion. riding along, the can_open block no longer calls the discarded 1,091.9 ms reading neighbourless in the sentence that says its neighbours did not support it. red under mutation, counts read: the pool precondition inverted (0 passed and 1 failed), restored to 1 passed. no migration and no format byte moves, so this owes no deploy. the workspace suite is green (4254 passed, 10 ignored) (#2236)
- ↳ supersedes
471fd653· not stored here - Kept, not rewritten: a carry replays a suffix as a new version and the one it replaced stays addressable. That is the difference between a carry and a rebase.
nothing readable · 5 sealed · 5 internal - ↳ supersedes
- loot runners lists the runner rows a forge holds for this repo, part 2 of 2 of this ticket: name, key, the step kinds each may claim, live or retired, and when the forge last heard from it, owner-only because it is the owner configuration, retired rows included because who was allowed to run a version is what an audit asks. last seen needed a column, migration 0021 runner.last_seen_at, written by the runner door on every admitted request and throttled in its WHERE clause to one write per runner per 60 seconds, so a runner polling every second costs one write a minute; a registration never sets or clears it, so a runner registered and not started reads never seen, and deriving it from job rows was rejected because an idle runner finding no work leaves none. the first spelling of that throttle, last_seen_at <= $3 - $4, failed on Postgres 18 at the first touch, which would have made every runner request a 500 on the live forge while every in-memory test passed; the edge is computed in Rust now, and landed part 1 never wrote the column. the shape is born frozen under ADR 0023: porcelain pubkey state capabilities last_seen and the name last because it may hold a space, JSON with contract, remote and one object per runner, absent times as null, both from one value and pinned byte for byte. the verb census cost, counted: MACHINE_OUTPUT gains runners, the README block and its sentence move to 85 verbs, verb lines 133 to 134 at three main.rs sites and one ADR 0066 claim, dispatched verbs 84 to 85, dispatch table 83 to 84, machine output 32 to 33 with its named list, and two ADR 0066 lines that are dated records were left alone as the census skips them; the site verb list gains runners and its gate is green. the verb runs end to end through the spawned binary against a real forge. red under mutation, counts read each time: the touch unthrottled (1 passed and 1 failed), the door never touching (0 passed and 1 failed), the name not last (0 and 1), an absent time spelled as a dash in JSON (0 and 1), and a registration allowed to set last seen (1 and 1) once its pin moved into the store, where the defence lives, after a first attempt at the route proved unobservable; on Postgres 18 the throttle removed from the SQL went red at 0 passed and 1 failed after a green unmutated arm, each restored green. bash ci/local.sh is green against Postgres 18 (4258 passed over 136 binaries, 10 ignored). migration 0021 rides the forge binary, so the forge owes a deploy (#2158)
- ↳ supersedes
e0d08c12· not stored here - Kept, not rewritten: a carry replays a suffix as a new version and the one it replaced stays addressable. That is the difference between a carry and a rebase.
1 readable · 23 sealed · 24 internal - ↳ supersedes
- HUNT-PERF gains the trap #2223 was filed on: the last cell of a sweep has no neighbour past it, so the outlier check that caught a 1,091.9 ms reading mid-axis had one side to hold the 226.2 ms reading at 128 KB against, and #2242 found that figure reproduces in neither arrangement it took. the entry says what is known and no more, since #2247 withdrew the claim that the figure rested on one reading; it names the arrangement of each ratio, 4.8x in the call and 3.4x as a process, and records the refutation leaning on the same end cell until #2242 carried the axis to 1 MB. the remedy is stated as a property rather than a number of sizes: a cell a finding is filed on needs a neighbour on both sides or a repetition spread reported with it. prose only with no behavioural seam, so there is no mutation proof; the generated work-counter cell is untouched and work_counters reads 12 passed before and after. the workspace suite is green (4254 passed over 135 binaries, 10 ignored) (#2245)nothing readable · 2 sealed · 2 internal
- a network budget refusal now says how long the wait lasted beside the limit, and a relay loot-net serves writes a received line and an answered line per request under LOOT_NET_TIMING=1 on the client clock, because the #2079 stall is unreproduced and nothing on record could say where its time went. the wait is timed around the whole send while the reqwest limit starts inside it, so time well past the limit was spent in this process rather than waiting for the host. the relay lines landed in the failing test captured output beside the client lines when a panic was put after a push and removed, and the land-shell use of the flag is written into workflow.md. no retry is built and no cause is claimed. a workspace run went red once on both format_skew_gate tests under load, one printing the wait lasted 5.5 s on its 5 s probe, and both passed alone: that file now prints what its stub took. the seek smoke test drops the flag from the loot it spawns, since it pins their stderr and failed with the flag set. red under mutation, counts read each time: the wait printed as the limit (1 passed and 1 failed), the wait stored as the limit (0 passed and 1 failed), the relay layer not attached (0 passed and 1 failed), no line on arrival (0 passed and 1 failed), each restored to 2, 1, 1 and 1 passed. no migration and no wire or format byte moves; a relay prints lines only with the flag set, so this owes no deploy. the workspace suite is green (4196 passed over 133 binaries, 9 ignored) (#2079)
- ↳ supersedes
45bf4d75· not stored here - Kept, not rewritten: a carry replays a suffix as a new version and the one it replaced stays addressable. That is the difference between a carry and a rebase.
nothing readable · 8 sealed · 8 internal - ↳ supersedes
- the forge gains the runner routes, part 1 of 2 of this ticket: the owner registers, retires and lists runner rows through /runners/add, /runners/retire and /runners/list, and a runner claims a job and answers it through /jobs/claim and /jobs/verdict, each with its client half in the new loot_net::runners. a runner key is refused at push and propose by absence and admitted at the claim by its row, and since the live operator door lists the owner alone a route a runner calls passes a delegated door, server::authenticate_runner, which admits a live row only while its registering key still passes the operator list, asked every request, so an owner the operator stops listing takes their runners with them. a verdict is accepted from the job claimant whatever the lease says and refused from anyone else, including a runner whose expired claim another has since taken, which narrows ADR 0091 section 7 and keeps its reason; the compare runs inside the store write, FOR UPDATE in the driver, and the signed pass is written in that same write into the attestation table, after the route checks it is present iff the job passed, verifies, and is this runner pass over this version under this job role. retirement is now terminal per key, because the 2157 upsert rewrote retired_at and a forge envelope carries no freshness, so a replayed add could bring a stopped key back; an account key, the owner own included, is refused as a runner; the claim filters by the runner trust floor through Trust::admits, one predicate for both ends; detail is capped at 16 KiB, a log tail at 256 KiB stored by its hash, a lease at 24 hours, a scope must reach .lootpipeline, and a wake-up URL must be https to a host that is not literally private, with the resolved-address check left to the sender in 2159. JobOutcome, the pass roles and the pipeline file name move to loot_net::pipeline and are re-exported, and the forge act tags become one repr u8 enum so a duplicated tag cannot compile. the job.log_oid cross-reference to 1720 is written in migration 0018 as a comment, so its checksum is unmoved. measured on a throwaway Postgres 18 on the desktop and not the VPS: the claim is an index scan of job_claimable at 0.14 to 0.17 ms over 50000 jobs, 4.0 ms at worst over 20000 inadmissible candidates, and one polling runner gets about 139 claims a second while 32 level off near 2150. red under mutation, counts read each time: fourteen named mutations through the routes and the reference store each went red at 0 passed and 1 failed or 1 passed and 1 failed with the pg stamp skipping, one of them only after a first attempt proved too weak to be a mutation, and two driver-only mutations on Postgres 18 went red at 0 passed and 1 failed, each restored green. bash ci/local.sh is green against Postgres 18 (4243 passed over 135 binaries, 10 ignored). no migration and no format byte moves, and the forge needs a deploy for the routes to exist (#2158)
- ↳ supersedes
9798927c· not stored here - Kept, not rewritten: a carry replays a suffix as a new version and the one it replaced stays addressable. That is the difference between a carry and a rebase.
nothing readable · 20 sealed · 20 internal - ↳ supersedes
- the migration-lock census reads the two scratch-database sites tests/proposal_read.rs adds, Scratch::new and restore_the_views, both of which already hold pg::migration_serial's cluster lock, so the unlocked list stayed empty and only the set it reads before trusting moved: ci/local.sh caught it red on the census and green once named. with the carried line, ci/local.sh is green against Postgres 18 (4229 passed over 135 binaries, 9 ignored), proposal_read's four among them run against the cluster rather than skipped; one earlier run lost loot-core's concurrent_stage_of_same_address_does_not_tear to an os error 5 on a rename, which passed three times alone and is filed as a sighting (#2175)nothing readable · 2 sealed · 2 internal
- the web can now list a repo's proposals, and a pin holds it to the same rule the CLI answers with: migration 0020 adds forge_read_proposal, forge_owner_proposal and forge_member_proposal, each one join onto its family's existing gate view, so none re-derives a gate and each inherits how that gate fails closed, and each family's role is granted its own view and nothing else. propose::visible_to in Rust and these views in SQL are two implementations of one disclosure rule, so tests/proposal_read.rs plants one fixture in a scratch Postgres, pairs each Rust reader with the web reader the site would use for the same person - anonymous, an owner through the account holding a rotated namespace key, an accountless namespace key, a member through its roster row - and demands the whole row set match, terminal rows included, with a vacuity guard that every class but the anonymous reader of a private repo sees rows on both sides, and an executable red that rebuilds the anonymous view without its gate and requires the pin to name that class and repo. THE KEY ARM IS PINNED RATHER THAN SKIPPED: visible_to admits a proposer and a tip author by key and no family names a key, so the web is narrower than the CLI by exactly the rows a key presented or authored and never wider, and the test fails if the difference is ever anything else. the family set is derived in migrate.rs from the earlier grants rather than listed, so a new barrier family meets the text guard and must carry its own proposal view or change it with a reason. a cleared gate answers no rows rather than raising, through the views that inherit it. the plan at 10000 proposals over 100 repos reaches proposal through proposal_pkey on repo_id for the owner and anonymous views, which were measured, with no sequential scan of proposal and no per-row author lookup, since the views carry no author; the member view joins proposal the same way through its gate and its plan was not taken. member_read's row-security set gains proposal, which a member view now reads, and drops the counts beside it; 0011, 0015, ADR 0075 and visible_to's doc now point at the second implementation. red under mutation, counts read each time, against a PG18 throwaway cluster: the owner view dropping terminal rows (0 passed and 1 failed), the member view reaching the owner gate (0 passed and 1 failed), the anonymous view ungated (0 passed and 1 failed, naming the four rows the ungated view served), the member view casting without nullif (0 passed and 1 failed on 22P02), the member grant dropped (0 passed and 1 failed), and the anonymous view reading repo in the text guard (0 passed and 1 failed), each restored to 1 passed. numbered 0020 because #2250 landed 0019 while this was built; the merge that follows resolves migrate.rs against it and carries the suite. the forge owes a deploy for the migration before the site can read these views (#2175) (conflict resolution: crates/loot-forge/src/pg/migrate.rs)nothing readable · 1 sealed · 1 internal
- adopt: catch up to landed main 97ace3ecmerge · 2 parentsnothing readable · 1 sealed · 1 internal
- the web can now list a repo's proposals, and a pin holds it to the same rule the CLI answers with: migration 0020 adds forge_read_proposal, forge_owner_proposal and forge_member_proposal, each one join onto its family's existing gate view, so none re-derives a gate and each inherits how that gate fails closed, and each family's role is granted its own view and nothing else. propose::visible_to in Rust and these views in SQL are two implementations of one disclosure rule, so tests/proposal_read.rs plants one fixture in a scratch Postgres, pairs each Rust reader with the web reader the site would use for the same person - anonymous, an owner through the account holding a rotated namespace key, an accountless namespace key, a member through its roster row - and demands the whole row set match, terminal rows included, with a vacuity guard that every class but the anonymous reader of a private repo sees rows on both sides, and an executable red that rebuilds the anonymous view without its gate and requires the pin to name that class and repo. THE KEY ARM IS PINNED RATHER THAN SKIPPED: visible_to admits a proposer and a tip author by key and no family names a key, so the web is narrower than the CLI by exactly the rows a key presented or authored and never wider, and the test fails if the difference is ever anything else. the family set is derived in migrate.rs from the earlier grants rather than listed, so a new barrier family meets the text guard and must carry its own proposal view or change it with a reason. a cleared gate answers no rows rather than raising, through the views that inherit it. the plan at 10000 proposals over 100 repos reaches proposal through proposal_pkey on repo_id for the owner and anonymous views, which were measured, with no sequential scan of proposal and no per-row author lookup, since the views carry no author; the member view joins proposal the same way through its gate and its plan was not taken. member_read's row-security set gains proposal, which a member view now reads, and drops the counts beside it; 0011, 0015, ADR 0075 and visible_to's doc now point at the second implementation. red under mutation, counts read each time, against a PG18 throwaway cluster: the owner view dropping terminal rows (0 passed and 1 failed), the member view reaching the owner gate (0 passed and 1 failed), the anonymous view ungated (0 passed and 1 failed, naming the four rows the ungated view served), the member view casting without nullif (0 passed and 1 failed on 22P02), the member grant dropped (0 passed and 1 failed), and the anonymous view reading repo in the text guard (0 passed and 1 failed), each restored to 1 passed. numbered 0020 because #2250 landed 0019 while this was built; the merge that follows resolves migrate.rs against it and carries the suite. the forge owes a deploy for the migration before the site can read these views (#2175)nothing readable · 8 sealed · 8 internal
- the forge keeps the attestations a push carries and serves them back with their change, where until now it kept none: ingest never read the bundle attestation lane and every bundle serve built carried an empty one, so a tag, a note or a sign-off pushed to a forge was gone and the push said nothing, while a relay was never affected because its store is a DagRepo. the rules are the engine own two and none is new: an /ingest keeps an attestation that verifies and whose change rides the same bundle and drops the rest without failing the push, and a pull serves one only with its change in the bundle being sent, so a caller already holding a change is not re-sent its attestations and a metadata-private repo needs no new rule. /stow keeps none, because it is also a proposer path and a stranger attestation must not write into the owner repo. migration 0019 adds the attestation table, repo-scoped, keyed on the engine dedupe triple so a re-push is a no-op and the first signature stands, with row security enabled and bound by the 0017 policy, and it joins the rls census, whose doc still said eight tables with ten listed and whose shipped-policy check named migrations by index - it now reads every migration from 0017 on. the driver sorts role COLLATE C, because the reference store orders by bytes and a text column otherwise sorts by the cluster collation. a bundle that sends changes pays one more batched read, so the two offer-cost pins move from 4 to 5 and a no-op pull still asks nothing. a late attestation on an already-pushed change still does not travel - that is #2251, in the engine and on every host - and a pin refuses the forge keeping one outside the bundle so that widening is done on purpose. red under mutation, counts read each time: keeping unverified attestations (0 passed and 1 failed), keeping them outside the bundle (0 passed and 1 failed), serving none (0 passed and 1 failed), a read that ignores the ask and a last-signature-wins upsert (1 passed and 1 failed each, the pg stamp skipping off-cluster), each restored to 1 and 2 passed; and on a throwaway Postgres 18 the COLLATE dropped failed the ordering case (0 passed and 1 failed) where the unmutated arm had passed, restored byte for byte. bash ci/local.sh is green against Postgres 18 (4200 passed over 134 binaries, 9 ignored), after one unrelated loot-cli flake on its first run, filed as #2258. migration 0019 rides the forge binary and nothing on the wire or in a format moves, so this owes a forge deploy (#2250)
- ↳ supersedes
82e40318· not stored here - Kept, not rewritten: a carry replays a suffix as a new version and the one it replaced stays addressable. That is the difference between a carry and a rebase.
nothing readable · 18 sealed · 18 internal - ↳ supersedes
- a land that publishes work you did not write now refuses when the repo asks it to, ADR 0092's gating half and part 2 of 2 of this ticket: a require line in the .lootpipeline on main names a step whose pass or the approval every foreign tip must carry, and with no such line the land stays advisory as decided. the syntax is a directive rather than the column section 5 named, the operator's choice, because a step line's command swallows the rest of the line so a column would have re-laid every step line in a grammar map #2099 owns - a require line has two words, every two-word line was already refused as a malformed step, so it changes the meaning of no line that parsed before, and it can express the approval, which was never a step. only the owner's own keys satisfy require approval, as review/approve: a trusted runner's passes count and its approval does not, or the machine that ran the tests could also sign the second pair of eyes. a require line that names no step, names a step not on change, or repeats refuses the land rather than reading as nothing required, because a typo there is a gate the owner meant to declare. the requirement is read at the judged main and never from the stack being landed, a sealed or burned landed file refuses, the check runs before the break-glass branch so --skip-tests cannot reach it, and own work returns before reading the file at all. only the foreign tips must carry the roles, since a pass on a version certifies the tree at that version, and a role signed by a key this machine does not trust gets its own refusal and repair because re-running would change nothing. not proved end to end: no test drives a real land with a second identity and a projected main, so the decision logic is pinned as pure functions and which copy is read and where the check sits are pinned by censuses on the source. red under mutation, counts read each time: the approval accepting any trusted key (34 passed and 1 failed), untrusted read as missing (33 passed and 2 failed), the landed-copy census fed the working tree (0 passed and 1 failed), the check moved after the break-glass (0 passed and 1 failed), and a require naming no step dropped silently (21 passed and 1 failed), each restored to 35, 35, 1, 1 and 22. ADR 0092 records the directive and what building it found, and workflow.md says how to turn gating on. no migration, no wire or format byte moves and no host behaviour moves, so this owes no deploy, though the primary's release binaries must be rebuilt before a land enforces it. the workspace suite is green (4208 passed over 134 binaries, 9 ignored) (#2178)
- ↳ supersedes
a5a6201a· not stored here - Kept, not rewritten: a carry replays a suffix as a new version and the one it replaced stays addressable. That is the difference between a carry and a rebase.
nothing readable · 6 sealed · 6 internal - ↳ supersedes
- the tutorial interrupt red #1916 read as a removal that lied is a tree made again after the sweep: on the linux runner std remove_dir_all answers success only once the root is gone, the captured transcript prints the first prompt ahead of the handler line, and walk, still running on the main thread beside the handler, opened with a create_dir_all over alice that minted the root back before ask beat the heartbeat into it. two delays injected into that sequencing reproduced the red and its alice, heartbeat remnant on this desktop with confirm_gone in place, so that check alone would not have closed it; Sandbox::create now makes alice before the handler is armed, the same two delays pass, and a unit pin drives the sweep, the walk and the beat in that order. what stays open is said at the fix: other writers, the relay thread and the loot subprocesses among them, can still put a path back mid-step. the CI run recorded the two readings and not the sequencing, so the mechanism is established by reading and by the reproduction rather than observed there, and workflow.md stops calling the report and the look one moment. the loot-s3 client keeps its 60 s budget for want of any measured rate to its bucket, but a request that runs out now names it and what it covers, where reqwest said error sending request or error decoding response body with no word of a deadline. the sdk doc stops claiming the native policy: the formula is copied, native allows the send budget between answer chunks where the sdk allows idleMs, the rounding differs, wire.rs now points back at the copy, and the unit test reads the floor from its constant. smaller: the engine.rs verdict compares the band against the band, the measure.rs refusals state a property instead of counting callers, the fixture generator import note sits at the import, and transportFailure takes a role rather than a string. red under mutation, counts read each time: create_dir_all put back in walk (11 passed and 1 failed, holding alice and heartbeat), the s3 timeout naming removed (12 passed and 2 failed), the sdk upload rounded down (6 passed and 1 failed), each restored to 12, 14 and 7 passed. no migration and no wire or format byte moves; the forge and relay deployables word a timed out object store request differently once next deployed, and no code in the tree matches on that text, so this owes no deploy. the workspace suite is green (4192 passed over 133 binaries, 9 ignored) and the sdk suite is green (137 passed over 13 files) (#2254)nothing readable · 13 sealed · 13 internal
- the offline site gate block stops stating its compensating detector as a fact, the shape #2071 fixed one block over: site-main.yml is still named as the net, but only if the land moved a path on its list and only when that job actually runs, which nothing in the land can see, and the fix line now says npm ci and npm run gate in site/ answers on this machine whether or not that job ran. the block does not hard-code whether Actions minutes exist, though the latest site-main.yml run was refused for account billing, the state #1916 found for test-main.yml. ci/local.sh is not named because what ci/test-main.sh runs of the site is its live-database pg tests and not the gate, and the comment there that said a land runs the gate only when it moves a site path and that site-main.yml runs it on main now says neither. land-change and workflow.md, which paraphrase the block, carry the same qualifier. the existing byte-for-byte pin over the block is moved to the new text and went red first (0 passed and 1 failed); red under mutation, counts read each time: the new qualifier dropped from the net line (0 passed and 1 failed) and the fix line put back to its old ending (0 passed and 1 failed), each restored to 1 passed. no migration, no wire or format byte moves and no host behaviour moves, so this owes no deploy. the workspace suite is green (4179 passed over 133 binaries, 9 ignored) (#2074)
- ↳ supersedes
b757d3a0· not stored here - Kept, not rewritten: a carry replays a suffix as a new version and the one it replaced stays addressable. That is the difference between a carry and a rebase.
nothing readable · 5 sealed · 5 internal - ↳ supersedes
- every land now states which versions it published that the lander did not write, and refuses nothing - ADR 0092's detection half, part 1 of 2 of this ticket. a version is foreign when its author is not in the lander's own key set, which is new: loot_identity::own_signing_keys reads the active id.pub plus every id.pub.rotated archive that loot id rotate writes, exposed as Workspace::own_signing_keys beside author_pubkey rather than instead of it, because the two answer different questions. carry_line's Foreign asks may I RE-AUTHOR this and only the current key can sign, while the gate asks did I WRITE this and authorship is historical, so a rotated operator's own change is foreign to the carry and theirs to the gate and both are right - each site now names its own question and points at the other, since unifying them breaks a caller in either direction. the judged set is the ancestor closure of the landed change minus the closure of the main it was judged against, read BEFORE the pass because by verdict time main already includes the landed change and the difference would be empty on every land, and it is the whole closure rather than carry_line's first-parent chain because a stack carrying a merge of its own would otherwise have one side waved through. an absent author is foreign, which is carry_line's answer too, so the two predicates disagree only about which keys are mine. the count rides the verdict line as foreign= and a block names the versions, since the count says that and the next question is which. foreign= sits immediately BEFORE pusher= and not after it, which I got wrong first: pusher= carries an arbitrary percent-encoded path and is designed as the tail, workflow.md says the path comes last, and five pusher tests read it with ends_with - appending after it broke exactly those, while inserting before it breaks only a fixed-index reader and the repo has none, and those five pass unmodified. the key set reads only id.pub.rotated and never the private id.rotated spelling, which holds a secret key and has no business answering a question about authorship. a machine that pulled rather than rotated holds no archive, so there your own pre-rotation work reads as foreign, which fails toward asking for an approval nobody owed and never toward waiving one, and the block and workflow.md both say so. red under mutation, counts read each time: the key set ignoring the archive (52 passed and 1 failed), the filter admitting the private archive spelling (52 passed and 1 failed), and an unauthored change read as the lander's own (202 passed and 1 failed), each restored to 53, 53 and 203. ADR 0092's status now says which sections are built, because section 5 - the gating column, review/approve and the refusals - is part 2 and until it lands detection reports and nothing refuses, which is that section's decided default anyway. no migration, no wire or format byte moves, and nothing on a host moves, so this owes no deploy. the workspace suite is green (4189 passed over 133 binaries) (#2178)
- ↳ supersedes
70c6feed· not stored here - Kept, not rewritten: a carry replays a suffix as a new version and the one it replaced stays addressable. That is the difference between a carry and a rebase.
nothing readable · 10 sealed · 10 internal - ↳ supersedes
- the browser sdk transport stops waiting on a silent host indefinitely, because fetch has no timeout of its own and in a browser nothing stands in for one: HttpRelayTransport now aborts through a watchdog that waits for an answer to begin for its idle budget plus the upload at the uplink floor, then re-arms on each chunk of the body, so an answer that keeps arriving is not cut for its size and one that stops fails once the idle budget passes with no bytes, the same policy #2079 gave the native client, with TRANSFER_IDLE_MS mirroring wire::TRANSFER_IDLE and each side now naming the copy on the other. a budget that runs out throws a TransportError naming which wait it was and how long, and the adapter passes that through instead of relabelling it as an unreachable host, one rule in errors.ts asked by every catch around a transport call. TWO OF THE THREE ITEMS ARE REFUTED RATHER THAN BUILT: the forge blocking clients the ticket named both sit inside cfg(test) modules, in the tree today and at the rev the ticket was filed on, and the forge talks to its object store only through loot_s3::S3Client, which already names its own 60 second timeout; and the upload side header wait was fixed under #2079 by the byte-derived transfer budget, whose refusal already names the answer budget. the frozen-tab limit is written down rather than fixed, since a timer on the page loop measures the page clock and not peer silence, which is #2079 in a browser. red under mutation, counts read each time: the re-arm moved outside the read loop failed the steady-answer pin (6 passed and 1 failed), the answer wait ignoring the bytes failed two (5 passed and 2 failed), the watchdog verdict dropped failed all three budget pins on a bare AbortError (4 passed and 3 failed), and the adapter relabelling every throw failed the pass-through pin (0 passed and 1 failed), each restored. the sdk suite is green against release binaries built in the lane (137 passed over 13 files), no migration, no wire or format byte moves and no host behaviour moves, so this owes no deploy. the workspace suite is green (4179 passed over 133 binaries, 9 ignored) (#2070)nothing readable · 10 sealed · 10 internal
- the figure the #1890 verdict rests on is now a column in the table it was missing from, taken the way that table was taken: the pre-#1572 clone spelling put back and the ignored sweep run once per arm, which reads the splice band at 41.3, 39.2, 38.0 and 37.2 percent of its own region on the before binary against 11.2, 6.2, 3.4 and 1.3 shipped, so the 37 percent the conclusion turned on is readable from the tree instead of from a session transcript, and the re-take reproduced every before and after cell already there to within one allocation. THE VERDICT IS NARROWED TO WHAT THE COUNT SHOWS: withdrawing the band ceiling and confirming the clean delta are separate acts and only the first was done, because a count does not convert to milliseconds - what is retired is a contradiction between two readings rather than the spread among the timings, and the published magnitude stays where they left it. two precision defects in the #2242 retraction go with it: the move over that size range is about 4.8x in the call and about 3.4x as a process and the sentence now names which, and rests on one reading is withdrawn because #2223 records no repetition count for its whole-verb rows, so what is known is that the figure does not reproduce and not how it was taken. the sweep doc had its axes inverted against the code under it and now states the property instead, that the shapes fall into runs holding one axis while the other moves, and the list class is answered for the third time in this run by replacing the two named untimed controls with the questions that decide membership. the duplicated in-loop guards are extracted: prove_the_position_has_not_ingested and prove_the_tip_was_spliced are asked by the timing and the count alike, each refusal written once and true of both. the named positive control is refused rather than built, because an in-tree knowingly worse arm means a wrong body in production code, and the before column now published stands in its place. sdk records the node type-stripping floor its .ts import imposes in engines, where npm reads it. red under mutation, counts read each time: the position control inverted failed all three allocation pins (0 passed and 3 failed, naming a position holding 1 change), and the splice control fed a false answer failed all three on its new text (0 passed and 3 failed), each restored to 3 passed. no migration, no wire or format byte moves and no host behaviour moves, so this owes no deploy. the workspace suite is green (4179 passed over 133 binaries, 9 ignored) (#2247)nothing readable · 8 sealed · 8 internal
- the second failure mode in the tutorial interrupt test stops being uncharacterized, because a message for it already existed in main CI and what it shows is a handler reporting a CALL rather than a PATH: run 35373927304 on 5e66658 went red on ubuntu-latest in 0.24 seconds with the interrupted - removed line on stdout and that path still on disk, at the agreement assertion, on a head that already carried the #2037 fix - so the change read as closing this did not, and the two sightings the ticket comment carries are the pre-#2037 assertion and are the defect #2037 named. what is established is the disagreement and nothing past it: why remove_dir_all answered success is NOT known and is not written down as if it were. so sweep_interrupted_tree asks the filesystem after a removal reports success and prints the refusal when the path is still there or cannot be read, which is the property #2037 asserted and did not reach, and the interrupt test says what a surviving tree held instead of printing true. THE SATURATION HALF IS A NEGATIVE RESULT STATED WITH ITS COUNT: 150 runs with capture on from the first, under up to four concurrent workspace builds at -j 6 over a scratch copy, against 12 idle, none failed, plus 300 more under a load generator discarded for measuring 7.9 percent busy while it spent its wall clock deleting a 16 GB target directory. the load that did run was bursty rather than sustained and the file says so, a median of 3.6 percent against a peak of 99.8, while the spread moved from 0.38 to 0.44 seconds idle to 0.48 to 126.4 loaded - and none in 450 does not retire one in 534, which is recorded too. two smaller corrections ride along, both read off runs: the fixture behind a tree already gone reports as removed answered NotFound over a directory it had left standing, so it pinned the mapping and never the filesystem, and the sentence saying the swept sandbox holds one heartbeat file missed the empty alice directory that walk mints ahead of its first prompt. workflow.md gains the shape its own falsifier asked to be told about, a test that fails at a closing assertion because the process it drove reported the state that assertion checks. red under mutation, counts read each time: the filesystem confirmation deleted failed the new pin (10 passed and 1 failed, the removal line over a tree still there), the old fixture restored failed the already-gone pin (10 passed and 1 failed, the refusal naming the path), and through the integration test a handler whose removal does nothing failed it naming the remnant as still there holding alice and heartbeat (2 passed and 1 failed), each restored to 11 passed and to 3 passed. no migration, no wire or format byte moves and no host behaviour moves, so this owes no deploy. the workspace suite is green (4179 passed over 133 binaries, 9 ignored) (#1916)nothing readable · 4 sealed · 4 internal
- the readiness waits over a spawned binary in sdk stop being budgets and start ending on the host itself, and the reason they were never seen to fire is now written where a reader meets them: every behaviour suite that stands a relay up polled a real loot serve behind Date.now() plus 20 seconds, which is a SMALLER budget than the 30 seconds that already lost in #1693 over a LARGER startup, and the 60 second beforeAll hook outside it sat inside the same measured spread, so raising the inner number alone would have left the effective bound where it was. the new sdk/test/spawned-host.ts has three endings and only the last is a clock: the probe succeeding, which is the condition these suites actually need since it proves the host ANSWERS rather than that it printed a line; the child exiting, reported with its code instead of polled at for the rest of the wait, seeded from the childs recorded state as well as its future exit event so a host already gone is not waited on; and a backstop carrying the same 600 seconds tutorial.rs carries, off the same saturated measurement and for the same reason, with the hook timeout derived strictly larger so the named refusal wins the race. the fixture generator imports that module rather than restating it, which node does by stripping the types itself. THE BEHAVIOUR SUITES CANNOT PIN ANY OF THIS AND THE CONTROL SAYS SO: with the condition deleted outright the whole sdk suite still read 11 files passed, because an idle machine has the relay up before the next await, so a dedicated pin over a spawned node child carries it instead. red under mutation, counts read each time: the exit ending deleted failed two of the three pins on vitests own timeout (2 failed and 1 passed, both at 5000ms, the wait running on to the backstop), the already-gone seed dropped failed the third alone (1 failed and 2 passed), and the probe discarded so the wait returns on the first look failed all three (3 failed and 0 passed, asked 1 against 3 and two promises resolving instead of rejecting), each restored to 3 passed and the sdk suite to 129 passed over 12 files. the gate question is NOT answered here and #2080 still owns it, but the sdk README now states what decides it and gives the grep that checks it rather than listing the workflows, and tutorial.rs stops naming budgets that no longer exist. no migration, no wire or format byte moves and no host behaviour moves, so this owes no deploy. the workspace suite is green (4178 passed over 133 binaries, 9 ignored) (#1914)nothing readable · 10 sealed · 10 internal
- the attribution the #2223 refutation asserted is now measured and the conclusion stands, because a nested count region says which span the surviving reads belong to: with this body live the open inside vouch_if_on_disk reads 0 files over 200 paths, and with the ceiling in place that same span reads 199 on status and 200 on surface, so the reads move rather than go away and a header only body would add its own opens on top of them. the size axis gains the fourth column the ticket asked for and the effect does not run away with it: at 1 MB objects over a 210 MB store the ceiling leads by about 8.5 ms on either verb, under a fiftieth of the verb, against 14.24 and 20.91 and 69.00 and 455.60 ms for status and 16.67 and 22.92 and 67.37 and 461.02 for surface. THE 3.3x DISCREPANCY IS RECONCILED AND THE GUESS ABOUT IT WAS WRONG: the loose store weighs 213,800 and 3,285,800 and 26,223,400 bytes here, the three figures the ticket body records, to the byte, so both arms ran the same incompressible fixture and compressibility explains nothing; what differs is that the ticket timed the process, which reads 22.7 and 29.8 and 77.6 ms over these same fixtures and meets it at the two smaller sizes, while its largest reading of 226.2 ms reproduces in neither arrangement against seven repetitions spanning 69.00 to 71.96 ms and a first unwarmed reading of 71.13. so the 11x move it was filed on rests on one reading, and the move these fixtures carry over that range is about 3.4x. #2226 gains the depth axis its own cost model lives on, swept without touching MISSING_DEPTH so no fixture and no workload_id moves: at width 200 the walk is linear in depth over 1.16 and 4.66 and 18.49 and 74.04 ms at depths 8 and 32 and 128 and 512, the probe deleted binary reads 1.08 and 4.38 and 17.38 and 68.82, and the probe share does not move across that sweep, which it would not, because probes and manifest entries are both O(changes x paths). that doc now cites #2240, records what the half is, and says the probe is two map lookups rather than one. ONE SWEEP ITEM IS REFUTED RATHER THAN FIXED: the short line in gate.rs is not a rewrap leftover, it is short because the 33 character intra doc link after it does not fit at this column norm, and the same file carries a 44 column line for the same link higher up. the cheapest item had the only seam and it is pinned: the WORK_COUNTERS exclusion table read as the excluded variants and nothing else, which is the definer #2234 sent a reader to, and it is now read back against Work::ALL filtered by gate::COUNTERS. red under mutation, counts read each time: graph_sorts dropped from gate::COUNTERS failed the new pin (0 passed and 1 failed, three variants named against four owed), the PolicyParses row deleted failed it (0 passed and 1 failed, two against three), and the table header renamed so the scan reaches no rows failed the anti vacuity guard (0 passed and 1 failed), each restored to 1 passed and the file to 12 passed. the two ADR 0073 enumerations are deleted rather than corrected, the CONTEXT and gate paragraphs are reflowed with identical word streams, and no code outside a test changes, so this owes no deploy. the workspace suite is green (4178 passed over 133 binaries, 9 ignored) (#2242)nothing readable · 8 sealed · 8 internal
- the residual #1888 could not settle is settled by counting instead of timing, and the answer is that the clean pair was never in excess: a new measure half counts allocations over the whole of ingest_shared_lineage and splits the call into the eager graph read and the walk and splice, two bands that sum to the region EXACTLY, counter by counter, because the region is those two doors with a map lookup between them - the property no stop instrument can have, and #1888 recorded its own stop reading an early return as costing more than the work it skips in six rounds out of six. at the width the half pins, the pre-#1572 clone spelling put back reads 6488, 12512, 24552 and 96803 allocations at depths 8, 16, 32 and 128 against 4289, 8113, 15754 and 61606 shipped: a removal of 1.37 per manifest entry at every depth in that table and of 33.9 to 36.4 percent of the region, both arms linear with per-entry slopes agreeing to four significant figures, so the same fit carries the depth #1888 measured at to about 281000 removed allocations out of 771000, or 36.5 percent of the times the region reaches the heap. THE TIMED BAND WAS NEVER A CEILING ON THIS CHANGE: the walk and splice the stop instrument left 16.2 ms, a tenth of the region by time, makes 37 percent of it by allocation, so a clean delta of 13.6 percent sits BELOW the share of the work the change removes rather than above it, and nothing is left over to explain. the published magnitude does not move, because a count does not convert to milliseconds and this instrument prefers none of the three timings - what is withdrawn is the reason to distrust the largest of them. no opt-in half is added and Fixtures::gated is untouched, because a land builds release, where the allocation control refuses rather than reporting a false zero, so this reading is a test one taken by hand: its own binary installs the allocator no library may install, opens on require_allocation_counting, and carries an ignored sweep. HUNT-PERF gains the marginal-cost trap it was missing, with the measured instance where a discarded second read reads over 100 percent of the region it is supposed to be a fraction of, the band decomposition that replaces it, and the note that a summing decomposition is still a perturbing one. red under mutation, counts read each time: the clone spelling restored failed the splice-band pin (2 passed and 1 failed, 190 allocations against 73 entries), the pool read moved outside its band failed the sum control in all three (0 passed and 3 failed, 65 against 272), the global allocator removed failed all three on the control itself (0 passed and 3 failed), and a fixture ignoring the declared width failed the scaling pin (2 passed and 1 failed, 0 entries added), each restored to 3 passed. no migration, no wire or format byte moves and no host behaviour moves, so this owes no deploy. the workspace suite is green (4177 passed over 133 binaries, 9 ignored) (#1890)nothing readable · 5 sealed · 5 internal
- the visibility gate goes on reading the whole object and the ticket is refused on a ceiling, because a binary with the read deleted outright still reads one loose object file per recorded path: can_open consults facts that sit beside the ciphertext rather than inside it, so reading less looked free, and the measurement says the read does not belong to this seam at all. a knowingly wrong body answering from the store index and the keyring alone, reading no object bytes, is strictly less work than any header only spelling could do, and over 200 paths of incompressible bytes at 1 KB and 16 KB and 128 KB it moves loot status from 14.13 and 20.57 and 67.91 ms to 14.21 and 20.84 and 66.73 ms and loot surface from 16.64 and 23.11 and 59.09 ms to 16.58 and 23.18 and 56.16 ms, a spread that runs in both directions and peaks at 5 percent on the largest fixture. THE COUNTER IS WHAT MAKES THAT A REFUTATION RATHER THAN A QUIET READING: object_disk_reads reads 200 on every one of those twelve readings, before and after alike, while object_gets falls from 401 to 201 on status and from 600 to 400 on surface, so the span really was deleted and the file reads moved rather than went away. they move to the pass that wants the bytes - same_content behind status and the clobber guard behind surface - which the read memo makes free today and which would pay for the whole tree if this call stopped filling it, and a header only body would add its own opens on top of that. the signal the ticket opened with is therefore not added either: the read counter that exists already settles the question, and a byte counter would have read the same total under the ceiling and a larger one under any header only body. so the code is UNCHANGED and what lands is the reading, recorded on can_open where the next hunt reads it before refiling. one list class defect is corrected beside it: the sentence naming two further sites that could move to this predicate named a merge site that cannot, since the readability question there sits on the theirs side of a cherry pick delta restriction and its own comment says the content must be read to re-seal it, and the sentence now states what decides membership instead of naming members. no mutation proof, because nothing was fixed and no pin was added, and the measurement controls stand in its place: the get counter moves, so the instrument is not blind to the span the ceiling deletes, and restoring the pristine body returned both counters and both timings to the landing band. no migration, no wire or format byte moves and no host behaviour moves, so this owes no deploy. the workspace suite is green (4174 passed over 132 binaries, 8 ignored) (#2223)nothing readable · 3 sealed · 3 internal
- the probe memo #2226 asked for was built and measured and then refused, because the probe it removes is under a twentieth of the walk: locally_missing_objects asks its membership question once per change and manifest entry pair while the answer is a set of distinct addresses, which is 25600 probes for 327 answers on a 200 path missing fixture, and the obvious shape is the probed BTreeSet copied from offered_objects. the named signal reads WORSE with it, in a paired opt-in --missing A/B at the fixture depth, one binary built from each arm and the gate reporting an idle machine on every reading: 18.3 ms to 20.9 ms at width 200 and 96 ms to 110 ms at width 800, because the probe here is a single BTreeMap lookup in the store index rather than the object read the sibling spells, so a set insert over a map of the same order costs more than the lookup it skips. THE CEILING IS WHAT MAKES THAT A REFUTATION RATHER THAN ONE BAD MEMO: a knowingly wrong binary with the probe deleted outright reads 17.5 ms and 91.5 ms on those two fixtures, so any probe-side change on this walk is bidding for under a twentieth of it, and with the manifest walk deleted too the same measurement reads 0.03 ms, which says what the walk spends is materializing and walking each deferred manifest to read the addresses out of it, a loot-codec seam and not this one. so the code is UNCHANGED and what lands is the reading, recorded on the function where the next hunt reads it before refiling. one list-class defect is corrected in the same doc: three callers stood asserted as the whole set beside a pub passthrough on Workspace that is a fourth, and the sentence now names what the callers share instead of how many there are. no mutation proof, because nothing was fixed and no pin was added, and the measurement controls stand in its place: the ceiling binary moves the reading at all, so the half is not blind to the probe, and restoring the pristine body returned it to the landing band. no migration, no wire or format byte moves and no host behaviour moves, so this owes no deploy. the workspace suite is green (4174 passed over 132 binaries, 8 ignored) (#2226)nothing readable · 2 sealed · 2 internal