Changes touching this path

  • a withdrawal binds the tip it was written for, and the format minor deliberately does NOT move: a withdrawal names a proposal by its durable change id, which is stable across revisions on purpose, so a captured envelope replayed after a REVISED re-proposal ended an offer its author never withdrew - and the author is the only party withdraw admits, so the captured envelope is exactly the one that works. the signed bytes now carry the tip, compared inside the retry loop against the row that round actually read rather than once outside it, which is the same window #1959 closed for the author check. the replay is run end to end: the author withdraws at one tip, revises and offers again, and the byte-identical envelope is refused with the proposal left open, while a withdrawal naming the version actually on file still works, so the refusal is the binding and not a wall. ⚠⚠ the ticket instructed a FORMAT_MINOR bump and following it would have been a mistake, which is the finding worth more than the field: the marker's minor is ONE GLOBAL CONSTANT stamped onto every artifact rather than a per-payload version, and persist_codec's is_canonical compares a persisted store against the major and minor pair to decide whether the graph file must be REWRITTEN, so bumping it to announce one optional field on one wire message would force a rewrite of every local store on every machine. every read_version caller discards the minor, so the bump buys no reader anything the trailer's presence does not already give, and a test pins both constants unmoved with that reasoning beside it. the same plan sits on #2159 for its kinds trailer and is flagged there rather than left. an older client sends no tip and is accepted unbound, exactly as safe as before the field existed and no less, since an attacker cannot cause that shape and can only replay one already signed - so the exposure shrinks to withdrawals old clients signed rather than widening. both interop directions are pinned: an old payload decodes here reporting no binding rather than inventing one, and a new payload is byte-identical to an old one up to the trailer, which is what every deployed forge reads. red under mutation, counts read each time: the tip comparison made unreachable so the replay succeeds (430 passed and 1 failed, restored to 431). ⚠ three rounds of missed call sites, each invisible to the scope before it - a package lib run compiles neither integration tests nor other packages, and only the full suite answered. no migration, FORMAT_MAJOR and FORMAT_MINOR both unmoved, and the forge behaviour moves, so this rides the next deploy. the workspace suite is green (4169 passed over 132 binaries) (#2180) 7f2cbeca · 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.