Changes touching this path

  • day 0: loot hosts loot f4c30e75 · dbf3dbe6…
  • evidence: crew minted and verified (#86) 1fada823 · dbf3dbe6…diff
  • catch up: GB1 loot ferry lands (PR #114) 6816f50d · dbf3dbe6…diff
  • normalize working tree to LF: byte-stable co-located bridge (.gitattributes -text) e58fdda6 · dbf3dbe6…diff
  • Liveness: one loot-core home for live/superseded/divergent/parked + the head partition (#216); CONTEXT.md vocabulary; rides along: cargo-dist release config (dist-workspace.toml, release workflow, 0.1.0 manifests) from the install-prototype session (loot-site map, #206) 3630469b · dbf3dbe6…diff
  • loot-first in Rust: land policy behind a Forge seam (#218) — new loot-first bin crate (ledger/forge/policy/orchestrator), in-process Workspace reads via loot-cli lib split, typed pr-map owner + shared ferry::WipState; every policy decide-tested against a fake forge. Build+test only; shadow-run, real land, and ps1 deletion remain operator-gated. 58c28888 · dbf3dbe6…diff
  • Run the pre-land gate in the landing position's tree, not the shared root (#287) orchestrator::paths() derived its one `root` from ws.dot() -- always the SHARED store's .loot -- so a land from a lane ran `cargo test` (and the relay push) in the primary checkout's possibly-stale tree. The #284 land was validated only because the operator had run the suite in the lane by hand. Same shared-vs-position confusion as the #229 mirror-gitdir and gh-cwd dogfood fixes. Fix: split Paths.root into `position` (Workspace::root(), new accessor: the lane dir from a lane, the checkout on the primary) and `checkout` (the shared .loot's parent). The pre-land cargo test now runs in `position` -- the tree about to be signed. relay_push decision: it now also runs in `position`, deliberately. loot push discovers .loot from its cwd and a lane's .loot points at the shared store, so the push ships the same store from either dir -- but from the position it reads the tip this land just signed (has_unsigned_tip) and records its push op in the landing position's own oplog instead of writing the primary's (ADR 0034 single-writer). The spawned binary is now resolved beside the running loot-first (one cargo build produces the pair) with the old target/release fallback, because a lane tree carries no release build of its own. The drift guard's git reads and init-hook stay on `checkout` on purpose: only the primary has a .git; run from a lane dir every probe would fail and silently mute the guard. Tests: loot-first regression pair (lane position vs checkout, primary degenerate case) built on a real spawned lane via temp-dir helpers; loot-cli locks root()-vs-dot() semantics at the Workspace seam. 46f1b598 · dbf3dbe6…diff
  • primary catch-up: adopt landed main (post #287) 7e3bbe42 · dbf3dbe6…diff
  • Extract loot-codec + wasm core; de-risk the in-memory SDK boundary (#423) Slice 1 (the tracer bullet) is a multi-session build; this lands its riskiest integration — the Rust→WASM crypto/codec boundary — proven end-to-end, per the #381 "verify spikes before building" mandate. Spike result (ADR 0040): zstd-sys will not build for wasm32 (needs clang for its C), while aes-gcm/blake3/ed25519/getrandom-js do. So: - Extract `loot-codec`: a no-fs, wasm-buildable crate holding the byte format, sync-bundle codec, sealed content (AES-GCM + blake3), and the leaf types (Oid/Visibility/RepoError/ChangeNode). loot-core depends on it and re-exports every item at its original path — a pure relocation, proven by the unchanged, still-green workspace (767 tests pass). - zstd is an optional loot-codec feature (default on for the native host; off for wasm). A new zstd-free `sealed::decrypt` single-sources the AES-GCM step so native `open` and the wasm core share decrypt code; public content is inflated host-side in JS. - `loot-wasm`: wasm-bindgen exports (bundle decode, decrypt, blake3 address) over a pure native-testable `core`, plus the minimal diskless identity (generate/fromSeed/publicKey) on ed25519 directly — not loot-identity, whose OpenSSH/passphrase machinery is native-only (#383). - Golden-parity harness (loot-wasm/tests/parity.rs) runs identical assertions natively and under `wasm-pack test --node`, freezing native-computed vectors; both green. Remaining for slice 1 (next sessions): the TS package, fetch transport + client-side path-scoping, host-side zstd, and the live-relay behavior suite. Refs #423, #421. ADR 0040. 491cf780 · dbf3dbe6…diff
  • Slice 4 (#426): author private (sealed) content — visibility + guards Adds the authorship/sealing side of private content to the in-memory TS SDK, plus same-session read-back. Cross-session grant delivery stays deferred (#383). ECIES to wasm (single-sourced, no drift): move the key_seal composition (ECDH over X25519 + ChaCha20-Poly1305, the "loot grant key wrap 2024" KDF, ed25519→ x25519 derivation, 80-byte wire format) into the wasm-buildable loot-codec. loot-identity::key_seal now delegates to it, preserving its IdentityError surface so loot-cli/loot-net callers are untouched. loot-wasm's Identity gains x25519PublicKey / sealKeyToSelf / unsealKey. ChangeBuilder::put now seals Restricted content: the content key is ECIES-wrapped to the author (never rides in the bundle), so the relay stores only ciphertext. AuthoredChange exposes the private grants (oid → wrapped key) for the SDK's RAM keyring; read() unwraps via the wasm identity to read own content back. SDK guard model: edit(path, bytes, { visibility }) inherits the path's current visibility (new path → public); describe/push take { allowDemote, allowReveal } and a visibility change without the matching guard is refused (GuardError). list()/read() report each entry's visibility. Seams: parity.rs freezes the deterministic x25519 pubkey + a native ECIES wrapped-key vector and asserts native AND wasm unseal it (round-trip + wrong- identity refusal). private.behavior.test.ts drives sealing-stores-ciphertext, same-session read-back, keyless-reader refusal, and the guard cases. ac8b8ef5 · dbf3dbe6…diff
  • loot-revset: query-algebra crate for selecting sets of changes (#394) cdec28d1 · dbf3dbe6…diff
  • loot remote info + relay GET /info endpoint (#10) 0d05d373 · dbf3dbe6…diff
  • Forge service skeleton: /ingest, the sync surface, and CLI forge remotes (#502) A new `loot-forge` crate and binary, sibling to the relay and never a mutation of it: `loot-net` is a dependency of `loot-cli`, so a storage driver in the relay would ship inside the released CLI binary (ADR 0041:53-57). The ban runs one way -- the forge may use the other crates; none of them may gain a driver. Every endpoint is envelope-authenticated, where a relay authenticates only /stow. The one declared exception is GET /info: a client reads the capability probe before it knows how to authenticate. POST /ingest = envelope_sign(bundle || head_declaration), one signature over both, so a genuine bundle can never be replayed under someone else's head claim. CAS first; nothing is written before the generation compare. A lost CAS answers with a 409 BODY carrying the current generation and head set -- behind, not bounced -- so the client re-carries with no extra round trip. /stow keeps relay parity and moves no ref, and consumes no generation either, since a client reads the generation once and then stows N batches. It DEFERS a change whose objects have not all arrived; /ingest REFUSES one, because a declared head whose objects are missing is a repo no pull can complete. That asymmetry is what keeping /stow is for. Bundles are filtered PER OBJECT by the caller's access class, closing the leak the ticket names: bundle_impl ships a key for every ANYONE-granted object, i.e. every Internal object. Change metadata rides the second #494 axis and gates the whole walk -- a ChangeNode carries the full tree, so shipping one while withholding its bytes still discloses every path, address, message and author. Validation before any write: author signatures, and a tree whose visibility contradicts its objects is rejected. vis_tag is derived from the SealedObject and never trusted from the tree, which is what makes content-addressed upserts safe. Forge purge policy: burn iff burner == object.introduced_by, recorded at first arrival as introducing.author else the envelope pubkey; maroon read off grant_log, global and exact. Both are judged before any object is stored and outside the CAS transaction. Repos are pubkey-addressed at /k/<pubkey>/<repo>, auto-created on first push, and may_push holds iff the signer owns the namespace. resolve_remote is untouched -- still no default host. CORS from day one. Storage sits behind three narrow traits with in-process reference implementations the whole suite runs against; the Postgres DDL they mirror is checked in at docs/sql/forge-schema.sql and its driver is #516. CLI: /info capability detection, the live head declaration off the Liveness view, and a refusal to push a lane -- v1 ingests the primary position only. verify_authored_change moves to loot-codec so the forge runs the identical gate without linking the fs-hardwired engine. Recorded in ADR 0041, the spec and CONTEXT.md: the forge stores no content key but a published one, because filing the ANYONE key lane would let the server read every Internal object, and the keystone is that only Published is server-readable. The cost is stated rather than hidden -- a fresh clone of your own Internal content from a forge is ciphertext you cannot open until #488 lands. Closes #502. 300e06ff · dbf3dbe6…diff
  • Give the forge an operator channel, with one redaction rule (#522) f9ca9f91 · dbf3dbe6…diff
  • loot tutorial: remove the temp tree on Ctrl-C (#513) fa318ba8 · dbf3dbe6…diff
  • Forge storage drivers: Postgres metadata tier, S3 blob tier, and one conformance suite both must pass (#516) f65970e6 · dbf3dbe6…diff
  • Release v0.2.0: lockstep versions, dist-able loot-forge, pinned rust-toolchain (#628) 7eef425d · dbf3dbe6…diff
  • loot-perf: the harness crate with fixture, measurement, and record seams (#634) ef9fd595 · dbf3dbe6…diff
  • emit_snapshot calls verbs in-process; test-support feature retired (#661) 8033f451 · dbf3dbe6…diff
  • loot-forge drains on SIGTERM and SIGINT instead of waiting out SIGKILL (#657) 0485a488 · dbf3dbe6…diff
  • Cargo.lock: sync the loot-perf serde rows the #636 land left out of the lockfile 00955a9a · dbf3dbe6…diff
  • loot-perf: the CLI tier — hyperfine-timed push, receiver-counted wire bytes, raw samples and load on the record (#635) 3588811d · dbf3dbe6…diff
  • Cargo.lock: record loot-perf's blake3 dependency (#635) bb48b171 · dbf3dbe6…diff
  • the pre-land perf gate: counters block at 0 percent, timing warns, and a lane pins itself (#637) 52a2341e · dbf3dbe6…diff
  • loot-first: exec the perf gate from a binary built from the position (#712) The gate measured in-process, exercising whatever loot-core was compiled into the running loot-first.exe while PinnedCheckout stamped the point with the position revision — and nothing rebuilds before the gate, so a stale binary reported perf: no move for a revision that provably moved a counter (#638's live find). The alarm failed OFF, silently. Now loot-first land builds loot-perf-gate from the landing position's own tree (cargo build --release --locked -p loot-perf --bin loot-perf-gate — cargo's staleness check is the provenance proof) and execs it. A land mode on the bin (--land <change> --stash <file>) keeps the split the record demands: the verdict decides before cargo test, and the would-be record line is stashed outside the tree for the land to append verbatim as its last act, so the recorded bytes are produced end to end by the position's own code. loot-first deliberately no longer links loot-perf: no compiled-in harness, no way to measure the wrong code. --locked keeps #698 held: a stale Cargo.lock is refused, never rewritten into the signed change; target/ stays outside the seal (verified live — loot status clean across the build). Measured cost: +0.16 s build on an already-built position, 3.1 s when the gate's own crates changed, 12.6 s from a stone-cold lane — against the unchanged 2.2-2.6 s measurement and the multi-minute cargo test that follows. 0d9c6195 · dbf3dbe6…diff
  • release: bump loot-cli and loot-forge to v0.3.0 for a relay+forge redeploy 15ba4ad7 · dbf3dbe6…diff
  • release: bump loot-cli and loot-forge to v0.3.1 for a relay+forge redeploy beab8725 · dbf3dbe6…diff
  • a change records when it was authored, so a projected commit stops reading '6 years ago' on GitHub (ADR 0043) loot changes carried no timestamp, so the git bridge fabricated one: BASE_EPOCH + generation, one second per ancestor depth from a 2020 epoch. All 526 commits sat inside seven minutes of September 2020. Earlier repairs (#626's floor, the missing-generation refusal) fixed ORDERING and never touched the absolute date, which is why this kept coming back. ADR 0028 inherited the no-timestamp constraint rather than choosing it. The real reason is upstream: a version id is blake3 over authored content, and a clock inside that hash gives two peers different ids for identical content, destroying the dedup and convergence of ADR 0001/0004. So authored_at rides the label seam ADR 0029 already cut for change_id -- covered by the finalize signature (no relay can restamp it) but never folded into the version id. The wasm golden vectors prove the separation held: FROZEN_VERSION_ID, FROZEN_OBJ_ADDR and FROZEN_SIGN are byte-identical, and only the version marker and one presence byte moved. Advisory, and never an ordering input: a self-reported clock is a claim, not evidence (ADR 0025). in_order/ids_topo, buoy and path_touch.ordinal are all untouched, the forge indexes nothing on it, and the projection floors it past every git parent so ancestry holds whoever's clock is wrong. Format v11: additive for readers (a v<=10 change decodes as None and an absent timestamp adds nothing to the signed message, so every existing signature still verifies), breaking for writers, so loot-cli and loot-forge go to 0.4.0 in lockstep. Forge migration 0004 stores it as bigint, not timestamptz -- signed data must round-trip bit-exact or pullers reject the change. Only new work gets real dates. Every commit on main predates v11, main is push-fast-forward-only, and backfilling would fabricate the very claim this replaces. Perf-Baseline: reset the change body grew one presence byte, plus eight where a timestamp is present, so bundle_bytes/store_bytes/wire_bytes step once at the v11 boundary eaa56d99 · dbf3dbe6…diff
  • release: bump loot-cli and loot-forge to v0.4.1 for the forge ingress deploy (#690) 70c27a07 · dbf3dbe6…diff
  • adopt: catch up to landed main f266c856 da01da3d · dbf3dbe6…diff
  • release: bump loot-cli and loot-forge to v0.4.2 for the forge ingress deploy (#690) f266c856 · dbf3dbe6…diff
  • the converge seam merges with a diff3 three-way, so two disjoint edits compose instead of one silently dropping the other (#790) 2fd16e7f · dbf3dbe6…diff
  • the metadata tier speaks TLS when the URL asks for it, and refuses rather than falling back to plaintext (#689) adf57547 · dbf3dbe6…diff
  • release: bump loot-cli and loot-forge to v0.4.3 for the relay grant-auth redeploy (#818) a595bfd9 · dbf3dbe6…diff
  • release: bump loot-cli and loot-forge to v0.4.4 for the 0006 migrate and the gc-tmp and auth-preamble redeploys (#895) 324e05c8 · dbf3dbe6…diff
  • the gate learns to see the wire: negotiation counters on a fixture with real history depth, and wire_bytes stops pretending (#847) 8fd4e950 · dbf3dbe6…diff
  • release: bump loot-cli and loot-forge to v0.4.5 for the 0007 identity migrate and the /account deploy (#753) 2f7eb70e · dbf3dbe6…diff
  • release: bump loot-cli and loot-forge to v0.4.6 so a deploy release carries the 0052 relay abandon-head (#996) 470db09e · dbf3dbe6…diff
  • a store knows which machine materialized it: an arrived copy refuses to record until loot rehome, so a bootstrap cannot silently capture every sealed path as deleted (#986) 4c15a2ca · dbf3dbe6…diff
  • release: bump loot-cli and loot-forge to v0.4.7 so the public installer matrix has a tag to build off GitHub (#1021) 042e1270 · dbf3dbe6…diff
  • a browser-minted identity reaches the CLI: loot id recover decodes the 24-word phrase to the exact seed, and a shared vector holds both implementations to the same bytes (#926) fd153402 · dbf3dbe6…diff
  • release: bump loot-cli and loot-forge to v0.4.8 so a deploy release carries the account-aware write rule (#927) f69ff6fd · dbf3dbe6…diff
  • release: bump loot-cli and loot-forge to v0.4.9 so a deployed binary carries migration 0008 (#927) 40c9e2d6 · dbf3dbe6…diff
  • release: bump loot-cli and loot-forge to v0.4.10 so a deployed forge and site can read credentials from a file (#1034) 21181dc8 · dbf3dbe6…diff
  • release: bump loot-cli and loot-forge to v0.4.11 so the deployed forge carries migration 0009's owner tier and a /wants that answers a whole address list (#1056) 43ce995d · dbf3dbe6…diff
  • release: bump to v0.4.12 so the deployed forge carries an ingest that loads the arriving delta's visibilities in one query (#1072) 44dbe40f · dbf3dbe6…diff
  • release: bump to v0.4.13 so the public catalog can carry the allow widening that its own durable-host guide documents (#1100) ec7728db · dbf3dbe6…diff
  • release: bump to v0.4.14 so the deployed forge can serve the grants peek that #1114 mounted 076042d3 · dbf3dbe6…diff
  • the relay store goes behind a RelayStorage seam, the SigV4 driver moves to its own crate, and an object-store relay is a second deployable the CLI never ships (#38) 330df915 · dbf3dbe6…diff
  • release: bump to v0.4.15 so the deployed forge carries the /offer walk that stopped probing Postgres and R2 once per (change x entry) (#1154) 0d37053d · dbf3dbe6…diff
  • release: bump to v0.4.16 so the public matrix carries the custody passphrase, the widened mis-seal gate, loot converge and the honest doctor e61b48cc · dbf3dbe6…diff
  • the relay contract is asserted once and both backends run it, the mailbox choreography moves behind a blanket impl a backend cannot override, and the fs and bucket acks stop disagreeing about which of them was right (#1164) eb659a91 · dbf3dbe6…diff
  • the unlock session holds the seed sealed under this repo's own ciphertext instead of the passphrase you typed, so a synced config directory leaks a window and not a secret you reuse elsewhere, and the passphrase is zeroized from the moment loot owns it (#1173) 1943d2c7 · dbf3dbe6…diff
  • loot 0.4.17: the Known Issues page is re-reviewed against the binary it now names, four entries move because their fixes shipped, and the land runbook learns that a skip drops three gates and not one 44de9890 · dbf3dbe6…diff
  • loot 0.4.18: the Known Issues page is re-reviewed against the binary it now names, the seal-away-from-yourself entry leaves because the lockout gate shipped, and the reverted-peer-edit entry narrows to the locked pull that can still do it 29f571e7 · dbf3dbe6…diff
  • loot converge stops buying a second whole-tree read for the note it prints, because the implicit capture one seam below has already read that tree, and the series added to catch exactly this learns to fail: its fixture had neither the co-located checkout nor the standing fork the branch needs, so it could not reach the cost it was watching (#1401) 12c1207a · dbf3dbe6…diff
  • loot-revset stops being a finished parser wired to nothing, because `loot log <revset>` and `loot shortlog` now read one door that tries the historic selector shape FIRST and only parses a non-selector word as an expression — which is what keeps `loot log <change-id>` working, since the crate reads bare words as hex while a change id is the letters k-z, so the grammar was never the strict superset its own docs and this ticket both claimed (#1495) dd223bf0 · dbf3dbe6…diff
  • the harness stops measuring only what a run produces and starts measuring what it does, because the three work counters this run built and threw away are now one family in loot-count that a single during region reads whole, with logical object opens and disk reads kept as separate numbers since 2,000 of the first can cost zero of the second, and the family ships compiled out rather than as cfg(test) one-offs because cfg(test) reaches neither an integration test nor loot-perf and would foreclose the gate #1602 exists to add, at a cost measured at zero bytes in the released binary (#1600) 482a72c1 · dbf3dbe6…diff
  • the land gate stops watching only what a run produces and starts watching what it does, because two work counters now ride the untimed batch it was already measuring so they cost no fixture, no repetition and no workload_id move and orphan no baseline, while the twelve opt-in timings stay ungated on the argument that a timing is dropped by the load policy exactly on the busy machine a land runs on, and the count feature stays off by default because turning it on in a manifest was measured to change the loot.exe a plain workspace build produces (#1602) f641ea1d · dbf3dbe6…diff
  • loot 0.4.19: the Known Issues page is re-read against the binary it now names by running it rather than re-reading its source, the six entries pin 5 held leave on the release that finally carries their fixes, and the pin retires with its emptied table on its own instruction — the re-run is what caught the evolog entry rotting without an edit, because loot log grew a when column somewhere in this range and the entry had claimed for two releases that evolog was the only time the CLI prints, and the recovery-door note is rewritten rather than deleted since loot id phrase shipping does not change that both it and recover open the repo first; and the restore no-op test stops calling its own instrument blind when the clock is the coarse thing, because it stamped the setup write and the restore microseconds apart against a file clock that ticks every 15.6 ms — it was failing four runs in five on main, standalone, while passing inside a full suite slow enough to straddle a tick, so it now probes for a distinguishable mtime rather than sleeping a constant that would bake this filesystem granularity into the assertion 4dcc9ff6 · dbf3dbe6…diff
  • six sentences that read wider than their subject are narrowed rather than deleted, because in at least two of them the sentence is the argument: the verb tier's width figure stops being typed at all and is generated off the impl blocks with the predicate written beside it, since this ticket's own 244 is not reproducible under any predicate - pub fn alone gives 202, every fn including test-gated gives 320, production-only gives 309 - which is the ticket committing a mild form of the class it was filed about, as was its claim that the figure had gone stale a second time when git log -S finds the sentence written once and never corrected; finalized_anchor is repointed to the two hops it really takes rather than dropped from the list of delegates; the duplicated surface tail is extracted and not deleted because BOTH copies are live, one a trait method the bench calls and one the escrow-promoted twin, so establish-which-is-live had the answer both; thiserror leaves two manifests that never derive it, each row replaced by why the absence is deliberate now that RepoError is loot-codec's type re-exported; the push-shape row is documented and pinned rather than renamed, because a metric string is output and this change is behaviour-neutral, and the new pin found the drift is two rows and not one; and the whole cost of opening a repo is narrowed in all three live copies rather than the single one named here, since correcting one of three is the trap #1629 records, while ADR 0012's copy stays because its subject is the loose-object slice and not the harness. one finding was simply wrong and is reported as wrong with no edit, since run_checks has a production caller and fourteen tests and the file this ticket cited does not exist. and load_from turns out not to be what the open calls at all: assemble asks load_from_needing with the verb's declared RepoNeed since #1536, so the timed call is the widest spelling rather than the live one, which is now said in both places that time it (#1656) 44b5b98a · dbf3dbe6…diff
  • loot archive grows prefix, format, -o and a pathspec, and the pathspec is the DECLARED group rather than a fourth matcher: no Glob, no matches and no string compare against a pattern is written anywhere in this change, so the empty-match refusal and the 1330 cwd rebase arrive free with the attachment. the attachment forced one thing the ticket did not name - archive was declared NO_PATHS, so under 1551 the group trailing list would have begun at position ZERO and swallowed the output file - and it now states its own arity of two opaque slots first, both opaque because a file name, an -o value and a prefix are not repo paths and must not be rebased. the honesty criterion is the one that carried risk, since this adds a SECOND reason an archive can be partial beside the reason 416 built the summary for, and the two must not collapse into one number that reads as either: three populations are kept apart, written, then unreadable as selected minus written which KEEPS 416 word partial, then outside as total minus selected which gets its own clause, its own denominator and the explicit words by request not unreadable. the first denominator switches to selected and SAYS selected exactly when a pathspec is in force, so 1 of 2 cannot be read against a five path change, and the pathspec clause prints EVEN AT ZERO because silence there is indistinguishable from no pathspec at all. written is derived from selected by MEMBERSHIP, so written under selected under total holds by construction rather than by a subtraction that could underflow, and the engine skipped count is deliberately NOT used because it is taken over the whole tree and would therefore count paths the operator never asked about - that IS the collapse. the format is tar.zst and it adds an EDGE rather than a crate: zstd is already default-on in loot-codec and loot-core takes it with default features, so it already compiles into the shipped binary and the lock gained exactly one line with no new package. gzip was rejected on MEASUREMENT rather than taste - flate2 is in the lock but its only dependent is automerge, which only spike-crdt links, so it reaches NO shipped binary and choosing it would put a second compressor in the released loot; the wider extractor reach is the real cost and the format table says so. it does not reuse the codec own compressor either, because sharing it would weld archive bytes, a thing people compare hashes of, to a level chosen for objects in a store. the byte-reproducibility promise is NARROWED where an operator reads it: the ustar bytes are host independent, the zstd framing is reproducible only for a given zstd build. two pins were aimed rather than merely live - the -o ambiguity mutation is invoked with @ rather than HEAD precisely so the mutated build SUCCEEDS, which makes refusal the assertion, and tar.zst is checked by DECOMPRESSING rather than by length, since a wrap that compressed the wrong buffer stays green under a size assertion. and a stale doc claim was found on the way: readable_tree was described as what loot grep searches, false since 1500 moved grep to readable_object, narrowed here and in the copy of it that archive carried (#1530) 95a1cfeb · dbf3dbe6…diff
  • loot 0.4.20: loot-cli and loot-forge move to 0.4.20 with their lock entries, the first public release since 0.4.19 and the one that carries the opt-in telemetry client, format major 14 and forge migration 0017; the relay and forge already read format 14, which ADR 0066 requires before the CLI publishes. the Known Issues page is re-reviewed by running the release binary against a local relay rather than re-reading source: both entries reproduce word for word and the unlock session file holds no raw passphrase, but the run falsified the pages own remedy. it read unlock, rehome, then converge, and when rehome refuses, which it does in exactly that state, carrying on to converge captures the stale copy the same way describe does, after which surface --force re-materializes the stale bytes. measured from the pristine post-pull state, moving the file aside and re-running rehome, or surface --force, each put the arrived version on disk, and describe then undo does too; the second column now says that in that order and says to stop before converge, and the entry says converge does the capture unasked. gone from the history was wider than the run, since the arrived change stays in the log one below a tip that reverts it, and the entry now says that. the converge capture is filed as 1963. tsc, the site suite, the build, the byte budget and the published-surfaces pins are green (#1960) b53bd00d · dbf3dbe6…diff
  • review sweep 7 fix-up: loot-first now reads loot --version --json with a JSON parser, so the relay push is decided on a single JSON object whose top-level contract fits a u8 and whose top-level version is a string, in any key order and beside other fields, while a duplicated field, trailing text or a looser number refuses the push. the pusher= path now percent-encodes control characters as well as percent signs and whitespace, and workflow.md says so. land-change gains the relay=REFUSED recovery (catch the primary up, cargo build --release, check the contract loot --version --json prints against the major the landed commit declares, then loot push, and record the push output on the issue), says the refused block names both majors on a mismatch and a read failure otherwise, and stops counting the relay and branch states. afk-loop agrees that a FAILED or REFUSED land stands and owes a push, gains that push as a step, and counts the ticket done once the push is recorded on the issue. format.rs, ADR 0066 and ADR 0083 note that a land carrying a FORMAT_MAJOR bump normally comes out relay=REFUSED rather than FAILED, and the --version usage line, the print_out and dispatcher comments and a verdict test name follow. both new pins went red before the change (2 passed, 2 failed), the control-character pin went red with that arm removed (1 passed, 1 failed), and a serde_json Value reader went red on the duplicated field (1 passed, 1 failed). the workspace suite is green (#1979) ce994e1d · dbf3dbe6…diff
  • loot 0.4.21: loot-cli and loot-forge move to 0.4.21 with their two lock entries, the first public release since 0.4.20 and the one that carries the locked-pull fix. format major stays at 14 and the live relay already reports 14, so ADR 0066 has nothing to sequence here, and the top forge migration is still 0017, so this release owes no schema step. the Known Issues page is re-reviewed by running the 0.4.21 binary against a local loot-relayd rather than by re-reading source, and the locked-pull revert NO LONGER REPRODUCES: rehome materialized rather than refusing and put v2 from alice on disk, the converge the note names next answered already on one line and left it there, loot status reported an empty working change and loot log showed the arrived change as the tip, which is the state the printed advice implies, reached by typing the printed advice. so that entry is deleted, and FIXED_IN_MAIN, its HELD_ENTRIES row and pin 5 go with it, on that pin own instruction, leaving loot edit, which reproduced word for word, as the one entry. REVIEWED_AGAINST moves to v0.4.21 and LAST_REVIEWED to 2026-09-19, and the locked-pull paragraph further down the page stops pointing at a deleted entry and says what rehome does now. RELEASE_TAG is deliberately NOT moved: it names what dl.millerbyte.com can serve, and this release has reached nothing yet. the privacy page verb-count claim was re-checked against the binary rather than assumed, since that is what fell over before the last cut, and note_dispatched is called on all three dispatch routes, the table, buoy and bisect, so every verb this release newly publishes is counted. the site byte budget is re-recorded and exactly one ceiling moves, /known-issues lowered from 210944 to 208896 by the deleted entry, with no raise anywhere. tsc, the site suite (659 passed and 61 skipped over 60 files), the build, the byte budget and the published-surfaces pins are green, and the workspace suite is green (3970 passed over 122 binaries, 7 ignored) (#2089) 71ed6938 · dbf3dbe6…diff
  • loot-hygiene (#2128, map #2099 ticket 4): the land text gates move from loot-first into a crate with no dependency that builds for wasm32, re-exported as loot_first::hygiene so every gate row, checkout test and AGENTS.md path reads unchanged; a new crate rather than loot-codec because three consumers now read it, loot-first, loot-cli and loot-wasm, and the wasm one is the fact ADR 0076 §2 no-new-crate reasoning did not have. Corpus::from_entries files an in-memory tree under exactly the allowlist the directory walk applies, top-level files plus SOURCE_ROOTS filtered by TEXT_EXTENSIONS minus SKIP_DIRS and SKIP_PATHS, through one filing rule both constructors share, and a link resolves against the entry set with dot and dotdot folded, so a Worker with no directory answers what the land answers. CORPUS_CHECKS is the table a .lootpipeline check step names, keyed by the land gate names and pinned to them, holding the floor-free halves: the land refusals still refuse a vacuous walk first because a mis-rooted land must not read as clean, but a three-file repo is not mis-rooted, so a check answers about the files it was handed. loot pipeline now runs check steps in-process over one corpus walk per run, a finding is failed and exits 2, an unknown name cannot start and names the set and exits 1, and the ? deferral #2127 shipped this morning is retired the same day with the ADR 0023 amendment saying so. loot-wasm links the crate and a wasm-bindgen test runs the three checks from entries under node. Measured: the site wasm is 475667 bytes before and after, unchanged, because nothing exported calls the crate; native the three checks over a 1120-file synthetic corpus take 0.8 ms in release and 28.0 ms under wasm in node; through the release binary on this repo, roughly 1355 covered files and 20 MB by an approximate count, the three checks cost 160 ms warm against a 73 ms listing and 6.5 s on a cold page cache. Two lands worth of censuses read off their refusals: the destination allowlist row for the moved file, and no other. Five pins broken once by a named mutation, red at 0 passed 1 failed then green at 1 passed: from_entries skipping widened, link resolution made true, the table key misspelled, a finding made a pass, an unknown check made a pass; the fifth sweep needed a second pass because its first restore string matched twice and left the mutation applied, caught by the full run that followed. 66 test binaries green across loot-hygiene, loot-cli and loot-first. No wire, format or store byte moves, no migration (#2128) 858575e1 · dbf3dbe6…diff
  • loot 0.4.22: loot-cli and loot-forge move to 0.4.22 with their two lock entries, the first public release since 0.4.21 and the one that carries the seek follow-ups, a fetch depth the hosts honour (#2123), the browser SDK bounded read (#2124) and the seek cache cap with --gc (#2125), beside the pipeline verb (#2127) and every land since 3039521e. format major stays at 14 and both live hosts already report 14, so ADR 0066 has nothing to sequence here, and the top forge migration is still 0017, so this release owes no schema step; the relay and the forge still deploy before the site, since #2123 is what the SDK bounded read refuses without. the Known Issues page is re-reviewed by running the 0.4.22 binary in a sandboxed home rather than by re-reading source: loot edit on an older change refuses with change tlqyroot has descendants, v1 edits only a tip (childless) change, word for word and exit 1, while on the tip it reopens the version as the working change, so the one entry stays as written; loot seek --gc --dry-run in that sandboxed home lists no positions and the defaults. REVIEWED_AGAINST moves to v0.4.22 and LAST_REVIEWED to 2026-09-20. RELEASE_TAG is deliberately NOT moved: it names what dl.millerbyte.com can serve, and this release has reached nothing yet. the site byte budget is re-recorded: NOT re-recorded, because no ceiling has to move: every one of the 62 surfaces is under its ceiling, and the uniform +150 to +170 B on every surface is #2127 CLI row in the shared docs chunk they all fetch, one growth and not sixty-two, and not this cut own; the site gate is green in the lane (669 passed and 62 skipped over 61 files). the workspace suite is green at the base (4023 passed over 126 binaries, 7 ignored, on the #2125 lane a commit below, and this land gate runs it again) and cargo build --release --locked validates the lock edit (loot 0.4.22 from the lane binary, and loot-forge 0.4.22 built beside it, refusing to open its storage without its URL, which is its answer outside a deploy) 8b4aa08f · dbf3dbe6…diff
  • loot 0.4.23: loot-cli and loot-forge move to 0.4.23 with their two lock entries, the first public release since 0.4.22 and the one that carries the seal-over-tree-entry arc, where every site that acted on a change tree entry unsigned visibility field now asks the seal instead (#2185 the three acting sites, #2188 the timed deposit lane, #2187 the push-time deposit plan, #2196 the git bridge projection, #2203 the git-side ingest, #2205 both grant doors and #2212 the sync ingest door, with #2206 and #2214 measuring and correcting the prose behind them), beside the forge runner and job tables (#2157, ADR 0091, migration 0018), the owner side of a proposal (#2162, ADR 0075), the pre-land gate learning which stream a doctest reports on and refusing a cargo it could not start (#2084, #2140, #2199, #2066), a patch that carries the ending of the line it shows (#2005), the relay mailbox taking the door the relay already writes objects through (#2112), and every other land since 3cfbe9b, 30 in all, the first of which is the RELEASE_TAG move that published 0.4.22 (#2147). format major stays at 14, format.rs has no diff at all in the range, and both live hosts answer format_major 14 over /info, so ADR 0066 has nothing to sequence. the top forge migration is now 0018_runner_and_job.sql where the 0.4.22 cut said 0017, so unlike the last release this one DOES owe a schema step: 0018 is include_str-ed into MIGRATIONS and rides the forge binary, so the forge deploys BEFORE the site. the Known Issues page is re-reviewed by running the 0.4.23 binary in a throwaway home, with HOME and USERPROFILE and XDG_CONFIG_HOME and XDG_CACHE_HOME all redirected into a temp directory and a local loot-relayd for the pull half, rather than by re-reading source: the one entry reproduces word for word, change xoxokopy has descendants - v1 edits only a tip (childless) change at exit 1, while loot edit on the tip reopens version 0101b025 as the working change, so it stays. the custody prose was re-run rather than re-read as well: once loot lock cleared the session, status, log, diff, grep, surface, whoami, describe and push each refused with the ADR 0068 message while lock and unlock still ran, a wrong LOOT_PASSPHRASE said so and was ignored, the session file held loot-unlock v2 and machine and sealed hex with the passphrase own hex in no file under the config directory, loot id phrase refused both a pipe and a redirect with no override, loot undo refused across the push barrier in the words the page quotes, and loot burn printed the never-pushed and pushed tiers with one line per disclosed host. ONE CLAIM IS CORRECTED, and it had rotted without an edit: the locked-pull paragraph read since {REVIEWED_AGAINST} the ingest writes down the claim it could not check, which was exact when #2089 wrote it at v0.4.21 and false one cut later, .loot/stale-disk-unverified being in the v0.4.21 tag and absent from v0.4.20, so it is the literal v0.4.21 now, for the reason the session-file boundary is the literal v0.4.17; the behaviour itself was re-run and holds, a locked pull leaving the receiving disk on the old bytes and parking .loot/stale-disk-unverified while the rehome its note names materialized the arrived version after unlock. REVIEWED_AGAINST moves to v0.4.23 and LAST_REVIEWED to 2026-09-21. RELEASE_TAG is deliberately NOT moved: it names what dl.millerbyte.com can serve, and this release has reached nothing yet. the site byte budget is NOT re-recorded, because no ceiling has to move: all 62 surfaces are under theirs, /known-issues is +7 B against a 208896 B ceiling, and the one figure worth a reader eye is /privacy at +586 vs recorded with 907 B of headroom, which is not this cut own since nothing here touches that page. the site gate is green in the lane (tsc, the build, 669 passed and 62 skipped over 61 files, and the budget), the workspace suite is green (4140 passed over 132 binaries, 8 ignored, 0 failed) and cargo build --release --locked validates the lock edit (loot 0.4.23 from the lane binary, and loot-forge 0.4.23 built beside it) 078d5ce1 · dbf3dbe6…diff
  • 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) 4ad8f503 · dbf3dbe6…diff
  • loot-cli and loot-forge are 0.4.25, the release that first carries the loot.build names (map #2412, ADR 0099), and the Known Issues page is re-read against it by hand in a throwaway home: one claim had rotted with no edit, since #2389 gated the reads of an allowlisted relay and a locked pull there now refuses by name as a forge does, so the page says an open relay runs while locked; one entry is added first, found by driving burn further than any pass had: burning a finalized path a forge has not yet received makes every later push to that forge refuse and the path cannot leave the tree, older than this release and filed as #2441, with the undo-first workaround that was run; LAST_REVIEWED is 2026-09-26 and REVIEWED_AGAINST v0.4.25. published_surfaces 6 passed; site gate 819 passed, /known-issues within budget; release build of the lane reports loot 0.4.25 and names security@loot.build and https://loot.build/trust in its help (#2419) c594ee28 · dbf3dbe6…diff

Renames are not followed. loot's tree maps a path to an address, so a rename is a delete and an add. This list is the history of the name, not of the bytes.