Changes touching this path
- two concurrent key rotations can no longer leave an account with no live key, which is a lockout because a key that is not live is refused the sync read, the push and the burn: the floor in site/src/server/identity/identity.ts is a count, the BEGIN it runs inside is the postgres default READ COMMITTED, and two registrations that each retire the key the other leaves live write different rows, wait for nothing, each count the other as live and both commit. registerKey now selects the key rows of that account in account_key under FOR UPDATE, ordered so two waiters take them in the same order, before it writes any of them, so the second registration waits and then counts a state that has stopped moving; and identity/db.ts stops rewrapping a refusal raised inside a transaction as a plain Error, which had been answering every one of them with the anonymous 500 rather than the 422 that carries the sentence the account page renders. the ticket preferred a database constraint and that was the right thing to prefer, but a floor of this kind says a row must EXIST, which a declarative postgres constraint does not say, and the trigger that would say it was measured failing on a throwaway cluster: with a DEFERRABLE INITIALLY DEFERRED constraint trigger counting live rows over a table shaped like account_key, two sessions each retiring the row the other left live both committed and zero live rows were left, because a deferred trigger fires at commit and still reads its own snapshot. what closes the race is a lock either way, so it is taken where it needs no migration, the forge release chain no-migration-since-v0.4.20 state stands, and this owes a site deploy and no forge deploy. red first against a throwaway postgres with the counts read: with the locking clause dropped the new concurrent test ends with the account on zero live keys and neither session refused (1 failed and 10 passed), with the refusal rewrapped the two class pins go red (2 failed and 9 passed), and with the pause keyed on a statement that never matches it refuses on zero arrivals rather than passing vacuously (1 failed and 10 passed). ADR 0038 records the decision beside the #2004 amendment whose argument rests on this invariant, and names what the lock does not cover. bash ci/local.sh is green against a postgres 18 it created (3977 passed over 122 binaries, 7 ignored, and 67 passed over the seven site live files with none skipped) and the site gate is green end to end (668 passed and 62 skipped over 61 files, 62 surfaces) with no ceiling moved (#2015)
18aca67b · 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.