Changes touching this path

  • 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…

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.