ADR 007 revision 7: hand-written ids are canonical (improvise-sboz)

Answer 29: the gensym suffix only ever goes on a generated id; an id
read from a file is never renamed, and two identical hand-written ids
refuse the load (answers 11, 22, 29). No question open; still proposed.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VdfEGXA7aJ6ftMsCc9PJze
This commit is contained in:
Edward Langley
2026-09-24 15:19:32 -07:00
parent 9d0b660d56
commit 56fb9abb88
+39 -10
View File
@@ -1,7 +1,7 @@
# ADR 007 — Stable identities for categories, items, formulas and views
**Status:** proposed (2026-09-24). Revision 6 folds in the user's fifth round of
answers, and **no question is left open**. Revision 4 checked the ADR against work
**Status:** proposed (2026-09-24). Revision 7 folds in the user's sixth round, one
answer, and **no question is left open**. Revision 4 checked the ADR against work
that landed since revision 1. This ADR is not accepted until the user says it is. ·
**Decided by:** the user, in part; see "What the user decided" ·
**Ticket:** improvise-sboz · **Unblocks:** improvise-fz77, improvise-fdk1 ·
@@ -300,6 +300,24 @@ This decides three things:
Clause 4 is rewritten to match. The rules this replaces (lowest free number, and
suffixing only on import) were revision 5's.
### Sixth round: answer on revision 6
The user replied at 2026-09-24 22:09Z to their own answer 28, on line 782 of
`e044c968`:
**29. Hand-written ids are canonical.**
> "You have to treat the file's hand-written ids as canonical, doing otherwise will
> corrupt the data section."
This constrains answer 28. The `gensym` suffix applies only to an id the program is
**generating**, on import, when an id-less file loads, or when the host creates
something in a session. **An id written in a file is never renamed**, because every
data key and reference in that file spells it. Clause 4 says what follows:
- a generated id that would collide with a file's id takes the suffix, and the file's
id keeps its spelling;
- two identical hand-written ids in one file are refused at load, not repaired.
## Context
**Today the name is the identity.** `Model::categories` is an `IndexMap` keyed by name.
@@ -468,11 +486,20 @@ The remaining rules:
removed is still reserved in memory.
- **Determinism:** the counter advances in file order or creation order, so the same
input gives the same ids, never hash order (gotcha 18).
- **This ADR's reading:** where a candidate `slug-<n>` is itself already taken, for
example because a new-version file spelled it by hand, the counter advances again
until the candidate is free. The counter lives in memory and starts at 1 when a
document loads. It is not written to the file, since retired ids are not persisted
(answer 13).
- **Hand-written ids are canonical** (answer 29). The suffix only ever goes on the
id being generated. An id read from a new-version file keeps its spelling, even
when it looks like a slug or a suffixed one (`east-3`), because the file's data
keys and references spell it. So when a generated candidate `slug-<n>` is already
taken by a file's id, the counter advances again until the candidate is free.
- **Two identical hand-written ids in one file are refused at load** (answers 11,
22 and 29). By answer 22 they are a duplicate even when they declare different
kinds of thing, such as a category and an item. Renaming either one is exactly
what answer 29 forbids, and answer 11 already refuses a duplicate because such a
file "can't safely resave". The error names both lines (clause 5). This follows
from the answers, so it is not asked as a new question.
- **The counter** lives in memory and starts at 1 when a document loads. It is not
written to the file, since retired ids are not persisted (answer 13). That is
this ADR's reading.
- **Across a save and reload, reuse is accepted** (answer 13, Q10(b)). Retired ids are
not persisted. So remove `East`, save, reload and add `East`, and the new item gets
`east` again. An in-flight command from before the reload is refused by the
@@ -560,7 +587,8 @@ the walker drops such lines, so without its own strategy every formula property
vacuous (improvise-rxz9).
**A duplicate, unknown or missing id in a new-version file refuses the load** (answers
11 and 21; answer 11:
11, 21, 22 and 29; a duplicate across two kinds of thing counts, and a hand-written id
is never renamed to resolve one; answer 11:
"don't load the file because you can't safely resave it"). The error names the id,
and for a duplicate it names both declarations. Repairing silently is ruled out,
because references carry ids (answer 3): reassigning a duplicate would silently change
@@ -803,11 +831,12 @@ are answered.**
| 20 | Virtual category ids | (a) lower-case ids, capitalised displays | **answered: (a)**, and no other id may begin with `_` (answer 23) |
| 21 | An old file naming an undeclared item | (a) declare it at load | **answered: (a)** (answer 24) |
| — | "A new file version" (a Consequences line) | — | **answered**: the version must be explicit (answer 25); it already is, and clause 10 adds a second literal |
| 22 | The suffix's spelling, and interactive collisions | (a) plain number; (b)(i) suffix there too | **answered**: a plain number (answer 27); any collision is suffixed, across kinds, from a `gensym`-style increment-only counter (answer 28) |
| 22 | The suffix's spelling, and interactive collisions | (a) plain number; (b)(i) suffix there too | **answered**: a plain number (answer 27); any collision is suffixed, across kinds, from a `gensym`-style increment-only counter (answer 28), and only a generated id is ever suffixed, since a hand-written one is canonical (answer 29) |
## Still open
Nothing. Every question Q1–Q22 is answered. This ADR still waits on the user's
Nothing. Every question Q1–Q22 is answered, and answer 29 settles the one case it
raises: two identical hand-written ids are refused at load. This ADR still waits on the user's
explicit "Approved" before it is Accepted.
## Consequences