v0.2.8
folio 0.2.8 — an id now identifies one task, and says so when it does not
0.2.7 preserved task ids across a move. It also, correctly, made a COLLISION PERMANENT: moves used to renumber, which broke collisions apart by accident. This release makes collisions rare, refuses to act on the ones that exist, and gives you a way to say which task you meant.
WHY THERE WERE COLLISIONS AT ALL — arithmetic, not a defect. 1274 distinct ids in a 4-hex (65,536) space with no uniqueness check predicts 12.4 colliding pairs. Fourteen were found. Nothing needed explaining beyond the space being too small and nothing checking.
Widened to 6 hex, accept-short/mint-long. All existing 4-hex ids stay valid
and every reference in every note keeps resolving; nothing validates id shape
and length was decided in exactly one place. At this corpus 6 hex expects
0.05 collisions against 12.5. Minting now rejects a candidate already in use. complete / reopen / update / move REFUSE on a multi-match and name every
match with its file and description, rather than silently picking one and
returning a success message. Batches refuse per-id: the ambiguous one stops,
the rest still run. A qualifier says which copy you mean, and the refusal message ends by
suggesting it — so refusal to retry is two steps, not a dead end. store-project-files is SORTED. Unsorted readdir meant which task an ambiguous
id resolved to depended on filesystem order and was not stable across
machines.THE RESOLUTION BUG WAS NOT WHAT IT LOOKED LIKE. The mutators do not use different lookup orders; they iterate the identical list, inbox first. What happened is that A MOVE DELETES THE ITEM THAT WAS DISAMBIGUATING THE ID: an inbox task is found first and deterministically, and moving it out of the inbox lets the next call fall through to project files, where a different task with the same id wins. Proved with a matched pair — renaming the fixture files so aaa-real.md and zzz-probe.md swap decides which task wins, and nothing about 'real vs probe' enters into it.
That is why 'act, then check where it went, then proceed' could not have worked: the check is valid only until the next mutation.
Found by damage, not by prediction: a leader probe minted t-de62 for a disposable item, an id that already belonged to a live task, and the cleanup completed the real one. Restored byte-identical.
Not fixed here: the 14 existing collisions. Repairing them is a separate, reference-rewriting change, deliberately not bundled with a library release.
The commit this tag names.