Stable Identifiers
One of Calor's most distinctive features is embedding unique identifiers directly into the language syntax. This document explains why this matters, what challenges it creates, and how Calor overcomes them.
The Problem: Code Identity is Fragile
Traditional programming relies on names and locations to identify code:
// How do you reference this function?
public static int Calculate(int x) => x * 2; // Line 42 in Calculator.csThis creates fundamental problems:
| Reference Method | Failure Mode |
|---|---|
| File + line number | Breaks when any line above changes |
| Function name | Breaks on rename, ambiguous with overloads |
| File + function name | Breaks on file moves or renames |
| Hash of content | Changes on any modification |
For AI agents that need to track, reference, and modify code across development workflows, these fragile identities create real problems:
- Lost context - Agent references become stale after refactoring
- Merge conflicts - Parallel branches create identity collisions
- Imprecise edits - "Edit the calculate function" is ambiguous
- Broken traceability - Can't track an entity through its history
The Solution: Semantic Identity in Syntax
Calor assigns every declaration a unique ID that represents semantic identity, not name or location:
§F{f_01J5X7K9M2NPQRSTABWXYZ12:Calculate:pub}
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
This ID survives ANY refactoring
§I{i32:x}
§O{i32}
§R (* x 2)
§/F{f_01J5X7K9M2NPQRSTABWXYZ12}The ID f_01J5X7K9M2NPQRSTABWXYZ12:
- Survives renaming (
Calculate→Compute) - Survives moving to a different file
- Survives reformatting and refactoring
- Is globally unique across all codebases (ULID)
- Can be referenced unambiguously forever
Benefits of Language-Level IDs
1. Agent Instructions Become Precise
Without IDs:
"Update the validate function in the user module to check email format"- Which validate function? There might be multiple
- What if it was renamed to
validateUser? - What if the file was moved?
With IDs:
"Update function f_01J5X7K9M2NPQRSTABWXYZ12 to check email format"- Unambiguous target
- Survives any refactoring
- Machine-verifiable reference
2. Merge Safety Across Branches
Traditional code suffers from merge conflicts when parallel branches modify the same entities. With ULIDs:
| Branch A | Branch B | Result |
|---|---|---|
Adds f_01ABC... | Adds f_01DEF... | Clean merge |
Modifies f_01ABC... | Renames f_01ABC... | Clean merge |
| Both add unnamed function | Conflict | N/A in Calor |
ULID's 80-bit random component makes collisions statistically impossible (~10^-24 probability per pair).
3. Round-Trip Stability
Code generation and conversion preserve identity:
// Original Calor
§F{f_01ABC:Add:pub}
§O{i32}
§R (+ a b)
§/F{f_01ABC}// Generated C# preserves ID
[CalorId("f_01ABC")]
public static int Add(int a, int b) => a + b;// Re-converted: Same ID
§F{f_01ABC:Add:pub}
§O{i32}
§R (+ a b)
§/F{f_01ABC}4. Traceable History
IDs enable tracking entities through their entire lifecycle:
f_01J5X7... created in commit a1b2c3 (Juan, 2024-01-15)
f_01J5X7... renamed Calculate→Compute in commit d4e5f6 (AI Agent, 2024-02-20)
f_01J5X7... moved to utils.calr in commit g7h8i9 (Maria, 2024-03-10)
f_01J5X7... implementation updated in commit j0k1l2 (AI Agent, 2024-04-05)The entity's history is continuous regardless of name or location changes.
The Challenges
Embedding IDs in code creates real challenges. Here's how Calor addresses each:
Challenge 1: ID Generation
Problem: Who generates IDs? When? How to ensure uniqueness?
Solution: ULID (Universally Unique Lexicographically Sortable Identifier)
- Timestamp + Random: 48-bit timestamp + 80-bit random = unique without coordination
- No central registry: Any machine can generate IDs independently
- Sortable: IDs sort chronologically by creation time
- CLI tooling:
calor ids assigngenerates IDs automatically
# Generate IDs for new declarations
calor ids assign .
# Preview what would be assigned
calor ids assign . --dry-runChallenge 2: ID Churn
Problem: Agents might accidentally change IDs during edits, breaking references.
Solution: Multi-layer protection
- Hook validation: Pre-write hooks detect ID modifications
- Agent rules: Skills explicitly forbid ID modification
- CI validation:
calor ids checkfails builds with ID issues
Challenge 3: Duplicate IDs
Problem: Copy-paste or extraction might create duplicate IDs.
Solution: Detection and automatic resolution
# Detect duplicates
calor ids check .
# Error Calor0803: Duplicate ID 'f_01ABC...'
# Fix duplicates (keep first, reassign others)
calor ids assign . --fix-duplicatesChallenge 4: Visual Noise
Problem: IDs add visual clutter to code.
Solution: IDs are for machines, not humans
- Agents are the primary audience: Calor optimizes for machine readability
- IDE support (roadmap): Future tooling can hide IDs in display
- Test IDs for docs: Short IDs (
f001) allowed in tests/docs/examples
// Production code: Full ULID
§F{f_01J5X7K9M2NPQRSTABWXYZ12:Calculate:pub}
// Documentation: Readable test ID
§F{f001:Calculate:pub}Challenge 5: New Developers
Problem: Developers unfamiliar with IDs might create invalid ones.
Solution: Omit IDs, let tooling assign them
// Developer writes (no ID):
§F{:NewFunction:pub}
§O{void}
§/F{}
// After `calor ids assign`:
§F{f_01NEW...:NewFunction:pub}
§O{void}
§/F{f_01NEW...}The tooling handles the complexity; developers just write code.
Why This Matters for AI-First Development
Traditional languages assume humans are the primary code authors. When AI agents become primary authors, different tradeoffs make sense.
| Human-First | AI-First |
|---|---|
| Names are primary identifiers | Names are documentation |
| Location matters | Identity is intrinsic |
| Minimize syntax | Maximize precision |
| Trust implicit conventions | Require explicit contracts |
Calor's stable identifiers are part of a broader shift toward explicit, machine-verifiable code that agents can reliably understand, reference, and modify.
ID Format
Production IDs use ULID with kind prefix:
| Kind | Prefix | Example |
|---|---|---|
| Module | m_ | m_01J5X7K9M2NPQRSTABWXYZ12 |
| Function | f_ | f_01J5X7K9M2NPQRSTABWXYZ12 |
| Class | c_ | c_01J5X7K9M2NPQRSTABWXYZ12 |
| Interface | i_ | i_01J5X7K9M2NPQRSTABWXYZ12 |
| Method | mt_ | mt_01J5X7K9M2NPQRSTABWXYZ12 |
| Property | p_ | p_01J5X7K9M2NPQRSTABWXYZ12 |
| Constructor | ctor_ | ctor_01J5X7K9M2NPQRSTABWXYZ12 |
| Enum | e_ | e_01J5X7K9M2NPQRSTABWXYZ12 |
Preservation Rules
| Operation | ID Behavior |
|---|---|
| Rename | Preserved |
| Move file | Preserved |
| Reformat | Preserved |
| Change implementation | Preserved |
| Extract helper | New ID for helper |
| Copy code | New ID required |
Next
- CLI Reference: ids - Command-line tools for ID management
- Design Principles - "Everything has an ID"
- Tradeoffs - What Calor gives up for explicitness