The resolution ledger never shrinks - #437
Conversation
Uniqueness protects identity integrity and does not protect completeness. The two are separate properties and the ledger only had the first. Three per-category tranches ran serially and each rebase conflicted on sources/resolution_ledger.yaml, because every pass appends to it. Taking one side of that conflict would have been invisible to every existing guard: dropping the two entries #431 appended produces no duplicate, leaves the four sentinel cases intact, and loads clean. Demonstrated rather than assumed - simulating that side-pick gives 288 entries, loads with no DuplicateResolution raised, and fails the new floor at 288 < 290. A count floor rather than a digest over the key set. A digest would also catch a swap, but it changes on every legitimate append and so would be noise; a side-pick does not swap, it drops a block. A floor rather than named entries, deliberately. Pinning the decisions a given pass appended couples the test to the order those passes merge in, which is the one thing a serialized queue cannot promise - the first draft of this did exactly that and failed on an entry still sitting in an open PR.
…n main The floor was 290, which was main before #433 landed. With MaiBot's decision now durable the ratchet can describe the actual ledger rather than an intermediate serialized state, which closes the one-entry tolerance: dropping #433's single entry gives 290 and would have passed the old floor. Verified both directions at the new number - dropping #431's two-entry block gives 289 and dropping #433's single entry gives 290, both load clean with no DuplicateResolution, and both now fail the floor. Also records why the constant is hand-written rather than derived. A floor that computes itself from the current ledger would derive 289 from a damaged one and prove itself correct; the independent number is what gives the test memory.
68404d6 to
3fb911a
Compare
|
Rebased onto main now that #433 has landed, and raised the floor 290 → 291. New head That closes the one-entry tolerance I had left deliberately. At 290 a side-pick dropping #433's single MaiBot entry would have given 290 and passed. Verified both directions at the new number: Both load with no I also recorded in the constant's comment why it is hand-written rather than derived, since that is the non-obvious part of the design: a floor that computes itself from the current ledger would derive 289 from a damaged one and prove itself correct. The independent number is what gives the test memory. One acknowledged limitation, stated so it is not discovered later: the floor only stays strong if append PRs ratchet it. If the ledger grows 291 → 340 with the constant untouched, losing 20 entries would pass. That is an argument for making "append a ledger decision → raise the floor" part of the contribution contract rather than for different machinery, and the failure message already points a maintainer at |
Uniqueness protects identity integrity. It does not protect completeness. The ledger only had the first.
The gap
Three per-category tranches ran serially tonight (#431, #432, #433) and each rebase conflicted on
sources/resolution_ledger.yaml, because every pass appends to it. The tempting resolution is to take one side — and doing so is invisible to every guard the file already has.Demonstrated rather than argued. Simulating a side-pick that drops the two entries #431 appended:
The dropped file produces no duplicate, leaves the four sentinel cases in
test_the_four_cases_that_produced_this_fileintact, and loads without error. Two governance decisions would simply be gone, and the next bulk run would recreate both — which is the exact failure the ledger was built in #419 to prevent.What this adds
One invariant:
len(load()) >= LEDGER_FLOOR, currently 290. Raise it deliberately when a pass appends; never lower it. The failure message points atgit log -p --followfor recovery rather than inviting a lower number.Two design choices worth stating
A count floor, not a digest over the key set. A digest would also catch a swap, but it changes on every legitimate append, so it would be noise rather than signal. And a side-pick does not swap — it drops a block, which a count catches.
A floor, not named entries. The first draft of this pinned one entry per recent tranche, and it failed immediately on
Mai-with-u/MaiBotbecause that decision is still sitting in open PR #433. Pinning specific decisions couples the test to the order passes merge in, which is the one thing a serialized queue cannot promise. That failure is the argument for the floor, so the docstring records it.Note on ordering
Floor is 290, which is main today. #433 appends one more, so after it lands the floor can be raised to 291 — but it does not need to be for this to be correct: a floor is a lower bound, and leaving it one behind main costs only the sensitivity to a single-entry loss. Raise it at the next convenient pass.