Changes touching this path

  • the forge test harnesses no longer race each other migrating a scratch database: a migration that adds one of the narrow roles creates it inside a check-then-act DO block and then states the attributes with ALTER ROLE, which writes a pg_authid row no database owns, so two harness threads each migrating their own scratch database write that row at once and postgres answers one of them tuple concurrently updated, the flake #1744 saw once in CI as migration 0003 failing on a fresh database. it reproduces three ways against a throwaway cluster: six concurrent loot-forge --migrate processes over six fresh databases failed 4 of 18 with that error, each leaving a ledger stopped at version 2 with the server log naming migration 0003; the statement alone, looped from sessions in separate databases, failed 2 of 4 where 18 single statements collided 0 times; and the two migrating pg::tests cases run as a pair went red in 5 of 8 runs. the new pg::migration_serial takes a session-level pg_advisory_lock on the admin url database and gives it back when the returned value drops, so the disconnect is the unlock and a test that panicked holding it releases it too. it is a lock on the cluster rather than the MIGRATING mutex the harnesses under tests/ each carried, because the row belongs to the cluster and the lib tests and each tests/*.rs are their own binary; every harness application of a migration now holds it, namely PrefixDb::new, the new apply_migration that apply and the new reapply share, the member read gate restore, and the rls case that makes a role bypass row security, where a second binary migrating during that window would have had migration 0014 refuse it. with the lock the pair is 0 of 8 red and four concurrent processes of it are 0 of 12; a process mutex in its place is 6 of 12 red, no lock in PrefixDb::new is 2 of 6 red with the census naming that function (1 passed, 1 failed), the lock dropped from the member_class harness is named too (0 passed, 1 failed), and the pg_advisory_xact_lock spelling reddens the new lock case (0 passed, 1 failed) and 4 of 6 runs. every_scratch_database_migration_holds_the_cluster_lock walks src/ and tests/ for the statements that apply a migration, holds the set of functions equal to what it expects and names migrate as the one deploy path exempt, since it already serialises runs on one database with LOCK TABLE schema_migration; it went red with ROOTS emptied and with that exemption emptied (0 passed, 1 failed each) and it needs no cluster, unlike the cases it reads. the_migration_lock_is_one_lock_for_the_whole_cluster pins that an advisory lock is per database, which is why the lock is taken on the url unmodified. PrefixDb migration panics print the server sentence through pg::describe now, where the original report read db error. docs/evidence/forge-datastores.md carries the run and answers the suspicion its #1744 section left open, the forge suite is green against the cluster (469 passed over nine binaries, no skip note) and the workspace suite is green (3832 passed) (#1956) 8aebb4de · dbf3dbe6…
  • review sweep 15 fix-up: the evolog dash is on an empty FIRST LINE, the subject rule now has one home, and the surface prune says plainly that it discards. loot evolog --porcelain dashes its last column when the message first line is empty, which an empty message and a message whose body starts under a blank one both are, and three places said an empty message: the doc above the encoder, the ADR 0023 amendment of 2026-09-17 and CONTEXT.md. The wording moved rather than the bytes, because dashing on an empty message instead is a porcelain byte change for a message that opens with a newline, and a new spawned pin, a_message_whose_first_line_is_empty_is_the_dash_too, puts a run under the sentence: the file reads 5 passed, and with the whole message put back in the column it is 2 passed, 3 failed, the single-line control among the two that stay green. render::subject_line was billed as the one home while six call sites derived that first line inline, the four the ticket named plus the shortlog author rollup and the missing-object listing, so log --oneline, heads, show, range-diff and those two all ask it now, and the near-cousins that each do something more, the note ellipsis, the revert subject and the loot-first PR title, are named beside it rather than routed. The control is the spawned binary: 28 outputs over two repos, one carrying a multi-line message and one a blank first line, are byte-identical before and after, and with subject_line returning the whole message 10 of 11 of them move and the loot-cli lib suite reads 1219 passed, 4 failed, naming the shortlog rollup and the oneline row among them. The surface stale-debt prune keeps the removal error it discards, which #2018 declined and its closing note then reported as fixed, and the doc above it now says the let _ is deliberate, that apply_change_delta propagates where this one does not, and why: the delta writer snapshots, so a refused removal would be recorded as a deletion the disk never took. The removal census in workspace.rs states what its matcher is blind to, a type-annotated binding, a call split over lines and a path derived from a root-joined one, and names disk_entry::drop_empty_parents, whose remove_dir climbs from a dest its caller joined and which the fold cannot reach, because remove_dir takes an empty directory only and a directory holding a file the writer just wrote is not empty; widening the matcher would not have reached that call, so the blindness is stated instead. CONTEXT.md gives that census its crate and spelling scope. migration_serial records that hold blocks and is not re-entrant, and that it serialises migration runs rather than pg_authid, naming the fixed-name CREATE ROLE in tests/rls.rs that writes the shared catalog outside it; Held loses its Option and its Drop impl, since dropping the field is what the compiler already does. The migration census compares a BTreeSet the way the removal census does: with the const reordered it stays green where the Vec compare it replaced goes red (0 passed, 1 failed), and a name dropped from the const reddens it (0 passed, 1 failed). pg/tests.rs cross-references its own source walk and fn finder to source_walk in loot-cli and states where the pair differs. The folds_case helpers in two test files become folds_case_under, so neither shadows the disk_entry rule it asks. The rewrap leftover in account.tsx that #2018 moved rather than fixed is rewrapped with its neighbours, and so is a second one mid-paragraph in CONTEXT.md that the same rewrap reached. The forge suite is green against a throwaway 17.0 cluster (469 passed over nine binaries, no skip note) and its lock case goes red with the session dropped rather than held (0 passed, 1 failed), the site gate is green (643 passed, every surface under its ceiling), and the workspace suite is green (3833 passed) (#2023) c38e3299 · 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.