Repository navigation
docs(adr-008): propose moving estate UUIDs to version 8 (proposal) #1158
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,170 @@ | ||
| // SPDX-License-Identifier: CC-BY-SA-4.0 | ||
| // Copyright (c) 2026 Jonathan D.A. Jewell | ||
| = ADR-008: Estate UUIDs move to version 8 (PROPOSAL) | ||
| :toc: preamble | ||
|
|
||
| [cols="1,4"] | ||
| |=== | ||
| | Status | *Proposed*: step 1 of `0-canon/constitution/CHANGE-PROCEDURE.adoc`. Not adopted. `ESTATE-UUID-V7` remains active and binding until this record is decided. | ||
| | Date | 2026-10-05 | ||
| | Supersedes (if adopted) | `docs/UUID-V7-ESTATE-STANDARD.adoc` (`ESTATE-UUID-V7` 1.0.0, effective 2026-09-29) | ||
| | Authority sought | normative: the estate identifier format, its checker, its Hypatia rule (`HYP-UUID-001`) and the migration register | ||
| | Origin | Owner ruling, 2026-10-05, via the selection UI: "v8 everywhere", given after the costs below were put to the owner | ||
| |=== | ||
|
|
||
| == The request | ||
|
|
||
| The owner asked that the standards state UUID version 8 as the estate format and | ||
| that an estate-wide campaign be planned to move every repository to it. | ||
|
|
||
| This record exists because the request reverses a standard that is six days | ||
| old, and the change procedure does not let a binding standard be replaced by | ||
| an edit. It writes down the request, the design that makes it workable, the | ||
| costs, and the campaign. It performs none of the later steps. | ||
|
|
||
| == Context: what version 8 is, and what it is not | ||
|
|
||
| RFC 9562 numbers UUID *layouts*, not generations. Version 8 is not "newer | ||
| version 7". It is the RFC's *custom* layout: the RFC fixes only the 4-bit | ||
| version field (`8`) and the 2-bit variant (`10`), and leaves the meaning of the | ||
| other 122 bits to the implementation. The RFC recommends version 7 for new | ||
| time-ordered identifiers. | ||
|
|
||
| So "v8 everywhere" is only an identifier *format* once the estate says what its | ||
| 122 bits mean. Without that, every repository invents its own layout, and two | ||
| v8 values from two repositories can no longer be compared, ordered or | ||
| validated beyond the version nibble. This record therefore defines the | ||
| estate's v8 layouts. That definition is the substance of the proposal. | ||
|
|
||
| What prompted the request: `metadatastician/berrywiki` already mints v8 for | ||
| content-derived import ids (`crates/berrywiki-import/src/marker.rs`: | ||
| SHA-256 of a domain-separated marker, first 16 bytes, version and variant | ||
| set). Re-importing the same CherryTree notebook reproduces the same page ids, | ||
| which is what makes re-import idempotent. Version 7 cannot express that. | ||
| `ESTATE-UUID-V7` has no clause for deterministic identifiers at all; DEED and | ||
| ANCHOR's `#u5` (UUIDv5, SHA-1) literals already sit outside it. | ||
|
|
||
| == Proposed decision | ||
|
|
||
| All UUIDs created, persisted, exchanged or exposed by an estate repository | ||
| MUST be version 8, variant `10`, under exactly one of the two estate profiles | ||
| below. Lowercase canonical `8-4-4-4-12` text, as before. | ||
|
|
||
| === Profile T: time-ordered (replaces v7) | ||
|
|
||
| Bit-for-bit the RFC 9562 §5.7 version 7 layout, with the version nibble set to | ||
| `8`: | ||
|
|
||
| ---- | ||
| 48 bits unix_ts_ms big-endian Unix epoch milliseconds | ||
| 4 bits ver 0b1000 (8) | ||
| 12 bits rand_a CSPRNG, or the RFC 9562 §6.2 monotonic counter | ||
| 2 bits var 0b10 | ||
| 62 bits rand_b CSPRNG | ||
| ---- | ||
|
|
||
| Every v7 generation rule carries over unchanged: a CSPRNG for the random bits, | ||
| monotonicity within a millisecond where ordering matters, and the timestamp | ||
| never used for authorisation or as an audited event time. | ||
|
|
||
| Generation: take a well-tested library's v7 output and set the version nibble | ||
| to `8`. This is the only bit operation permitted, and it is one shared, tested | ||
| function per language, not a per-repository recipe. | ||
|
|
||
| === Profile C: content-derived (replaces v3, v5 and ad-hoc hashing) | ||
|
|
||
| ---- | ||
| bytes 0..15 of SHA-256( domain ":" name ) | ||
| then: byte 6 high nibble = 0b1000 (ver 8); byte 8 top two bits = 0b10 (var) | ||
| ---- | ||
|
|
||
| `domain` is a registered, repository-unique ASCII string (for example | ||
| `berrywiki-import`). It separates the uses so that one name hashed for two | ||
| purposes cannot collide. Profile C ids are reproducible by design: the same | ||
| domain and name always give the same id. They are not secret and must not be | ||
| used where unpredictability is required. | ||
|
|
||
| === Distinguishing the profiles | ||
|
|
||
| A v8 value does not say which profile produced it, and this proposal does not | ||
| spend id bits to make it do so. The profile is part of the field's declared | ||
| type: a schema, API contract or metadata key that holds a v8 id MUST state | ||
| `profile T` or `profile C`. Open question Q1 records the alternative. | ||
|
|
||
| == Costs the owner accepted | ||
|
|
||
| Put to the owner before the ruling: | ||
|
|
||
| . *A second migration within weeks of the first.* `ESTATE-UUID-V7` (effective | ||
| 2026-09-29) is mid-migration. Mitigation: a v7 value maps to profile T by | ||
| flipping one nibble, reversibly, so a v7 -> v8 alias is a pure function and | ||
| needs no stored mapping table. | ||
| . *Library support.* Few libraries mint v8 directly. Profile T depends on the | ||
| shared nibble-flip wrapper; profile C is a few lines over SHA-256. | ||
| . *External validators.* Systems that accept only v4 or v7 will reject v8. | ||
| External boundaries keep their own formats under the existing "preserve and | ||
| type explicitly" rule. | ||
| . *Self-description.* A reader cannot tell T from C from the bits alone (Q1). | ||
| . *DEED and ANCHOR.* `#u5` literals would become profile C. That is a format | ||
| change in two specifications with their own grammars (`anchor.abnf`), and | ||
| needs its own record. | ||
|
|
||
| == Alternatives considered | ||
|
|
||
| . *Keep v7 and add a deterministic-id clause* (v8 profile C only where | ||
| content-derived ids are needed). Smaller and keeps RFC alignment. Not chosen | ||
| by the owner. | ||
| . *Strict v7; BerryWiki dedupes imports through its stored marker.* Loses | ||
| reproducible import ids. Not chosen. | ||
|
|
||
| == Campaign plan (planned, not started) | ||
|
|
||
| End-condition, per `AGENTS.md` §5e: the repository set is the estate census of | ||
| *live, non-archived source repositories*, measured 2026-10-05T13:38Z as | ||
| *401 (hyperpolymath) + 58 (metadatastician) = 459*, re-measured at campaign | ||
| start with the census revision recorded. The campaign stops when every one of | ||
| them has a migration record in state `complete` or `excluded`. `not checked`, | ||
| `unknown`, `partial` and `blocked` remain non-success states, as in | ||
| `ESTATE-UUID-V7`. | ||
|
|
||
| Phases, each its own increment that leaves `main` green (ADR-006): | ||
|
|
||
| . *P0, ratify.* Steps 2-6 of the change procedure on this record. Nothing below | ||
| starts before P0 completes. | ||
| . *P1, tooling.* `scripts/check-uuid-v8.sh` (version `8`, variant `10`, | ||
| profile declared), the Hypatia rule, the governance-reusable job, shared | ||
| profile T and C generators per estate language (Rust, Zig, Elixir, Julia, | ||
| Bun/JS for existing JS), and their tests, including planted negatives. | ||
| `check-uuid-v7.sh` keeps running until P4. | ||
| . *P2, dual-accept.* Write boundaries accept v7 and v8 and emit v8. Every v7 | ||
| read is aliased to profile T by the nibble flip. | ||
|
hyperpolymath marked this conversation as resolved.
|
||
| . *P3, repository migration.* Per repository, in census order, with a | ||
| migration record: inventory, classify, convert literals and fixtures, | ||
| convert generators, post-cutover scan. Batches are capped by the AGENTS.md | ||
| §5e session limit; findings go to `dev-notes/inbox/findings.md`. | ||
| . *P4, retire v7.* Remove v7 acceptance and `check-uuid-v7.sh` only once every | ||
| in-scope repository is `complete` or `excluded`. | ||
|
|
||
| Known first entry for P3: `metadatastician/berrywiki`. Its import ids are | ||
| already profile C, apart from the domain-registration step. Its page ids are | ||
| v7 minted by hand from `std::collections::hash_map::RandomState`, which is | ||
| not a CSPRNG. That already breaks `ESTATE-UUID-V7` (hand-rolled bits, | ||
| non-CSPRNG randomness) and will need fixing under either standard. | ||
|
|
||
| == Open questions for review | ||
|
|
||
| . *Q1: profile in the bits?* Reserve the top bit of `rand_a` (profile T) or | ||
| of the hash (profile C) as a profile flag, costing one bit of entropy, so | ||
| ids self-describe. Recommended: no; declare the profile in the type, as | ||
| above. | ||
| . *Q2: DEED and ANCHOR `#u5`:* migrate to profile C in this campaign, or | ||
| leave them as an external-format exception? Recommended: separate record. | ||
| . *Q3: profile C hash:* SHA-256 as proposed, or BLAKE3 for speed? | ||
| Recommended: SHA-256, since it is already used in BerryWiki and is in | ||
| every standard library. | ||
|
|
||
| == What this record does not do | ||
|
|
||
| It does not change `ESTATE-UUID-V7`, `scripts/check-uuid-v7.sh`, | ||
| `HYP-UUID-001` or any repository. All of those stay binding and unchanged | ||
| until this proposal is decided. | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
Specify canonical, unambiguous Profile C input bytes.
SHA-256 hashes bytes, but this formula does not define how
namebecomes bytes or how the fields are framed. Sincedomainmay contain:andnameis unrestricted,(domain="a", name="b:c")and(domain="a:b", name="c")produce the same preimage. Different language defaults can also produce different IDs for the same text. Define the name encoding and normalisation, and frame both byte strings unambiguously, before ratifying Profile C.🤖 Prompt for AI Agents