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

Re-filing Is a Revision, Not a Rewrite

~10 min · append-only, revision, identity, history

Level 0Cold Hearth
0 XP0/34 lessons0/12 achievements
0/100 XP to next level100 XP to go0% complete
"Changing target_date appends a revision to the same crumb. It never tombstones and recreates."

Moving a Crumb Without Losing It

The one date you're allowed to change is target_date — you can re-file a crumb from one day to another, or move it to a different journal, or fix its text. The question is how. The lazy answer is to overwrite the field in place, or worse, delete the crumb and create a fresh one on the new date. Vesta does neither. Re-filing is an append-only revision: a CrumbRevision record is appended that preserves the prior value and the revision timestamp, while the crumb's ULID, captured_at, time zone, and media references stay exactly as they were. The crumb is the same crumb; only its assignment moved, and the move is now part of its history.

Change by appending, not by destroying. Every mutation that matters — re-file, journal move, text edit — is recorded as a new event that preserves what came before, never an in-place overwrite that erases it. The source log is never rewritten. This is the same append-only discipline that makes the whole store recoverable; a crumb's own history obeys it too.

Why Delete-and-Recreate Is Poison

Tombstoning a crumb and making a new one looks equivalent — same text, new date — but it quietly destroys three things. First, identity: the new crumb has a new ULID, so anything that pointed at the old one is now dangling. Second, provenance: a draft that cited the original crumb's ID in its source_crumb_ids suddenly references a crumb that no longer exists, and the paragraph's lineage breaks. Third, history: the record that this fragment was originally filed to a different day, then moved, is gone — you can no longer see that you changed your mind. Append-only revision preserves all three; delete-and-recreate throws all three away to save one row.

Identity Is the ULID, Not the Date

The deep reason this works is that a crumb's identity is its ULID, fixed at birth, not any of its mutable fields. Its date can change, its journal can change, its text can be corrected — and through all of it, it remains the same crumb because its ULID never moves. That's what lets a draft cite a crumb once and trust the citation forever, even after the crumb has been re-filed twice. Presentation (which day, which journal, what text) is fluid; identity is bedrock.

Code

Re-file as an appended revision (identity preserved)·json
// the crumb, born on 2026-03-14
{ "id": "01J8Z...", "target_date": "2026-03-14", "captured_at": "2026-07-24T09:15Z" }

// re-file to 2026-03-13 — append a revision, do NOT overwrite
{ "event": "revise", "crumb_id": "01J8Z...",
  "field": "target_date", "from": "2026-03-14", "to": "2026-03-13",
  "at": "2026-07-25T20:02Z" }

// live state folds to target_date = 2026-03-13, but:
//   - the ULID 01J8Z... is unchanged  -> every citation still resolves
//   - captured_at is unchanged          -> the write-truth survives
//   - the move itself is now history    -> you can see you changed your mind

External links

Exercise

Take an 'edit' operation in a system you know — moving a task between projects, changing a record's owner, correcting a typo. Ask whether it overwrites in place or appends a revision. Then find who points at that record by ID, and reason about what happens to those pointers if the edit were implemented as delete-and-recreate instead. Whose citations would rot?
Hint
The danger surfaces wherever something else references the record by a stable ID — a foreign key, a citation list, a cache. Overwrite-in-place keeps the ID and is usually fine; delete-and-recreate mints a new ID and orphans every reference to the old one.

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.