Changes touching this path
- Extract Custody from DagRepo (mechanical) (#323)
8957dcf1 · dbf3dbe6… - Grant expiry: optional expires_at on GrantEntry and tag-3 wire (#20)
a01c3d53 · dbf3dbe6…diff - loot id rotate: new keypair, expiry-preserving re-grant wave, archived old key (#16)
2b16256f · dbf3dbe6…diff - Refuse --expires on the tag-1 file-grant path; only --relay enforces expiry (#352)
0dfb8d46 · dbf3dbe6…diff - Rotate ritual warns per expiring re-grant: expiry does not travel (#368)
Decision on #368: accept + document loudly (option 3). Routing re-grants
through the tag-3 sealed path conflicts with the wave-runs-first abort
safety of the rotation ceremony (the new key does not exist yet when the
wave is minted, and rotation is deliberately offline/file-based); the
tag-1 wire extension was already rejected in #352.
The ritual output now names each re-grant bundle that carries an expiry
and warns that the expiry is recorded only in the rotating machine's
manifest - the applying machine records none (tag-1 limitation, #352),
so the applied copy is effectively unexpiring - and points at the one
wire that enforces expiry: the original grantor re-issuing via
loot grant --relay (#20). Docstrings on RotateRegrant, rotate_regrants,
and cmd_id_rotate no longer overclaim that the expiry travels.
593f77c4 · dbf3dbe6…diff - Rename the Public visibility tier to Internal (ADR 0041 §2, #480)
6969626d · dbf3dbe6…diff - Authenticate the purge lane: a purge is a signed request (#503)
A wire purge carried only an oid and yielded an unauthored tombstone, and
stow honored it before storing objects -- so any authenticated pusher could
destroy any oid across every tenant, needing no key, only the address.
ADR 0038 already called a purge event a request asking cooperating relays
and peers to destroy their copy. Cooperating meant nothing: loot honored
anyone. The signature now says who is asking; a per-receiver policy decides
whether to honor. Peers honor registered senders and quarantine strangers;
relays honor their push allowlist. Local burn is untouched and needs no key
-- only propagation requires a signature, so a keyless repo burns locally
and refuses to propagate, explicitly.
No global entitlement predicate exists: ChangeNode.tree is a full manifest
and loot duplicate copies a tree address-for-address, so authored-a-change-
referencing-this-oid is satisfiable by any cloner. Both withdrawn rules
have regression tests.
Format 9 to 10. A v10 reader parses a legacy purge lane and drops it, so no
unsigned request is honored while the rest of a v9 bundle still decodes; an
older client hard-fails on v10.
Destruction is structurally unreachable without verification: HonoredBurn
has a private field and authorize_burn is its only constructor.
Both halves are signed, with domain-separated schemes -- without tags a
maroon signature re-encodes byte-for-byte as a burn tombstone at path
length 31. Maroon entitlement is an exact Manifest grantor join, and a
grant only records a grantor when it actually installed a key, so a crafted
grant cannot plant one for content you already hold.
Closes #503.
913c5cc6 · dbf3dbe6…diff - Stop shipping grantee nicknames on the wire (ADR 0015, #521)
55ba71eb · dbf3dbe6…diff - SDK/WASM cross-session grant delivery: pull queues, accepting applies (#508)
e0bb183e · dbf3dbe6…diff - loot-core testkit: extracted engine modules own their tests again (#660)
3412c046 · dbf3dbe6…diff - Sweep the rust-1.96 clippy debt; document the land-holds-the-binary hazard (#667, #681)
26cfbaa9 · 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 - every persisted artifact crosses the store as its own type, and raw paths retire to the four recorded owners (#863)
Land A of #863 (decision #851): RepoStore gains a typed read/write pair per
artifact — identity, graph, keyring/escrow and their shared halves, manifest,
purges, attestations, burn log, conflicts, resolved — each owning the file,
the atomic replace, and the absent-file semantics, with the codec staying
where it lives. The engine's save_to/load_from/refresh_hold/gc/verify raw
sites, the op log's capture/restore and ops file, and the bisect session all
migrate through; the ops file and view restore gain the temp+rename atomicity
they lacked, and the op-log capture now waits out the Windows replace window
instead of recording a live file as absent. Absent is now NotFound alone
everywhere but `resolved` (best-effort by contract): a torn read fails loudly
rather than reading as an empty artifact. The migrated getters drop to
pub(crate); `objects/`, the keypair/peers, `git-mirror/`, and doctor keep raw
access as the recorded second owners.
0edf769d · dbf3dbe6…diff - the change graph gets one read view, and the two forwarding bands over it retire (#868)
826c3339 · dbf3dbe6…diff - a key's provenance is stated at the one door, and a grant is refused at the two that bypass it (#864)
82e06601 · dbf3dbe6…diff - adopt: catch up to landed main a10e310f
ebbe89be · dbf3dbe6…diff - a push deposits this identity's own keys: a standing self-grant carries Restricted custody to the second machine, so a same-identity clone opens what it authored (#980)
684f37cb · dbf3dbe6…diff - a forge push deposits the keys the forge itself drops: standing self-grants widen to Internal and embargoed-to-self at forge remotes, closing #502's second-machine cost (#1042)
f8edab5d · dbf3dbe6…diff - a relay's deposit stops standing in for the forge's: the push-time dedupe moves off the Manifest onto a per-remote deposit ledger, so relay-first Restricted custody reaches the forge inbox the browser reads (#1052)
499739af · dbf3dbe6…diff - a push deposits self-grants for the finalized tree it ships rather than the unsigned working manifest, and a parent arriving after its child no longer seats as a phantom head (#1125)
bc7c27df · dbf3dbe6…diff - ADR 0070 cites the publish ban by its right number, ADR 0019 records its exemption class inline, a stow backend failure reads as server fault, and a working head stands every parent line down (#1168)
57354896 · dbf3dbe6…diff - content whose embargo has already lifted stops reading as sealed, because promoting due keys out of escrow is now the construction of the reader every content read hangs off instead of a doc-comment obligation six callers hoisted by hand, and deleting four of those hoists left all 2438 tests green while loot surface told an author to request a grant from herself (#1464)
e59e46b3 · dbf3dbe6…diff - delta_of's modified arm stops reporting a due-embargoed path nobody edited as an uncaptured edit that wedges every #436 clobber-guard verb, because the base read it decides on hangs off the promoting reader its sibling arm in the same match already used instead of the bake-off trait's frozen public door, and ADR 0007 stops claiming a compiler forbids that spelling while the shipped tree held one (#1471)
fbd758fe · dbf3dbe6…diff - loot migrate and loot maroon stop refusing an author's own revealed content as unauthorized, because the two re-seals promote due embargoed keys at their own first line instead of borrowing one from a capture that --no-snapshot skips outright and that an unchanged working tree short-circuits past before the engine is ever called (#1485)
34b5bb79 · dbf3dbe6…diff - the reader seam's own doc stops telling a loot-core author that content can only be read through it, because the sentence #1464 wrote into four places and #1471 corrected in three is now enumerated in ADR 0007 as one thing that changes together, and spawn.rs likewise stops claiming every command every gate runs while GATES row 0 shells out to git beside it (#1486)
b6657789 · dbf3dbe6…diff - a verb stops reading every loose object in the store before it can open one, because the open now indexes the object directory and reads the file at the address it is asked for, so one complete index still answers membership, the persist's candidate set, gc's sweep and ADR 0038's burn while a burned address keeps no route back through the directory it came from, and the sixth opt-in half that can see any of this refuses a fixture whose object directory does not hold the objects it claims (#1545)
5c98eff6 · dbf3dbe6…diff - loot grant stops issuing an untimed key for an embargoed path whose ciphertext has rotted, because the verb asks for a reveal_at directly instead of reconstructing one from a visibility whose absence has meant held-but-unreadable as well as not-held since #1545, and a timing that cannot be read now refuses instead of defaulting to zero (#1578)
d291d168 · dbf3dbe6…diff - a publication marker that could not be read stops being carried as an unpublished one, because the three re-seals that carry @world across a rewrite now ask a read that refuses instead of a predicate whose swallow was argued for the publish guard alone, and the refusal is a typed variant rather than prose so a corrupt seal reaches the run-loot-verify remedy a vanished one already did, while ADR 0012 table rows name their consumer and state the grep the census is the output of (#1582)
6ee38984 · dbf3dbe6…diff - the loot-core tests stop sharing one repo and the shared store stops growing, and the design was already written one line above the bug: tmp said the temp root every fixture repo lives under while returning the root itself, so returning a fresh directory beneath a per-process root makes that sentence true as written with no doc edit and none of the 161 call sites changed. uniqueness is per call rather than per process, because the sites run on the harness's parallel threads inside one process, which is the half a pid does not touch and the half the isolation gradient had already separated - lib alone green, workspace red. a third hole neither ticket named is the larger one: 48 sites spelled DagRepo::init with the bare temp dir longhand, bypassing the helper entirely and invisible to both tickets, so the real set is 51 statements and not the sixty a three-line window had suggested - established by reading whole statements, since that window reads past short ones and stops short inside this crate's many multi-line format calls. the evidence is a paired experiment rather than a green run: two concurrent processes reddened object_reads five times in five, asymmetrically with the earlier starter losing every time, while the shared-store half needed six concurrent runs to reproduce at all and then failed 28 of 36 across four test names, two of them never recorded before, which is the fifth and sixth name for a ticket whose own point is that the name is not stable. after, the same experiments are ten of ten and thirty-six of thirty-six green, and a paired count on an idle machine has the unfixed arm adding 44 objects and rewriting graph, keyring and heads while the fixed arm moves nothing, byte-identical with mtimes unchanged to the millisecond. a census derives the property from source with no allowlist and was proved red four ways, including one site from the third hole that any list written from the two tickets would have passed, and including the census blinded, which reported ok on an empty set and was caught only by its own non-vacuity guard (#1667, #1688)
87c3aacb · dbf3dbe6…diff - a move becomes a recorded fact instead of a later guess, and the premise this ticket rested on was false: #98's object reuse is keyed by PATH, so a moved path is absent from the outgoing tree at its new key, falls through to put_sealed and gets a fresh address - the same object under a different key was not a fact waiting to be read off the tree, it had to be made true by extending the reuse across the move, and everything else follows from that. The rule is that a move is recorded only when the path's sealed object survives it, so a move whose content also changed in the same capture window shares no object, records nothing and is two rows, while a move and then an edit across two captures of one change composes and reads as one row saying the content also changed. Empty content and any ambiguity are refused for the same reason the whole design exists: zero bytes equal all zero bytes, and choosing between two vanished twins would make a signed fact depend on iteration order. On the boundary the render side compares nothing at all - resolve reads keys and never an address, a visibility or a byte, pinned by running it twice over trees that agree on every key and differ on every address at three unopenable tiers, with a positive control beside it so agreement is not evidence it answers nothing. The capture side compares once: it opens a vanished object with this identity's own keys, fail-closed so an unopenable object is never read or hashed, and pairs only against an addition at an identical visibility and publication tier, with the digest living for one call, never an address, never stored and never on the wire. What a relay newly learns is that two keys held equal ciphertext, which recording a move states in plaintext anyway since tree paths are plaintext at every tier, so it is inherent to the feature rather than the ADR 0004 oracle - written into that ADR rather than left in a message. Renames ride the label seam change_id and authored_at already use: covered by the finalize signature so a relay cannot rewrite a move, never folded into the version id so two peers reaching one tree by different routes still agree on its address, which is why every existing id and signature is unchanged and a v11 store reads with no move recorded. That was tested rather than argued, against a real store built by the shipped binary, where the old move still renders as a delete and an add in the same repo the new one renders as a rename, because inferring the old one would be the equality oracle arriving through the compatibility door. Three further things were wrong and are corrected rather than worked around: ADR 0019 says an additive change bumps the minor and has never described this project, since the minor is still zero and all five additive changes took the major; plan_moves own doc comment claimed the tier is checked before the digest, three lines above code that does the lookup first, when the property actually holds by the stronger route that nothing unopenable enters the index at all; and the empty-content refusal was written on both sides of the pairing, where each made the other unprovable and removing either left the test green - a duplicated guard is not belt and braces, it is two guards neither of which can be shown to be doing anything (#1539)
3c7e029b · dbf3dbe6…diff - grant promotes at the clock it grants at, and the conservative alternative turns out to be an ORDERING ARTIFACT rather than a policy: skipped_unheld recorded only whether some earlier command in the repo whole history happened to open a reader, so one input at one clock gave two answers, and no state anywhere expresses that a reader has revealed an embargo. the flush goes on grant first line and rotate_regrants inherits it BY CONSTRUCTION rather than by a second flush, because it reads no content key except through self.grant - so the two cannot drift apart the way four hand copies of one body did. grant_sealed gets NOTHING and the ticket premise is refuted there: its keyring-OR-escrow lookup is already order-independent, which is why its pin was the one GREEN BEFORE THE FIX, and that measurement is recorded rather than a flush added to make the set look uniform. the eager direction, promoting a not-yet-due key, is not expressible through this seam because a flush IS the reveal gate applied - Escrow::flush now >= reveal_at is sealed::open own comparison. what WOULD be a hole is an escrow FALLBACK that skips the gate, which grant_sealed has deliberately for ADR 0027 timed deposit and grant must never copy, since a tag-1 bundle is a plaintext key in a file. the cost is asymmetric and lands on rotation: a grant dropped from the loot id rotate wave is access PERMANENTLY LOST, and the report line about what the outgoing key can no longer read was false for a due embargo. three pins, ONE REPO PER ARM because Escrow::flush promotes EVERY due entry and a second embargoed path in one repo makes the second arm vacuous - the trap #1485 hit - and both clocks pinned on each, since a fix that reveals early is worse than the bug. four mutations make the case: deleting the flush reddens grant AND rotate, which is what proves the by-construction inheritance, and giving grant the escrow fallback reddens the CONTROL arms instead. the caller list on flush_due_keys stops being a hand-written count and becomes a membership rule with a grep census, which is what kept it right while it gained a caller (#1488)
1d44cc64 · dbf3dbe6…diff - the KEYRING IS A CACHE, NOT THE GATE, and the reveal comparison is asked at every read off the sealed object own header rather than inherited from whatever a persisted keyring happens to hold. #1488 repaired the un-promoted state and left the other one: flush PROMOTES AND NEVER DEMOTES, and the promotion persists, so at a clock short of reveal_at a never-promoted repo REFUSED and an already-promoted repo GRANTED - one input, one clock, two answers, in the direction that ticket own done-bar calls worse than the bug. the sweep claim was REPRODUCED BEFORE ANYTHING WAS FIXED, twice: swapping the two arms of the old pin reddens its control, and two fresh repos differing ONLY in whether a flush ran first answer granted against Unauthorized. grant now reaches its object and key through ONE PRIVATE DOOR that flushes, looks the key up, then asks sealed can_open - which is open steps 1 and 2 VERBATIM, so the gate is the SAME FUNCTION a plain read uses and cannot drift from it. demotion was rejected on merits rather than taste: it would need the escrow to retain an entry it has HANDED AWAY, it would make a READ MUTATE CUSTODY BACKWARDS, and it would be one more copy of a rule vis already carries. the refusal stays Unauthorized deliberately, because rotate_regrants classifies by VARIANT and an Embargoed would turn a skipped standing embargo into a FAILED ROTATION WAVE. the doc defect is repaired where it actually sits: membership of the flush census is NECESSARY AND NEVER SUFFICIENT, since a flush repairs only the un-promoted arm, and the #1488 amendment now records the invariant it kept rather than the one it claimed. reachability is reported honestly rather than dramatised - a forward-only wall clock CANNOT reach it, because a promotion requires the clock to have passed reveal_at, so it needs a now BEHIND a clock at which a promotion already happened: the engine API directly, where a perf harness already passes a backdated now, or a backwards wall clock, which is ADR 0007 own D-threat. the structural violation is unconditional and is where the claim is made and measured. three mutations, and the two halves fail in OPPOSITE directions: removing the gate reddens the new pin and the re-pinned control while the rotation pin stays green, removing the flush reddens the two due-direction pins while the new one stays green - which is the separability #1488 lacked. and one limit is stated rather than dressed up: the both-ways-round half builds a fresh repo per arm, so no mutation can redden only the second run, and the doc says so (#1840)
ae911b61 · dbf3dbe6…diff - every bare remove_file of a loose object in loot-core fixtures now waits for the absence it asserts, and the set that does is a census rather than a sentence: #1596 measured that a handle held without FILE_SHARE_DELETE makes remove_file return os error 32 and leave the file, fixed the sites in negotiation.rs and swept no further because its aggressor had reddened nothing else, and #1897 asked whether the rest were safe or merely unexposed. they were unexposed. the aggressor was rebuilt and lives in the tree now as testkit::hold_without_share_delete, and with it holding one handle across one removal the whole family went red site by site rather than statistically: the selection reads 1 passed, 10 failed, every panic os error 32, with the already-fixed negotiation site under the identical hold as the green control, and the second removal inside accept_loss measured on its own with the first hold lifted (0 passed, 1 failed). with the helper at all of them the same selection under the same hold is 11 passed. the helper moved from negotiation.rs into testkit keyed on the OBJECT DIRECTORY rather than a store directory, because the object_store.rs fixtures are an object directory with no store around them, and #1899 exit rule and the three-way scan answer came with it unchanged. the ticket list was wrong in BOTH directions, which is the finding: it named sites a realistic scan does not reach and MISSED two of the most exposed, the live-repo removals in engine.rs and custody.rs that are #1596 own shape; under an emulated indexer scanning the fixture roots, 50 paired interleaved rounds, the bare tree is red at a removal in 36 rounds over four sites, two of them the ones the list omitted, and the converted tree is red at a removal in ZERO. what is bare and why is now derived: tests/loose_object_removal_census.rs reads every removal whose statement or whose binding names a hex-encoded address out of src and tests, and names the one home, the aggressor pin own deliberate bare arm and the two PRODUCTION removals, which return their error rather than panicking and are right to. run against the pre-change files the census names exactly the nine test functions that were converted, the tenth site being the one it states it is blind to, a removal by directory entry, which was given its address so it could take the helper and so the census could see it. mutations: a bare removal put back single-line, multi-line and through a let binding reddens the census each time naming that function (2 passed, 1 failed each); a name dropped from the expected set reddens it (2 passed, 1 failed); blinding the address needle reddens the classifier fixture and the not-gone-blind guard too (0 passed, 3 failed); giving the aggressor FILE_SHARE_DELETE reddens the new pin because the bare removal then succeeds (4 passed, 1 failed); dropping the named-first precondition reddens the should-panic pin (4 passed, 1 failed); and the pre-#1899 scan spelling reddens the moved scan pin (4 passed, 1 failed). docs/agents/workflow.md flake section carries the rule, the aggressor and the census, and stops saying the fix ends at one file. a latent write-side exposure was found on the way and is NOT fixed here: save_objects_loose renames its staging file without store.rs retry, so a scan holding the stage makes an ordinary save fail with os error 32, which is the create side #1596 explicitly left alone. every edit is inside a cfg(test) item or a doc comment, so no production byte moves and no perf gate is owed. the workspace suite is green (3843 passed over 120 binaries, 7 ignored) (#1897)
b8eb322d · dbf3dbe6…diff - a prose pass narrows claims that read wider than the code, and LineDelta::rendered goes crate-private. ADR 0007, CONTEXT.md and the object_and_key_at doc say the reveal gate is asked at every read through the grant key door, not at every read, since grant_sealed keeps its escrow fallback by design; the Refit enum count, the family list in CONTEXT.md and the refit.rs header, and a stale claim that every planner runs the draft check, now point at the enum and the wildcard-free minted_edges match instead of a count; role_display gets back the blank doc line rustdoc folded into the last bullet, verified in the built HTML, and a record type added to ReservedRecord replaces a third record type; the Landmark entry records the one-line rendering from #1519; the revset named door and the hunkpick PATCH constant state the exceptions a reader finds (grep writing its own refusal, restore spelling -p out under the main.rs census); the ChangeGraph insert doc stops saying the callers do not insert parents-first, names DagRepo::apply_sync for an apply_bundle DagRepo does not have, and says why the ordered ingest_shared_lineage splice stays on insert; ADR 0023 now says what #1870 wrote in the delta_shape header, that the reveal_at split is about the field and not the number, which embargoed@ can carry. nineteen assertion messages lose the run of spaces a rewrap left inside the literal. LineDelta::rendered and Rendered become pub(crate), with a compile_fail probe and a positive control: the probe was red before the narrowing (2 passed, 1 failed), and making the method pub again reddens it (3 passed, 1 failed). items 2, 8 and 14 need no fix here: the #1515 raise is real because #1516 lowered the ceiling between the two raises, #1968 dropped the width narrative, and #1860 withdrew the figures. cargo doc warnings are unchanged for loot-core, loot-codec and loot-cli. no migration, no wire or format byte moves, and no forge or relay byte moves, so this owes no deploy. the workspace suite is green (3913 passed over 123 binaries, 7 ignored) (#1848)
eb966bdb · dbf3dbe6…diff - the ingest check is refused and the census that would have judged it is what lands: a change tree entry records a visibility that sealed::seal leaves outside the address, that compute_change_id_raw discards on the way into the version id the finalize signature covers, and that ObjectStore::put keeps from whichever bundle arrived first, so on a tree that came from a peer that field is a claim under no signature. tree_entry_visibility_census.rs derives every two-identifier vis pair spelled under crates/*/src and carries per site what a lie in that field would change - rendered, acted on, re-recorded, or not a tree entry at all - keyed by file, enclosing fn, which declaration of that name and which binding under it, because bundle_codec and loot-wasm each declare a name this keys on more than once and a key on the name alone would hand two functions one ordinal run, the ambiguity store_rename_census holds out of reach with a guard instead. the class is a judgement and is not measured; what is measured is that a row cannot be written without one. the sites it turns up as acted on are what the ADR 0012 entry rests on: ride_entry reads the tree entry rather than the seal when it decides whether an anyone-granted content key rides a bundle, embargoed_paths hands a timed relay grant the reveal_at the entry records, and publish_gate refuses an embargoed publication on the anchor entry tier - each of them holding or reaching the object it could ask instead, which is why the repair is per site rather than a global refusal at apply_sync, where the check would be partial exactly where it is wanted (a change ships its whole tree, ciphertext rides only for the addresses a bundle carries), where disagrees is not is false once put_vis_redacted has stripped a holder list on the wire and same_seal exists because of it, and where one refusal rejects the whole bundle. the workspace walk moves into census_text under its own admission rule, now that a second workspace-scoped census wants it. red under mutation, counts read each time: a planted binding in maroon_inner (2 passed and 1 failed, naming maroon_inner@1#2), a named row deleted (2 passed and 1 failed, naming bundle_impl_within@1#1), the type exclusion dropped from the needle (1 passed and 2 failed, the fixture naming qualified@1#1 off an (Oid, Visibility) annotation), the declaration ordinal dropped from the key (2 passed and 1 failed, twice@1#2 where twice@2#1 belongs, the table green beside it) and the walk blinded (0 passed and 3 failed, the guard saying gone blind rather than clean). no migration, no wire or format byte moves and no host behaviour moves, so this owes no deploy. the workspace suite is green (4096 passed over 132 binaries, 8 ignored) (#1892)
2ece96df · dbf3dbe6…diff - the three sites that acted on a tree entry unsigned visibility ask the seal now, and the bundle key lane stops reading that field at all: #1892 censused who reads a change tree entry visibility, refused a blanket ingest check at apply_sync, and left these three deciding on a value that sits outside the content address, outside the change id and outside the finalize signature, so on a tree that came from a peer it is a claim under no signature. ride_entry read open off the manifest entry while reading anyone off the object a line above, so an entry recording Internal over an embargoed seal put that content key into a sync bundle any peer can hold; it reads the seal the same probe already returned, memoised per address beside anyone, and costs no store read the walk was not already paying. the same predicate on the batched path moves with it, since a transfer with a second batch would otherwise reach the old answer: the key rule comes out of wanted_finalized_entries, which answers presence alone now, and bundle_objects_only asks the object it is already holding. embargoed_paths takes the reveal_at a timed relay grant is withheld until from embargo_reveal_at, the door #1578 built for that number at loot grant --relay, and the entry number survives only where the seal cannot be produced, which is a state where grant_sealed cannot produce a deposit either. publish_gate asks seal_visibility beside the oid_is_published read it already makes of the same object, and refuses where the seal cannot be read rather than swallowing, because a swallowed published-ness read asks for consent already given while a swallowed embargo publishes content nothing could say was unsealed. each pin carries both directions, since a repair that only tightens is as wrong as one that only loosens, and none of them compares the two recordings, which ADR 0012 records as supposed to differ once put_vis_redacted has stripped a holder list on the wire. the rows for the sites that stopped binding the field left #1892 census rather than changing class, a discard being no row by its own definition. red under mutation, counts read each time: ride_entry open restored to the manifest entry (negotiation 34 passed and 3 failed, the byte-identity difference red beside both direction pins, and the census 2 passed and 1 failed naming bundle_impl_within@1#1 and @1#2), the batched key arm stopped from asking (35 passed and 2 failed, naming the follow-up batch arm), the deposit reveal time taken off the entry again (custody 65 passed and 1 failed, reading Some(0) where Some(9000) belongs), the publish gate embargo read taken off the anchor entry again (loot-cli 0 passed and 2 failed, red in both directions, and the census 2 passed and 1 failed naming publish_gate@1#1) and the unreadable seal swallowed (2 passed and 1 failed, naming ghost.txt). no migration and no wire or format byte moves, but the key lane decision moves on the client bundle builder and on the relay fetch path, so this rides the next release and owes a relay deploy. the workspace suite is green (4101 passed over 132 binaries, 8 ignored) (#2185)
0a33e5c4 · dbf3dbe6…diff - the timed deposit lane asks the seal whether a path belongs in it and not only when its key is released: #2185 took the reveal instant off the seal at embargoed_paths and left the tree entry deciding whether that lane was reached at all, so an entry claiming an embargo over a seal that records none was answered 0 by embargo_reveal_at, the number for content under no embargo, and a Restricted seal content key was fanned out to every registered peer as a timed grant the relay releases on arrival - the same disclosure direction widened by the repair that narrowed the other one. reproduced at the plan before deciding, running the disagreement cases through embargoed_paths, restricted_paths and internal_paths and through plan_timed and plan_standing, where the row read c.txt to bob and to carol at 0. membership of the lane comes out of seal_visibility now rather than embargo_reveal_at, because the tier and the instant come out of one read and the number alone cannot tell an agreeing entry from one whose seal records no embargo; the unreadable seal fallback stays, the deposit it feeds refusing on the same read. which lane a path is offered to is still matched off the entry and stays with #2187, deferred on the store read per finalized tree path it would cost rather than on impossibility, and both missing directions are pinned: the claimed embargo the seal denies, and the internal claiming entry over an embargoed seal, where what keeps the untimed lane off the path is the key and not the tier, a live embargo key being staged in the escrow that lane does not read. the sentence naming the internal lane as the one a lying entry moves to now names what selects a lane, the reason that argued a seal knows nothing about paths says cost and #2187 instead, the negotiation stop rule comment cites its pin under the name #2185 gave it, the key lane fixture says what defines the set of builders rather than counting them, the census helper doc says compiled into rather than asking, the spike crdt key lane records why the census cannot see it, and BTreeSet stops being spelled in full beside an imported BTreeMap. red under mutation, counts read each time: the dropped arm made to plan the row again (custody 67 passed and 1 failed, reading Some(0) where None belongs), internal_paths widened to the escrow (67 passed and 1 failed, the live embargo reaching the untimed lane), and the lane selection made to ask the seal, which is the #2187 repair (67 passed and 1 failed, the timed lane reading cve.txt and other.txt where other.txt alone belongs), each restored to 68 passed and 0 failed. no migration, no wire or format byte moves and no host behaviour moves, but what a push deposits moves on the client, so this rides the next release and owes no deploy. the workspace suite is green (4103 passed over 132 binaries, 8 ignored) (#2188)
efb3d8ed · dbf3dbe6…diff - the permissive deposit arm gets the pin its defence was resting on, the standing-lane assertion that could not fail gets a comparison, and the declined cross-shape census weighs the instrument it never considered: #2194 carries the sweep 14 fix-ups plus the five a focused review of #2188 found, each premise re-verified on the tree first. item 8 is judged kept rather than closed to match its siblings, because where the seal cannot be produced embargoed_paths keeps the tree entry claim and the tree entry instant, which an unsigned tree can spell 0, and the recorded defence is real, grant_sealed reading the same object through the same door before it seals anything; so the arm stays and both halves are asserted now, the row surviving with the number the entry chose and the grant that row plans refusing at the same address, under a control that the same call succeeds while the seal reads. item 9 is made to bite: a second path over the same Restricted seal is entered honestly, so the standing-lane arm is a comparison rather than an absence, and the comment names the mutation that reddens it. item 10 narrows a pin message that reached past what it asserted, onto the deposit-plan pins that do assert the fan-out. item 1 corrects the ADR 0023 cost paragraph, which named the spool leaf where the endpoint moved too, off an unescaped format string onto the shared escaping, so an endpoint holding a quote or a backslash had been emitting JSON a parser rejects; the matching commit correction is already a comment on #1971. item 3 weighs the text census and records a measurement instead of an assumption: it needs no visibility widening and spawns nothing, so no census can was the wrong shape of decline, but what a text census reads is a spelling and section 3 is a rule about rendered bytes, and measured, the comma-join needle finds its own home and sites that are not columns while the contract field is not spelled one way, so a needle on either spelling reads a subset and says nothing about a shape carrying no such field at all, which is the defect #1971 came here to fix. item 2 replaces a false inference in the land-change skill: those three headings belong to the cargo test gate, and every other gate arrives as a finding under prose of its own, the line-ending gate among them, so an unfamiliar heading is another gate before it is a new kind; workflow.md scopes the same sentence. item 11 takes the completeness claim off the deposit accessor doc, where privacy is what bounds the callers, and off the census block that asserted a key guard over a membership rule which does not check it. item 12 names the two readers still deciding off the entry, the mirror projection and the forge manifest fold, with what bounds each, and records that neither belongs to #2187. items 4 to 7 are the smells: the forwarding closure, the clone taken and then borrowed, the exemption pin that read the real machine config and now builds its report from values, and the reversed import. red under mutation, counts read each time: the unreadable-seal arm dropped (68 passed and 1 failed, the row reading empty), that arm handing on a substituted instant (68 passed and 1 failed, 9999 where 0 belongs), restricted_paths made to ask the seal (68 passed and 1 failed, secret.txt joining owned.txt in the standing lane), a contract field added to the report the sender posts (6 passed and 2 failed) and joined_col joining with a semicolon (loot-cli 1335 passed and 8 failed), each restored to 69 passed and 0 failed. no migration, no wire or format byte moves and no host behaviour moves, and no output byte moves either, every code edit here being a refactor or a test, so this owes no deploy. the workspace suite is green (4110 passed over 132 binaries, 8 ignored) (#2194)
609afd9f · dbf3dbe6…diff - the push-time deposit plan asks the seal which lane a path belongs in, so a lying tree entry can no longer move a key between the internal and restricted lanes, and the bound the deferral rested on turns out to be false: #2187 asked for the key-not-tier claim to be tested before anything was built on it and it does not hold, because the Escrow is where the doors that read the seal own vis file an embargoed key and neither grant-apply door does - a tag-1 bundle files straight into the keyring with no embargo question asked at all, and apply_sealed_grant files by the frame reveal_at rather than the seal one, which is that verb recorded cooperative-defence posture - so a position keyring can hold the key to a live embargo and keyring.holds was never the guard that sentence said it was, pinned now rather than described. the cost was measured here rather than imported from #2196, whose zero was its projection already-open object: the question is asked behind the key guard, so it is one store read per row a lane returns and not one per finalized-tree path as the deferral estimated, and 64 held internal rows cost 64 object gets and 64 disk reads alone against zero disk reads behind a push, because a change node carries a full manifest and ride_entry has already opened every one of those addresses by the time push_with reaches the deposit plan. a disagreement withholds rather than substitutes or refuses: filing the path under the tier the seal records would move an embargoed seal into the timed lane, whose rows are crossed with every registered peer, and refusing would stop a push over an entry its operator may not have written. the comparison is std::mem::discriminant and never an equality, because #521 keeps holder names local so a wire-redacted restricted entry over a seal this repo persists the name in is the ordinary case and not a lie, and a tier added to Visibility needs nothing there; an unreadable seal keeps the row on embargoed_paths own permissive arm and its defence, grant_sealed refusing on the same address, which is what keeps a rotted object from costing a second machine the standing self-grant it opens the path with. a counted note beside the DepositPlan embargo one was weighed and declined: it would need each lane to hand back what it withheld as well as what it kept, for a state an honestly captured tree cannot enter. red under mutation, counts read each time: internal_paths no longer asking (70 passed and 3 failed), restricted_paths no longer asking (72 passed and 1 failed), the tier comparison made an equality (72 passed and 1 failed, the wire-redacted row dropped), the unreadable-seal arm made to drop (72 passed and 1 failed), the seal question moved ahead of the key guard (72 passed and 1 failed, object_gets reading 65 where 64 belongs), the fixture embargoed seal made unproducible (72 passed and 1 failed, the vacuity control firing) and the tag-1 door given an embargo gate (72 passed and 1 failed), each restored to 73 passed and 0 failed. the census row keeps its class and stops calling the widening unmeasured, and ADR 0012 takes a tenth amendment recording the direction, the measurement and the refuted bound. no migration, no wire or format byte moves and no host behaviour moves, but which keys a push deposits moves on the client, so this rides the next release and owes no deploy. the workspace suite is green (4127 passed over 132 binaries, 8 ignored) (#2187)
51ebdcfd · dbf3dbe6…diff - the deposit cost test measures the shape its comment claimed and the ships-nothing push is measured rather than assumed: #2206 asked for every premise to be re-verified on the tree first and the leading one holds, the comment saying a peer that already holds the tip while the code passed have = &[], a peer that holds nothing, so what it measured was the shipping push twice — the promotion the zero rests on comes from ride_entry probing every entry of every sent change, and send nothing, promote nothing. the missing shape is on the same fixture now: have = [tip] with no want promotes no address at all, the send pass skipping a held change before it touches the manifest and the wants lane skipping on empty wants, so the deposit plan behind it pays the whole cold walk, 64 object gets and 64 disk reads against the zero disk reads behind a push that ships, which is the cold arm number and not the pushing one. it is recorded rather than repaired and the reason stands where a reader meets it: the waste is plan_standing already-deposited filter, a map lookup, running after each lane has built its list with a store read per row, and putting the ledger first needs the remote and the recipient the lanes take no argument for, which gives the dedupe rule two spellings whose failure direction is a standing self-grant silently not planned, so it is #2208 and ADR 0012 takes an eleventh amendment carrying the number, the measurement and the scope the tenth amendment zero really had. the third Visibility comparison keeps the discriminant and says why rather than leaving it to a later tidy, the value under a tier being unable to move a path between lanes so that reading it withholds a self-grant over a difference that files the key in the same place, with same_seal named as the strictly stricter call it must not become and the row that separates them pinned; the case that doc named and the function cannot see, an entry spelling an embargo instant the seal does not, is gone. the two grant-apply doors asserted as complete become the question that decides membership — Keyring::insert callers against the one filing keyed on obj.vis — at the code, in the ADR and in #2205 body, refresh_hold and the save/load unions being routes the count missed. the silent drop keeps its decline and stops resting on a state an honestly captured tree cannot enter, an ingested tree being the only kind the repair is for. the standing fail-open arm is asserted over both lanes in one comparison, a struct Run no longer collides with a test-local alias, unjudged residual arm stops claiming the suite answered, kept_or_unwind records that its resume orphans the child, and the ungrammatical no-verdict sentence is fixed at the gate doc and in workflow.md. red under mutation, counts read each time: the tier comparison made same_seal (73 passed and 1 failed), the fail-open arm made to drop (73 passed and 1 failed, both lanes empty), the seal question moved ahead of the key guard (73 passed and 1 failed, object_gets reading 65 where 64 belongs), the ships-nothing peer made to hold nothing (73 passed and 1 failed, the bundle opening 65 where 0 belongs), the new pin fixture made to agree (73 passed and 1 failed, the vacuity control firing) and the Internal lane made to stop asking (71 passed and 3 failed), each restored to 74 passed and 0 failed. no migration, no wire or format byte moves, no host behaviour moves and nothing an operator can observe moves, the one string that changed being a clause no refusal quotes today, so this rides the next release and owes no deploy. the workspace suite is green (4128 passed over 132 binaries, 8 ignored) (#2206)
723c7f1e · dbf3dbe6…diff - both grant doors weigh the seal before they choose a lane, so an early-releasing sender can no longer file a live embargo key into a receiving position Keyring: #2205 asked for every premise to be re-verified on the tree first and each of them held, a tag-1 frame asking no embargo question at all and a tag-3 grant filing by the frame reveal_at, which is the sender word, so at a clock of 0 against a seal recording Embargoed reveal_at 9_000 both doors left keyring.holds true and escrow.holds false. the framing question is answered where a reader meets it rather than inherited by proximity: which lane a key waits in is decided once, at the door that files it, because a route carrying a key from one position custody to another reads one lane and writes the same one and Escrow::flush promotes but never demotes, so a filing is the answer every position that key later reaches inherits with nothing re-asking, and ADR 0007 states its guarantee over identities rather than over one door, which makes a door that does not ask a gap in it rather than the Escrow scope. one rule in one body, because two doors asking one question in two places is how they come to disagree about it: a grant-borne key is staged until the latest instant any party to the handoff named, so a party added later is another term of the same max rather than another arm. a disagreement withholds rather than refusing or dropping, refusing costing a recipient a grant over a claim they did not write with a remedy that is not theirs to act on, and taking the seal instant alone being strictly weaker, because a grantor own delay over content under no embargo is ADR 0027 timed deposit and a seal contributing 0 would release it on arrival; withholding costs only the wait the seal already imposes on every reader of those bytes, sealed::open embargo gate refusing them at that clock whichever lane the key sits in. the seal is read back from this store and never off the arriving bundle, because the address does not cover vis and put is first-write-wins, so weighing the incoming copy is reading the sender word a second time under another name, pinned on a fixture whose lying copy keeps the address and is therefore a dedup. the two doors get one answer for two reasons and the difference is recorded: tag 3 had a recorded cooperative-defence posture and this applies it to a second party, which is why ADR 0007 takes a #2205 amendment, while tag 1 had no decision at all, existing to bypass the entitlement question and having taken the embargo one with it, which sealed::open first gate separates in four words, time not identity. the cost is measured rather than assumed: one object get per key the door files and zero disk reads where the grant carried the object its key is for, eight keys costing eight gets beside a ninth address the same bundle carried no key for. red under mutation, counts read each time: the seal term dropped from the staging max (654 passed and 3 failed), the tag-1 door reverted to filing into the Keyring (654 passed and 3 failed), the frame term dropped (655 passed and 2 failed), the staging comparison widened to greater-or-equal so an undue key stages (653 passed and 4 failed), the seal weighed off the bundle copy rather than this store (654 passed and 3 failed, the held-seal pin naming the lane), the question moved ahead of the key guard (656 passed and 1 failed, object_gets reading 17 where 8 belongs), and each of the two fixtures inverted as a vacuity control (656 passed and 1 failed, the control firing), each restored to 657 passed and 0 failed. ADR 0012 takes a twelfth amendment recording that this class is a sibling of its own, the disagreement being with a frame rather than a tree entry so no census row moves, and that keyring.holds is still not a bound, a key some door filed before this change being in .loot/keyring still. no migration, no wire or format byte moves and no host behaviour moves, but which lane a grant-borne key is filed into moves on the client, so this rides the next release and owes no deploy. the workspace suite is green (4131 passed over 132 binaries, 8 ignored) (#2205)
ec80ad4d · dbf3dbe6…diff - the sync ingest door weighs the seal this store holds rather than the one that arrived beside the key, so a tag-0 bundle spelling a weaker tier at an address this position already owns can no longer file a live embargo's key into the Keyring: #2212 asked for item 1 to be demonstrated before it was repaired and the reproduction read exactly as the ticket claimed, bob holding oid sealed Embargoed 9_000 and a Sync frame carrying a byte-identical object that spells vis Internal beside the real key leaving bob's Keyring holding that key at a clock of 0, which is the state #2205's own new pin forbids reached by changing the frame tag, no plaintext escaping because sealed::open's header gate still refuses. the same read carries grant_ids and that half was demonstrated too, a copy spelling the ANYONE marker over a held Restricted seal getting a key past the entitlement filter #864 built, so every question this door puts to a seal now goes to the copy that will stand at the address. it is asked before the put while the arriving object is still in hand, because first-write-wins makes the held copy the standing one only where the store holds the address at all, so the cost is zero store reads on a fresh address and one on a dedup, which is the read the key verification already owed and now answers the seal's questions with its own; the already-held guard consults both lanes, so the lane the first door chose is the one that stands. the prose is the harder half and the lesson is sharper than do not list members: the sentence that failed was in the correct derived shape, the set of them is Keyring::insert's callers which the compiler enumerates, with a hand-maintained count welded on in the same breath, and the count is the half that was wrong, so every replacement names what decides membership and stops there, at ADR 0007's amendment, escrow.rs, CONTEXT.md, custody.rs twice, ADR 0012's tenth and twelfth amendments, negotiation.rs and the grant-door pin, and escrow.rs's headline stops claiming that no route moves a key between lanes when flush is one and a grant is a new filing at the recipient rather than a carry. secondary items: the stale pin citation and the now-false claim around it, spawn.rs's three false statements about the orphaned child, the unproducible-seal fallback recorded as releasing nothing only at the instant it files, expires_at declined as a term of the staging max with the reason at the code, the demotion refusal naming which of the two recordings fired, the census group sentence that named its members, ADR 0012's push qualifier at the tip with no want, the ingest cost fixture given a publishes-nothing control, workflow.md's three refusals derived from CargoTestFailure and the PRE_LAND constants, a usize subtraction restated as a sum so the sentence beside it can print, and orchestrator.rs's tombstoned pin names declined with the reason. red under mutation, counts read each time: the vis term reverted to the arriving copy (659 passed and 1 failed), the grant_ids term reverted (659 passed and 1 failed), the already-held guard narrowed to the one lane it writes (659 passed and 1 failed), the seal question asked through a second store read (659 passed and 1 failed, object_gets reading 8 where 0 belongs), the lying sync copy made a different object (659 passed and 1 failed, the vacuity control firing), the lying grant ids made to agree (659 passed and 1 failed, the second vacuity control firing), the publishes-nothing control inverted (1353 passed and 1 failed), the refusal made to say both either way (1352 passed and 2 failed) and the ingest cost relation moved by one (1353 passed and 1 failed), each restored to 660 and 1354 passed with 0 failed. no migration, no wire or format byte moves and no host behaviour moves, but which lane a sync-borne key is filed into moves on the client, so this rides the next release and owes no deploy. the workspace suite is green (4140 passed over 132 binaries, 8 ignored) (#2212)
c643aadc · dbf3dbe6…diff - the arc's one self-contradiction is gone and its newest list shape is answered wherever it stands: #2214 asked for every premise to be re-verified on the tree first and items 1-5 all held, and two sites the ticket did not cite held with them plus one list that was already stale — ADR 0007's own #2205 header carried both the hand-maintained variant count and a second copy of the false carry claim, and the impl doc over the store door named the ingest paths where the chokepoint property would have named itself, missing the tag-1 grant door and put_published. the self-contradiction is ADR 0012's twelfth amendment, which #2212's commit message lists as corrected and which it edited one clause later, still saying a crossing between positions carries a key into the lane it is already in while ADR 0007 and escrow.rs both say a grant is a new filing at the recipient that reads the seal and answers for itself; the clause is deleted with the false reason named rather than quietly dropped, because the surviving conclusion has to be seen standing on what an older binary already wrote and on the absence of any route that takes it back out. the sharper lesson is item 4's and it is sharper than do not list members: the sentence that failed was already in the correct derived shape, and a count welded on beside it in the same breath was the half that went stale, so every replacement says what decides membership and stops there — store's call sites are the ingest paths and the compiler enumerates them, file_granted_key's call sites are the Frame variants that carry a grant, and the headline over the grant-door pin stops counting the doors it drives. secondary items: the store door's promise that a garbage key is rejected rather than filed is narrowed to the held-address arm with the fresh arm's literal true named as vacuous where a reader meets it, the carry definition stops being true by construction and says what a carry does to the lane so its own falsifier lands on that axis, the three co-travelling seal facts become a Weighed struct whose third field is named for what both arms make it, embargo_reveal_at delegates its Visibility half to embargo_instant so Embargoed is destructured for an instant in one place and the zero belongs to whoever asks for a number, the demotion refusal spells every bool pair and returns the name from the match so no arm asserts a pair it cannot be reached with, embargo instant becomes the glossary's reveal_at, the 127-character ADR line is rewrapped, and the one-address sync fixture preamble collapses into sync_of_one. red under mutation, counts read each time: the entry arm of the demotion refusal made to say the seal (2 passed and 1 failed), embargo_reveal_at's collapse moved off zero (655 passed and 5 failed) and the fresh arm's unrefuted made false (523 passed and 137 failed), each restored to 3 and 660 passed with 0 failed. no migration, no wire or format byte moves, no host behaviour moves and nothing an operator or a client can observe moves, so this rides the next release and owes no deploy. the workspace suite is green (4140 passed over 132 binaries, 8 ignored) (#2214)
ea968e98 · dbf3dbe6…diff - a deposit plan builds the tree it ships once instead of once per custody lane, and the first act had to be an instrument that could see the difference: no counter a land can read moved over a repeated whole-graph pass, so Work::GraphSorts now tallies ChangeGraph::in_order and is gated at 0% beside the object pair and store_file_reads, reading 8 on the gated fixture and one string across the drift pin repetitions. THE TICKET ASKED FOR THE TALLY TO REUSE Work::TreeWalks INSIDE finalized_tree and for ADR 0073 to re-decide that exclusion, and that was refused rather than followed: tree_walks counts a whole-tree FILESYSTEM walk of a working tree and this is a graph pass, and the in-process tier links loot-core and never loot-cli while nothing in loot-core outside its own tests calls the deposit lanes, so a tally there would have read zero on that tier anyway and the pin would have stayed green while its stated reason went false. the exclusion therefore stands unmoved on its own measurement, and the ADR records the trigger that did not fire rather than a re-decision it did not force. measured in the counter and not in wall clock: a forge plan over a fixture carrying an embargoed path, a Restricted path and the Internal default read 3 sorts before and 1 after, a relay plan 2 before and 1 after, the three lanes now taking the finalized tree as an argument plan_deposits builds once. the tips membership test inside that build became a set lookup rather than a Vec scan, which no counter can see and which is named as such rather than claimed. red under mutation, counts read each time: the shared build removed so each lane derives its own again failed the new pin (0 passed and 1 failed, graph_sorts 3 against 1, and with the forge arm relaxed the relay arm failed at 2 against 1), the tally dropped from in_order failed the anti-vacuity pin (10 passed and 1 failed) and the new pin (0 passed and 1 failed), and graph_sorts dropped from gate::COUNTERS failed four at once (7 passed and 4 failed), each restored to 11 and 1 passed. the two generated membership sentences in CONTEXT.md and HUNT-PERF.md are pasted by hand as their pins demanded, the hand-written workspace width in verbs/mod.rs moves to 390, the visibility census gains the two argument bindings the by-reference lanes create, and a count in loot-count that was wrong in the commit that wrote it is replaced by the property. no migration, no wire or format byte moves and no host behaviour moves, so this owes no deploy, though the gate records a new metric from the next land. the workspace suite is green (4174 passed over 132 binaries, 8 ignored) (#2225)
fe6a089f · dbf3dbe6…diff - the sentence written to replace a deleted list names the definer instead of counting, and the fix-up is read back under the rule it enforces: ADR 0073 said the gated work counters were the object pair and a further one at each amendment since, which is false because the #1903 amendment added none, so the clause now names measure::WORK_COUNTERS and stops. every other count beside a set that can grow is replaced by what decides membership rather than by a corrected number - the first nine and the six artifact counters and the other three Work variants in gate::COUNTERS become every row WORK_COUNTERS does not name, the Tally Display doc shows one label=value per Work::ALL entry and an ellipsis instead of hand-listing the labels, the deposit lane counts point at kind.widens, and the landed proposal state points at store::ProposalState. THE SWEEP MISSED THREE COPIES OF ITS OWN ITEMS and they are corrected here too, because a corrected claim left standing in a second copy is how #1903 shipped one stale: the three lanes sentence in loot-core custody and again in its test, and the ADR 0075 three terminal states sentence duplicated in a forge server test. two code fixes ride along: #2174 landed the wire_state doc inside the propose withdraw doc comment, so the may_propose rationale documented the wrong function and the route documented nothing, and the stale-tip refusal was broken across source lines with no continuation, so an operator read the indentation of the source in the middle of the message. ONE ITEM IS CORRECTED RATHER THAN FOLLOWED: the ticket reads that literal as carrying a newline, and on the tree it is a single line carrying two runs of collapsed indentation, so the rendering defect is real and its shape is runs of spaces rather than a break. red under mutation, counts read each time: the continuations removed so the break rides in the string again failed the strengthened pin (0 passed and 1 failed, the panic printing the refusal across three lines), restored to 1 passed. the CONTEXT rewrap is a pure reflow, identical byte count and identical word stream, so the generated membership sentence and its pin are untouched. 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, 0 failed, 8 ignored) (#2234)
f7721acb · dbf3dbe6…diff - the push-time deposit plan asks the deposit ledger before its standing lanes ask any seal, so a push that ships nothing no longer pays a cold store read per held path for rows already deposited: plan_standing narrows the shipped tree by the ledger in place, one retain applied once, and hands the restricted and internal lanes that narrowed tree, which keeps the loot-core lanes remote-blind and plans the same deposits because each lane decides an entry on that entry and what this position holds at its address. measured cold on 66 standing rows at a forge, counts read each time: 66 disk reads with the ledger empty, as before; 50 with 16 rows delivered, where it read 66; 0 gets and 0 disk reads with every row delivered, where it read 66 of each. the plan is pinned field for field on an empty, a partly full and a full ledger holding rows at another remote and for another recipient, green before and after. red under mutation, each restored to green: the old lanes-first order (22 passed and 1 failed, the cost pin at 66 against 50), the dedupe ignoring the remote (20 and 3, pitch.md dropped), ignoring the recipient (21 and 2, f00.md dropped), no dedupe (19 and 4). the timed lane is not narrowed, its dedupe being per recipient. custody.rs, ADR 0012 in a fifteenth amendment and CONTEXT.md record what it costs now. no migration, format byte or wire byte moves, so this owes no deploy. the workspace suite is green (4402 passed over 139 binaries, 13 ignored) (#2208)
5149e99f · dbf3dbe6…diff - loot maroon refuses while a working change is in progress, where it signed a change built on every head, the working change among them: the re-seal took its tree from current_tree and its parents from heads, so the content of that change travelled under the maroon signature past the finalize gates loot new runs, a .env reached a peer holding no grant with exit 0, and the peer held the signed maroon over a parent no bundle ships. the check sits in maroon_inner, which forward and hard maroon share, before anything is sealed or recorded, and it covers a working change that holds no edit, such as one describe named over a clean tree, and one the implicit capture of the maroon has just recorded; the refusal names loot new as the route. building on the finalized tip was rejected, since the working change would stay a sibling head pointing the path at the old object. ADR 0010 gains a #2467 amendment, and CONTEXT.md, the help line and the maroon-identity skill say what maroon signs. a #1485 test whose maroon arm signed over a described working change now finalizes first and takes --no-snapshot, the spelling on which no upstream read promotes the key, and it stays red with the maroon flush deleted (0 passed and 1 failed); a relay purge test signs its anchor. pinned through the binary, red first: the peer read the .env and held two heads (2 passed and 3 failed over the maroon smoke pins). red under named mutations, each restored: no guard (11 passed and 1 failed over the engine maroon tests, 2 and 3 over the smoke pins), refusing only a working change that holds an edit (4 and 1 over the smoke pins), the hard arm unguarded (11 and 1 in the engine), and the refusal after the change is recorded (11 and 1 in the engine). no format, wire or migration byte moves, so this owes no deploy. cargo test green, 4671 passed over 142 binaries with 13 ignored (#2467)
d03ee3f9 · dbf3dbe6…diff - loot runner add enrols a runner and every push to a forge keeps it current, the second land of #2130 (ADR 0091 §3, §4): add registers the row on the forge first, which judges it, then writes the runner's key into .loot/runners, and when it minted the key writes a home file under .loot/runner-homes/ in ADR 0059's KEY=VALUE dialect with the forge URL, name, kinds, pubkey and private seed, and deposits through the same route a push takes. a push to a forge reads the repo's runner rows from /runners/list and seals a key-only grant, DagRepo::grant_key_only, for each held Restricted or Internal key inside each live runner's scope, filed through /runners/deposit and deduped by the (remote, oid, pubkey) ledger, written only once a batch is delivered; an embargoed key is never deposited, since grant_key_only reads the keyring and not the escrow, and a forge that will not answer the list is a note and not a failed push. ⚠ the #2162 amendment put a runner in .loot/peers, which is also the burn-trust set, the unquarantined grantors and every timed grant's recipients, so the runner file is its own and only the attester trust reads it, keeping the operator's trusted-but-not-a-recipient decision by construction; ADR 0091 records the correction. the scope always reaches .lootpipeline, added when left out. the verb census moves: README 88 verbs, 146 usage lines, ADR 0066 at 88 dispatched and 51 refusing the flag, PROSE_ONLY_CEILING 51 to 52 with its reason, the placeholder census and the site verb list. red under eleven named mutations, each restored, counts read each time: the key-only grant falling back to the escrow (0 passed and 1 failed), and 1 passed and 1 failed over the two workspace tests for no scope filter, no ledger check, a retired runner planned, no Internal lane, the ledger written before delivery, the attester trust ignoring the runner file and the runner file read from peers, five of which also went 0 and 1 end to end, as did the runner written into .loot/peers, a push depositing nothing to runners and the pipeline file left out of the scope. the loot-cli, loot-core, loot-identity and loot-net suites green but for two census tests fixed since, and the site gate green at 842 passed (#2130)
37a00dd9 · 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.