Story: Lock down uppercase UUID invariant
Table of Contents
This page documents a story in Sprint 18. It captures the goal, current status, acceptance criteria, and the tasks that compose it.
Goal
Make uppercase UUIDs a hard invariant of the documentation system: every :ID: property and every [[id:UUID]] link in doc/ and projects/*/modeling/ must use uppercase hex, and every code path that mints or templates a UUID must produce uppercase by construction. Prior commits (abe6686ce, a8ddd4920, d1022161e) normalised the existing corpus and uppercased the freshly-minted ID in the v2 doc generator; this story closes the remaining holes so the invariant cannot drift back.
Status
| Field | Value |
|---|---|
| State | DONE |
| Parent sprint | Sprint 18 |
| Now | Completed 2026-05-24. |
| Waiting on | Nothing. |
| Next | None. |
| Last touched | 2026-05-24 |
Acceptance
- All
v2_doc_*.org.mustachetemplates carry uppercase concept-link UUIDs (story,task,sprint,version,capture,investigation,product identity). v2_doc_generate.pyuppercasesparent_idandpredecessor_iddefensively, so a lowercase value passed via--parent-id/--predecessor-idstill produces an uppercase link in the output.build/scripts/generate_release_notes.pyuppercases the generated release-notes UUID.projects/ores.codegen/scripts/regenerate_backlog_indexes.pyholds its hardcoded UUID constants in uppercase.- A sweep across
doc/andprojects/*/modeling/for lowercase:ID:properties and lowercase[[id:...]]links returns zero matches. Recipe placeholders such as[[id:UUID]]and[[id:<parent_id>]]are excluded by design. - Generating a fresh story or task end-to-end through
generate_v2_doc.shproduces a file in which every UUID is uppercase, without manual touch-up.
Tasks
| Task | State | Start | End | Description |
|---|---|---|---|---|
| Task: Enforce uppercase UUID invariant in codegen and audit docs | DONE | 2026-05-24 | 2026-05-24 | Patch v2 doc templates and Python scripts so every generated UUID and link is uppercase; sweep org docs for stragglers. |
Decisions
- Why hardcode uppercase in the templates rather than post-process the rendered output. The concept-link UUIDs are not generated, they are author-baked content. Uppercasing them at the template source matches how the rest of the doc graph already stores them, and avoids a post-render rewrite step in the generator.
- Why also keep the defensive
.upper()onparent_idandpredecessor_id. The generator auto-detects parent IDs from on-disk org files (already uppercase), but a user may pass--parent-idor--predecessor-idmanually from a copy-paste source where casing is unknown. Belt-and-braces. - Why uppercase
GLOSS_CAPTUREandINDEX_IDSconstants inregenerate_backlog_indexes.pyeven though the on-disk index pages were already uppercased by prior normalisation. The script regeneratesnear.organdfar.orgfrom these constants; if it ran with the old lowercase constants it would silently undo the on-disk normalisation. Sourcing them as uppercase makes the regen idempotent against the invariant. - Where the invariant is documented. A single sentence under
:PROPERTIES: :ID:in document types points at this story for the rationale and audit trail. Future contributors editing UUIDs by hand discover the rule there. - Malformed UUIDs in v1 plans treated as a separate bug. The exhaustive sweep surfaced three v1 plan documents whose
:ID:values were uppercase but containedHandGcharacters — invalid hex, predating this work. Rather than fold them into the case-enforcement commits, they ship as a self-contained[doc] Fix invalid (non-hex) UUIDs in 3 v1 plan filescommit so the case/validity concerns stay distinct ingit log.
Out of scope
- Postgres / generated SQL UUID literals (
00000000-0000-0000-0000-000000000000and live workspace sentinels). SQL has its own conventions and case rules; this story is about the documentation graph only. - External docs in
external/and third-party samples.