C.W.K.
Stream
Lesson 04 of 05 · published

Captured Is Not Owned

~9 min · identity, ulid, revision, append-only

Level 0Cold Iron
0 XP0/36 lessons0/12 achievements
0/100 XP to next level100 XP to go0% complete
"The date is where a crumb reads. The ULID is what a crumb is. Never confuse the label for the thing."

Two Dates That Never Merge

A crumb carries two dates that look similar and must never collapse into one. captured_at (with captured_local_date and timezone) is immutable capture truth — the true moment Dad wrote it. target_date is the owning date he assigns, the day the crumb belongs to for filing and reading. The previous lesson used this split; this one names why it is an invariant: the moment of writing and the day of belonging are different facts, and a design that stores only one of them will eventually lie about the other.

The ULID Is the Identity

Neither date is the crumb's identity. Every crumb has an immutable, device-generated ULID, and that is what it is; dates, filenames, and titles are presentation and sorting metadata layered on top. This matters the instant you need to change something. Because identity is the ULID and not the date, moving a crumb to a different day does not create a new crumb — it appends a revision to the same one. The thing keeps its name while its label changes.

Identity is the ULID; dates are metadata; a date change is an appended revision. Changing a crumb's owning date is never delete-and-recreate — that would mint a new identity and drop the history. It is an append-only revision of the same immutable crumb, so the trail of what changed and when stays intact.

Why Append-Only Revision, Not Edit-in-Place

Delete-and-recreate is the tempting shortcut for "I filed this on the wrong day," and it is exactly wrong here. It throws away the crumb's identity, its capture truth, and any interpretation already attached. An append-only revision keeps all three: same ULID, same captured_at, a new target_date with the old one still visible in the revision trail. The folded state shows the current date; the journal remembers every date it ever had. That discipline — change the projection, never erase the history — is the same one that governs the whole storage layer in Track 7.

Code

Moving a date is an appended revision, not a new crumb·python
# WRONG: delete-and-recreate mints a new identity, drops history
db.delete(crumb.id)
db.insert(new_crumb_with_fixed_date)   # new ULID, lost trail

# RIGHT: append a revision to the same immutable crumb
journal.append({
    "op": "crumb.revise",
    "id": "01J8Z...",            # SAME ULID - identity unchanged
    "target_date": "2026-07-21", # the corrected owning date
    "revision": crumb.revision + 1,
})
# captured_at is untouched. The old target_date stays in the
# journal. Folded state shows the new date; history remembers both.

External links

Exercise

You filed a symptom under today when it actually happened on Monday. In a naive app you would delete the entry and add a new one. In Forge, what happens instead — and name the three things that stay constant through the fix.
Hint
Forge appends a revision: the ULID, the captured_at, and any existing interpretation all stay constant; only target_date moves, with the old value preserved in the revision trail.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.