agentleFS
rodrigo-guerraarodrigo-guerraa/roblox-hub/skills/vendored/save-systems/SKILL.md

save-systems

Skill2 starsChanged 29 days agoMITView on GitHub

Engine-agnostic save-system design (what to persist, schema versioning, a migration chain, defensive loading, backups), adapted for Roblox DataStores with ProfileStore session locking. Use when designing or changing a Roblox player profile, adding, renaming or removing saved fields, bumping dataVersion, writing or testing migrations, deciding what to store vs recompute, handling saves from a newer server during a rolling update, or debugging lost or corrupted player data. It does not replace the roblox-lessons data.md facts, which win on any conflict.

5 other projects use a version of this.

Adopted by

Most recently taken up by

The file

We split this skill into the rules it gives an agent

4 rules. 0 are common, 2 are distinctive, and 2 are very specific to this project.

  • 0

    Common

    Most of the 5 edited versions we read keep these.

  • 2

    Distinctive

    This author's own choices. Worth a look.

  • 2

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    No Instances, Vector3, CFrame or Color3: store numbers and strings.

    line 19
  2. Distinctive:

    No tables mixing array and string keys, and no sparse arrays.

    line 20
  3. Distinctive:

    No NaN or infinity.

    line 21
  4. Very specific:

    Run Migrate(data, DATA_VERSION) on a deep copy.

    line 30

“Common” is measured: kept by at least 6 in 10 of the 5 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Save systems

Where this conflicts with the roblox-lessons skill, roblox-lessons wins. Read roblox-lessons references/data.md first.

A save is a serialized snapshot of authoritative state that outlives the code that wrote it. The hard parts are choosing what to save, never losing it to a bad write, and loading old saves after you ship an update.

On Roblox

  • No files: DataStores, through ProfileStore. Session locking stops two servers overwriting each other during fast server hops. ProfileStore auto-saves, saves on leave and on shutdown, and does the atomic read-modify-write for you. Don't hand-roll GetAsync/SetAsync.
  • One table per player (profile.Data), mutated only by the server through one service API. The client never writes saves; it only displays what the server sends.
  • Never rename the DataStore to change the format. A new name is a new, empty store, and everyone loses their progress. Migrate instead.
  • Studio uses its own store (NAME .. "_Studio"), so playtests never touch live players. Guard Studio conveniences with RunService:IsStudio().
  • Format limits: values must be JSON-encodable.
    • No Instances, Vector3, CFrame or Color3: store numbers and strings.
    • No tables mixing array and string keys, and no sparse arrays.
    • No NaN or infinity.
    • There is a per-key size limit of about 4 MB.
  • Backups: DataStores keep previous versions of a key for a limited time (ListVersionsAsync, GetVersionAsync), which is your rollback path. The Open Cloud API can read a live entry to confirm what was really saved.

Workflow

  1. Decide the authoritative state. Save data (ids, counts, levels, flags), never Instances or object references. Rebuild the world from data on load.
  2. Stamp dataVersion in the template from day one.
  3. Load pipeline:
    • Run Migrate(data, DATA_VERSION) on a deep copy. Steps[v] upgrades v to v+1; run each in pcall.
    • Replace the live data only if every step succeeds.
    • Then Reconcile() to fill new template fields.
    • Then validate (ids still exist in content, numbers sane).
  4. Outcomes:
    • current or migrated: load normally.
    • newer (an old server during a rolling update), missingStep or failed: leave the save untouched, end the session and kick with "please rejoin".
    • Reset only pre-launch dev saves below a declared baseline, never live ones.
  5. Bump DATA_VERSION together with Steps[old]. A spec should fail until every version from the baseline up has a step. Keep old steps forever.
  6. Store, don't derive. Save choices and progress, and recompute derived values (income totals, multipliers) on load, so a formula change in a patch can't desync saves.
  7. Verify:
    • Spec the migration chain against fixture saves from each shipped version.
    • Playtest with Studio API access on: save, rejoin, and confirm the state matches.
local function Migrate(data, target, steps)
	local v = data.dataVersion or 0
	if v > target then return "newer", data end
	if v == target then return "current", data end
	if v < BASELINE then return "reset", nil end
	local copy = deepCopy(data)
	while v < target do
		local step = steps[v]
		if not step then return "missingStep", data end
		if not pcall(step, copy) then return "failed", data end
		v += 1
		copy.dataVersion = v
	end
	return "migrated", copy
end

Pitfalls

  • Renaming the store, or wiping on a version mismatch.
  • Loading a newer save with older code, which silently drops unknown fields on the next write.
  • Migrations that mutate the live table and fail halfway.
  • Saving Roblox datatypes or Instances, which are dropped or rejected on write.
  • Mixed or sparse tables. Key by tostring(id).
  • Content removed in a patch (an item id) that crashes the load. Drop or substitute it during validation.
  • Trusting the client with any saved value.
  • Studio playtests writing to the live store.

References

  • references/versioning-and-migration.md: the version field, the migration chain and its rules, backups and rollback, format constraints, a load-time validation checklist, and store vs recompute.

Related: roblox-lessons (data.md, monetization.md for purchase-receipt dedupe stored in the save).


Adapted from awesome-gamedev-agent-skills (Apache-2.0). Modified; see SOURCE.md.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.