Stable Identifiers
Calor can embed an identifier in a structural declaration so tools can target
that declaration independently of its name or file location. In current syntax,
source IDs are optional: the parser creates an in-memory _auto ID when an
opener omits one.
// Compact source: the parser supplies an in-memory ID
§M{Calculator}
§F{Calculate:pub} (i32:x) -> i32
§E{}
§R (* x 2)
// Explicit source identity for durable external references
§M{m_3hj8x2bw9pkq:Calculator}
§F{f_7k9m2npqrstv:Calculate:pub} (i32:x) -> i32
§E{}
§R (* x 2)Explicit IDs are useful when an agent, index, generated artifact, or review packet needs a durable target. They are not magic history: a rename or file move keeps the identity only if the editing tool preserves the ID, and a copied or extracted declaration must receive a new one.
What Explicit IDs Solve
Names and locations are fragile references:
| Reference | Failure mode |
|---|---|
| File and line | Earlier edits move the line |
| Declaration name | Renames and overloads make it stale or ambiguous |
| Content hash | Any implementation edit changes it |
| Preserved Calor ID | Remains stable across rename, move, and body edits |
That makes explicit IDs helpful for precise edits, navigation indexes, and round-trip metadata. Collision checks make accidental reuse visible, but IDs do not eliminate ordinary textual merge conflicts when two branches edit the same lines.
Optional and Explicit Forms
Structural openers accept either their compact name-first form or an explicit
ID-first form. Indentation ends the block; structural closer tags were removed
and now produce Calor0830.
| Operation | Explicit ID behavior |
|---|---|
| Rename declaration | Preserve the existing ID |
| Move declaration or file | Preserve the existing ID |
| Reformat or change the body | Preserve the existing ID |
| Extract a helper | Give the helper a new ID |
| Copy or clone a declaration | Give the copy a new ID |
Omitting IDs is the simplest authoring style. Materialize explicit IDs when the repository needs persistent cross-tool identity:
# Preview and then assign IDs to compact declarations
calor ids assign src/ --dry-run
calor ids assign src/
# Validate formats, prefixes, duplicates, and production/test policy
calor ids check src/ID Formats
New IDs use a kind prefix plus a 12-character lowercase Crockford Base32 payload. Legacy 26-character uppercase ULID payloads remain accepted for backward compatibility.
| Kind | Compact example | Legacy accepted example |
|---|---|---|
| Module | m_3hj8x2bw9pkq | m_01J5X7K9M2NPQRSTABWXYZ12 |
| Function | f_7k9m2npqrstv | f_01J5X7K9M2NPQRSTABWXYZ12 |
| Class | c_z4w7n5q2gxht | c_01J5X7K9M2NPQRSTABWXYZ12 |
| Interface | i_9p3hxq7m4nbk | i_01J5X7K9M2NPQRSTABWXYZ12 |
| Method | mt_5v8b3kxq2nhp | mt_01J5X7K9M2NPQRSTABWXYZ12 |
Short IDs such as m001 and f001 are for tests/, docs/, and
examples/ (or an explicit --allow-test-ids workflow), not production
source.
To migrate legacy ULID payloads without changing their mapped identity:
calor fix src/ --compact-ids --log compact-ids.json
calor fix src/ --compact-ids --revert --log compact-ids.jsonThe migration is reversible only while its log is available.
Tooling
calor ids check src/ # text validation
calor ids check src/ --format json # schema 2.0 envelope
calor ids assign src/ --fix-duplicates # keep first, reassign repeats
calor ids index src/ --output calor.ids.json # declaration indexKeep ID-preservation rules in agent instructions and run ids check in CI.
The compiler catches malformed, wrongly prefixed, duplicated, and disallowed
test IDs; repository history or an edit hook is needed to detect that a valid ID
was deliberately replaced with another valid ID.
Next
- CLI Reference: ids - Validate, assign, and index IDs
- CLI Reference: fix - Run reversible ID migrations
- Tradeoffs - Where explicit identity helps and what it costs