← ClaudeAtlas

savinglisted

Keeping a player's progress across a reload, a crash and a version bump. Use for saving a game, autosave, load, "my save is gone", "it forgot my progress", changing the shape of saved data, migrations, a reset or start-over button, or when a player on an old build might open a newer save.
C-Aniruddh/lattice · ★ 37 · AI & Automation · score 74
Install: claude install-skill C-Aniruddh/lattice
# Saving Three ideas, and every failure mode in this skill follows from one of them. - **The chain *is* the version.** `createStore` reads the current version off the migration chain's head, so declaring version 7 while shipping a chain that ends at 6 is not a bug you can write down. - **Every failure is a value.** `open()` never throws, for any content whatsoever. Seven ways a save fails to become a state, and every one degrades to a fresh game *with a report*. - **A save stores causes, not consequences.** Persist the player's brand hue, never the `#rrggbb` it derives to. The alternative to a chain — `parsed.version === SAVE_VERSION` with a fallback to `newGame()` — is not a migration policy. It is a delete. The game this kit was extracted from said so in its own source: *"a bump is not a migration — it is a deletion of every player's campus."* --- ## A save, its chain, and its autosave ```ts import { asEpochMillis } from '@latticekit/core'; import { browserStorage, createStore, migrations, scheduleFrom } from '@latticekit/persist'; import type { Recognize } from '@latticekit/persist'; import type { Loop } from '@latticekit/loop'; interface V1 { readonly version: 1; readonly coins: number } interface V2 { readonly version: 2; readonly wallet: { readonly coin: number }; readonly hue: number } // A recognizer returns the value TYPED, or throws naming the field. Never a boolean — // a boolean has already discarded the value that was wrong, so it cannot name it.