Changes touching this path

  • propose::list reads the tip authors its gate needs in one proposal_tip_authors read, taken only when a row cannot be answered without one, where it read change once per row; the read is scoped by the repo proposal rows rather than repo_change, since a tip is held to change_node alone and the Postgres key-arm pin in tests/proposal_read.rs went red on the repo_change join first tried. ingest asks propose::landed_by_declaration, the one landed closure run over the stored graph overlaid with the push, where it carried a second walk of its own, and a new conformance case hands the ingest transaction a decision that disagrees with the graph both ways and holds both stores to applying it and deriving nothing. counted rather than timed through a new call-logging store double, calls.rs: a listing makes the same reads at 1 row as at 40, a push makes the same reads at 1 open proposal as at 40 with the proposal rows and the graph each read once and the transaction last, and a push with no open proposal reads no graph; a push that lands a proposal is answered in the same bytes as the same push to a forge holding none. measured on a throwaway Postgres 18 over a 1,000-change repo with one-change pushes landing nothing, three interleaved passes of 45 pushes per arm: per-pass medians 1.80 to 1.90 ms with the decision removed, 2.18 to 2.23 ms with it and nothing open, 4.52 to 5.94 ms with 1 to 100 open and flat in the count, against a knowingly wrong arm reading the graph per open proposal at 24.7 to 37.1 ms for 10 and 232 to 360 ms for 100, so the recorded fallback is not taken and ADR 0075 now says so. the proposal read still carries no pipeline verdict; that half of this ticket is left. red under mutation, counts read each time: the list reading change per row (0 passed and 1 failed), each store deriving closes from the head set (0 and 1 on memory and on Postgres, while the existing close case stayed at 1 passed), landed following no parent (0 and 1 on the ingest walk pin), the graph read per open proposal, read with none open and a read after the transaction (0 and 1 each), a landing push answered in other bytes (0 and 1), the tip authors answering every held change (0 and 1), and the Postgres read joined through repo_change (0 and 1 in conformance and in proposal_read), restored to 8, 4 and 1 passed. no migration and no format byte moves; the new read is forge SQL, so the list change reaches the live forge with its next deploy. bash ci/local.sh is green against Postgres 18 (4273 passed over 135 binaries, 10 ignored) (#2235) 3cffe4b6 · dbf3dbe6…
  • Manifest::addresses reads the frames only after a step over them shows the stored keys strictly ascend in Path order, so a manifest whose frames are not its map, two keys naming one path or keys out of the map order, answers from the map, and a pull over a corrupt local graph file no longer asks for an address the map dropped. tier_counts keeps its backslash test, now documented as not a proof, and the a.txt and a/b ordering it cites is corrected to component order in both places. what the check costs, paired --missing readings, one loot-perf-gate --features count per arm, eight interleaved rounds, load idle at 2 to 10 percent: 0.875 to 0.924 ms without it against 2.09 to 2.18 ms with it at width 200, and 5.76 to 5.88 against 10.50 to 10.63 ms at width 800, where a decode arm read 18.75 to 19.13 and 100.0 to 100.8 ms, so about 9x against the decode where it was about 21x and 17x; the Path comparison is most of it. red under mutation, counts read each time: the check removed, compared by byte, not strict, byte equality only and path equality only (1 passed and 1 failed each), restored to 2 passed. riding along: store.rs and ADR 0075 say the ingest transaction still reads for checks of its own and that the landing decision is what reads nothing inside it, that a push with no proposal open pays 0.28 to 0.43 ms where #2177 asked for nothing measurable, and that the fallback decision rests on one repo size; finish_stage says why any refusal is answered by the address on disk; the dated test counts in format_skew_gate.rs and workflow.md say at the time; two rewraps; the calls.rs runner stubs use the file imports. no migration and no format byte moves, so this owes no deploy. the workspace suite is green (4274 passed over 135 binaries, 10 ignored) (#2273) 0a64d280 · dbf3dbe6…diff
  • the review-sweep fix-up over #2303, #2159 and #2172. the wake-up sender: its client is built with no_proxy, so a proxy named in the environment is not used, pinned in a test binary of its own that sets the proxy variables and sees the request reach the pinned address and not the proxy; refused_wake_address now refuses every address that is not globally routable unicast, an ipv6 address carrying an ipv4 one (mapped, compatible, nat64 64:ff9b::/96, 6to4) judged by the one it carries, so 64:ff9b::a9fe:a9fe is refused, with the refused blocks copied from the iana special-purpose registries at the code and a table of example addresses pinning the rule; name resolution runs on a thread waited on for at most WAKE_TIMEOUT_SECS with the request given what is left, and the wake-ups in flight are bounded by MAX_WAKES_IN_FLIGHT, a resolver that outlives its wait keeping its place until it returns. the ingest kinds trailer is now a byte per head in head order, still absent when no head declares a kind, so each head is given jobs for its own kinds only and no job is made for a kind a head has no step of; the pre-2159 decoder still reads the new payload, the one-byte union shape was in no release, and neither format constant moves, pinned against the marker put_version writes rather than its digits. MetadataStore::ingest answers the jobs its commit wrote (store::Ingested) and a push wakes runners for those alone, so a triple a racing request filed first wakes nobody here; the proposal job half answers no error once the proposal is committed and logs instead. the job_cost no-kind pin now states the property, that the job half calls the store for nothing when nothing is declared, instead of a list of methods. the seek busy marker: one that does not parse is believed until its mtime is past the window, so the instant between its creation and its clock no longer admits two holders, and a marker that cannot be created for a reason other than one standing there is retried and then refused naming the state rather than counted as held; the held-marker comment is scoped to a round that claimed it. the census wire half plants each oracle into every payload sent builds and asks each payload for moving bytes, calls the now-public verbs::sync::forge_declaration instead of a copy, records that the fixture declares no kinds, and records the measured flake rate of its minimum reading (200 runs: 40 of 1600 store builds and 15 of 1600 wire builds above the floor, none red). false sentences fixed: jobs.rs says what a false proposal declaration costs the owner and what the trust floor does and does not stop, ADR 0091 section 7 and its 2159 amendment are corrected with a 2307 amendment and the reads-not-writes scope of the job cost measurement, ADR 0090 and ADR 0004 gain corrections, CONTEXT.md names the per-head trailer, the census module doc states the property instead of naming places, and an in_progress rewrap leftover is rewrapped. red under mutation, counts read each time, each restored to green: no_proxy dropped (0 passed and 1 failed), the 2159 refusal rule restored (2 and 2), embedded forms unrecognised (3 and 1), 6to4 unrecognised (3 and 1), the resolver thread not holding its place (0 and 1), the ceiling ignored (0 and 1), the resolve wait unbounded (0 and 1), the trailer encoding the union (25 and 2), the forge unioning kinds across heads (1 and 3), the memory ingest answering every handed job (4 and 2), the push waking for planned jobs (5 and 1), the proposal refusing after a failed job write (3 and 2), the job half reading with nothing declared (3 and 1), an unparseable marker read as idle (1 and 1), a failed create counted as held (1 and 1), the census reading the bundle alone (9 and 4), every payload a copy of the bundle (10 and 3), the ingest payload emptied (11 and 2). no migration, no format byte and no published wording moves; the forge binary changes, so the sender and per-head jobs are live once the forge is redeployed. the workspace suite is green (4360 passed over 138 binaries, 12 ignored), and green under bash ci/local.sh against Postgres 18 (#2307) 665b3cec · dbf3dbe6…diff
  • a push now carries an attestation recorded over a change the remote already holds, so loot tag after loot push reaches a relay and a forge instead of being left behind under a success line: a local attestation ledger (.loot/attestation-ledger) records per remote what each push delivered and is read by a push and by no open or save, written by RepoStore::record_attestations_sent as a read-merge-write under the shared-store lock; the push sends the attestations over the held changes of the remote that the ledger has not recorded beside the send set and prints how many, the bundle builder keeps a late attestation only over a change inside the have closure of the recipient whoever handed it in, the forge /ingest keeps one over any change its repo holds through a new changes_held store read that costs no query when every attestation rides its change, and /info gains an additive late_attestations field so a forge that does not advertise it is sent none, has nothing recorded as sent, and the push warns how many it left behind. the land gate store_file_reads is 24 on its workload with no move, where a first cut that read the ledger on every open measured 26 and was refused; an open reads 20 store files, 21 with that cut. the #48 bound holds on the wire: a push carrying one late tag sent 256 B at a relay and 306 B at a forge over both 2 and 24 held tags. the ticket recipe, whose fresh clone lacked late-tag2 through the 0.4.24 binary, shows it through a lane build. red under mutation, counts read each time, each restored green: the open reading the ledger again (0 passed and 1 failed), the ledger write overwriting instead of merging (0 and 1), the ledger ignored (0 and 2), the late lane dropped (0 and 2), the privacy filter removed (1 and 1), the forge back to in-this-bundle (2 and 1, and end to end 1 and 1), the forge keeping any change (2 and 1), a relay push recording nothing (0 and 2), a push recording to a forge that does not keep them (1 and 1), the /info flag ignored (1 and 1). no format constant, codec byte or migration moves; the forge change is live once the forge is redeployed. the pull half is not built: a pull still carries an attestation only with a change it sends. the workspace suite is green under bash ci/local.sh against Postgres 18 (4425 passed over 139 binaries, 13 ignored; site live suites 7 files passed) (#2251) 414ba6b2 · dbf3dbe6…diff
  • a proposal has a conversation on the forge, stored and written and read, and no CLI flag posts to it yet: migration 0022 adds proposal_comment, keyed under the proposal row so a thread outlives a decline and a re-proposal, holding the envelope its author signed and its body, both set to NULL by a withdrawal that leaves a tombstone saying author or owner, with CHECKs that refuse a withdrawn row holding either, row security bound by the 0017 policy and the rls census widened. POST /propose/comment, /propose/comment/edit and /propose/comment/withdraw are tagged acts over propose::comment, where the rules live: a signer must see the proposal and is otherwise told what an absent one is told; the owner class writes, the accountless namespace key included; anyone else needs a live account key, so a bare keypair or a retired key is refused; members, the proposer and the tip author write through a shut door, anyone else only through an open one; a comment binds the tip on file when it arrives; a path must be in that tree and a reply must name a comment on the proposal; the body is at most 16 KiB of UTF-8, refused and not cut; only the author edits, re-signing the same id, anchors unmoved and bound to the BLAKE3 of the envelope it replaces; the author or the owner class withdraws, and members and proposers do not moderate. the comment id is signed by its author, so the same envelope again is one comment and a withdrawn one is not re-posted back. the thread rides POST /propose/read as a trailer and loot propose --show prints each body only when its envelope verifies against the author and anchors beside it. the flags that post, edit and withdraw a comment, and the disclosure printed before posting, are not built, since ADR 0095 names no flags; ADR 0095 amended and CONTEXT.md updated. red with each piece undone, counts read each time, each restored green: a bare keypair admitted, the door not asked, visibility not asked (each 0 passed and 1 failed on the writer-set pin), a member moderating, the owner editing another comment (0 and 1 each), anchors free to move (0 and 1), a withdrawal that writes nothing (0 and 1), no cap, and the body cut to the cap instead of refused (0 and 1 each), no tip binding (0 and 1), an edit not bound to its prior (0 and 1), an edit past the writer rule (0 and 1), the read sending no thread and an unverified body printed (0 and 1 each); on a throwaway Postgres 18, a withdrawal that kept the words with the CHECKs dropped left the conformance case green (1 passed) and turned the pin that reads the whole row red (0 and 1). bash ci/local.sh is green against Postgres 18 (4476 passed over 139 binaries, 13 ignored). no format constant or codec byte moves; migration 0022 rides the forge binary, so this owes a forge deploy (#2331) 6af9b14f · dbf3dbe6…diff
  • review sweep 13 over the proposal conversation: loot propose --show verifies a comment against the repo and change id of the proposal it reads, since one change proposed to two repos shares its tip and a forge could serve one repo comment as said in another, and cages the body, the path and a refusal quoting the envelope through render sanitize, so ESC or a bare CR from a stranger prints as its control picture. an author is an account: a live key of the account whose key wrote a comment edits and withdraws it after a rotation, the key that signs an edit becomes its author so readers still verify it, and the author withdraws without the visibility check, so a lost view does not strand their words. the door arm calls may_propose_in and a party is asked live_account_in, its second half, where a copy stood. the comment routes read at most COMMENT_REQUEST_MAX, refusing a larger request before its signature is checked. /propose/read serves a thread a page of THREAD_PAGE comments at a time with a cursor, a trailer on both payloads, and --show follows it. an anchor must be spelled as a tree spells a path and is asked of tree_entry by its key through manifest_holds; Manifest::address_of reads manifest bytes the forge does not hold. a runner key under no account is refused moderation, and ADR 0095 now says any live key on the owner account moderates. finalized_before_review sends a lane to lane_landing_route rather than a land with no PR to name. ADR 0095 records the account rule, the paged read, the unbuilt withdrawal warning of its section 6 and the rewritten #2332 pin; ferry.rs says the #2370 pins run in-process; a pin holds the 0022 CHECK equal to COMMENT_BODY_MAX. red with each piece undone, counts read each time, each restored green: the envelope not bound to its proposal (0 passed and 1 failed in loot-net and in the CLI), the body or the path printed uncaged (0 and 1 each), authorship by key, and an edit keeping the old key as author (0 and 1 each), the author asked to see before withdrawing (0 and 1), the door arm a copy (0 and 1), no per-route limit (0 and 1), the forge thread unbounded and the CLI stopping at one page (0 and 1 each), anchors matched by Path over the whole tree (0 and 1), a runner key moderating (0 and 1), the lane land its own (0 and 1), the cap moved on one side (0 and 1). bash ci/local.sh is green against Postgres 18 (4498 passed over 139 binaries, 13 ignored). no format constant or migration moves; the proposal read gains trailers and the forge behaviour changes, so this owes a forge deploy (#2374) bb95f4fb · dbf3dbe6…diff
  • a decline may carry a reason signed with it, kept on the proposal row and shown as the thread closing event: ProposalDeclination gains an optional reason as a trailer on its signed bytes, so a decline with none is the bytes #2176 signs and neither format constant moves, and a reason a carrier swaps or strips no longer verifies. migration 0024 adds proposal.decline_reason with a CHECK holding it to a declined row, never empty and at most COMMENT_BODY_MAX bytes, and replaces the three 0020 proposal views with the column appended, which a text guard holds equal to 0020 once the column is taken out. propose::decline refuses an empty or oversized reason whole and a repeated decline with other words, close_proposal writes the reason only with a decline, and a re-proposal clears it with closed_at. POST /propose/read carries the reason as a trailer after the thread continuation, absent from an older forge, which would drop a reason unread, so loot propose --decline <tip> --reason <text> asks the read first and refuses a reason such a forge would drop, then prints who can read it before signing; --show prints the decline under the thread with its reason caged through render sanitize. frozen before_2336 codecs pin old client against new forge and new client against old forge both ways, and tests/proposal_read.rs compares the column for each reader class with an executable red that blanks it in one view (1 class disagreeing). red with each piece undone, counts read each time, each restored green: the reason left out of the signed bytes (0 passed and 1 failed), the read trailer not decoded (0 and 1 on each of two pins), the route dropping the reason, the read serving none (0 and 1 each), no cap and an empty reason admitted (0 and 1 each), a repeated decline changing the reason (0 and 1), a re-proposal keeping it and a reason written with any terminal state in the memory store (1 and 1 each, the pg stamp of the case green without a database), the CLI signing for a forge that would drop it, the reason printed uncaged (0 and 1 each), --reason admitted beside no decline and no disclosure before signing (0 and 1 each), the migration cap moved and a view joined to another family gate (0 and 1 each). ADR 0095 amended, CONTEXT.md and the forge spec updated. bash ci/local.sh is green against Postgres 18 (4519 passed over 139 binaries, 13 ignored). migration 0024 rides the forge binary and the read and decline routes change, so this owes a forge deploy (#2336) c8d5a6ea · dbf3dbe6…diff
  • the reap of runner deposits ADR 0091 section 4 owed is built, which closes the last half of this ticket: a runner mailbox is now PER REPO (migration 0026 runner_inbox, keyed repo_id and runner_pubkey and blob_address under 0017 binding), because the question that stopped the first attempt was that grant_inbox is keyed by recipient alone while liveness is recorded per repo, so a reap for one repo could drop a key another repo jobs still need and the standing walk, deduped by its own ledger, would never deposit it again. the operator chose scoping the mailbox over a reader that crosses repos or refusing one runner key in two repos, and the third option turned out not to be enforceable where it would have had to be anyway, since runner is keyed repo_id and pubkey and repo-bound so a forge serving one repo cannot see that a key is a runner elsewhere; a global unique index could, at the cost of making its refusal an existence oracle about another tenant repo. it is a second table rather than a repo column on grant_inbox because row security is per table and grant_inbox mailbox is the caller own verified envelope pubkey, which is what closes the read-anyone-mailbox hole, while an owner filing a key for a runner they registered is a different fact through a different door. WHAT IS LIVE IS THREE ROOTS, and a change ships its whole tree so a root manifest entries ARE its objects and there is no ancestor walk: the declared heads, open proposal tips, and the versions of UNFINISHED JOBS - the third is not redundant, since a push moves the heads and a withdrawal closes a proposal while a job made for that version still stands, and a pin holds it alone. history is deliberately not a root, because any change references it is true of every superseded object forever, so a reap rooted there keeps everything and does nothing, which is the failure section 4 describes. the door is loot-forge --reap-runner-deposits --repo owner-hex/repo, which lists and destroys nothing until --apply, the shape reap-grants has, because the row is the only copy of that key the runner gets; it names its repo and reaps no other, refuses --dev, and refuses a missing or malformed --repo. the decision of what is dead is one implementation over the trait and the store write is pure, the split ingest keeps. red under mutation, counts read each time: ten mutations through the policy and the reference store each went red at 1 passed and 1 failed with the pg stamp skipping, each restored to 2 passed - the three roots removed one at a time, a terminal proposal and a finished job counted as roots, a listing that destroys, history as the root, the mailbox read across repos, a deposit that is not idempotent, and a drop that ignores which runner it is for; the history mutation was TOO WEAK on its first attempt and is recorded as such, since an empty version list emptied the live set rather than widening it and so duplicated the heads mutation, and re-done as rooting at every change the repo holds it goes red one line further down at the assertion it is for. on a throwaway Postgres 18 the two mutations only the driver SQL can carry went red at 0 passed and 1 failed after an unmutated green arm, the DELETE no longer naming the runner and the INSERT no longer idempotent, each restored. bash ci/local.sh is green against Postgres 18 (4565 passed over 142 binaries, 13 ignored) with all three new conformance cases running on the real database, and the forge suite is green after the sweep (638 passed). NOT EXERCISED AGAINST A REAL DEPOSIT: nothing writes a runner deposit until loot runner add, so every row this has run against is one a test filed, and the first run over a mailbox a push filled is owed to that ticket. migration 0026 rides the forge binary so the forge owes a deploy, and nothing is broken until then because no code path writes the table yet (#2159) a4cabdfd · 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.