---
title: "Saves persistence player progress and data stores"
description: "Anything that must outlive the current match or session: score, inventory, unlocked levels, best time, checkpoint. Durable values live in the data warehouse, addressed by store, record, and field."
engineVersion: v1.0.234
date: 2026-09-28
license: "(c) Gessa, proprietary. Cite with attribution to https://gessa.ai/docs/. Terms: https://gessa.ai/terms/."
canonical: https://gessa.ai/docs/knowledge/playbooks/saves-and-persistence/
---

# Saves persistence player progress and data stores

## When to use

Anything that must outlive the current match or session: score, inventory,
unlocked levels, best time, checkpoint. Durable values live in the data
warehouse, addressed by store, record, and field.

## The recipe

1. Declare the store with `project_create_data_store`
   (`project.data_store.create`) so the game has a durable place for records.
2. Address a value with three coordinates used by every warehouse op: `storeKey`
   (defaults `progress`), `recordKey` (defaults `player`), and `field`.
3. Read into a script binding: `readTypedStateValue`
   `{ "scope":{"kind":"handler","handlerRef":<key>}, "storeKey":"progress",
   "recordKey":"player", "field":"coins", "valueType":"number",
   "assignTo":"coins", "defaultValue": 0 }`. `valueType` is a Script IR value
   type (`number`, `boolean`, `string`, `vector3`, `json`, ...).
4. Write a value: `writeTypedStateValue` with the same coordinates plus `value`.
   Increment a counter atomically: `incrementNumericState`
   `{ "field":"coins", "by": 1 }`. Flip a flag: `setBooleanState`
   `{ "field":"tutorialDone", "value": true }`.
5. From a GessaScript body (`replaceScriptFromGessaScript`) the full warehouse
   surface is available: `ctx.warehouse.get`, `set`, `update`, `increment`,
   `append`, `query`, `create`, `delete`.
6. Surface a saved value in the HUD with `wireHudStateBinding` (see
   ui-hud-panels-and-widgets); it reads the same store/record/field.

## Pitfalls

- `writeTypedStateValue`, `incrementNumericState`, and `setBooleanState` carry
  `writeState` + `durable` + `requiresAuthority`: they run server-authoritatively
  and persist. Do not treat them as client-local scratch.
- `readTypedStateValue` is `readState` + `durable`; give it a `defaultValue` so a
  missing record does not read as undefined.
- The `valueType` must match how the field is written elsewhere; reading a number
  field as `boolean` will not coerce.
- `persistence.v1` guards `persistence.prefetch_only` and
  `persistence.drain_audit` mean reads are prefetched and writes audited; keep
  per-tick warehouse traffic bounded.

## Verify

- `simulation_run` (`qa.run.start`) to confirm a written value reads back after a
  reload path.
- `project_get_graph_snapshot` to confirm the data store and the warehouse-op
  handlers exist.
