Lesson 212: Steam Cloud Save Slot Label Receipt After Branch Promotion (2026)
Direct answer: After you promote playtest → fest_public, file save_slot_label_receipt_v1.json and a per-surface save_slot_label_map on BUILD_RECEIPT so UI slot numbers, on-disk filenames, and Steam Cloud remote names stay aligned for each build_label. This is the first H1 2026 player trust milestone—continuing from Lesson 211 Q4 capstone (scope/URL isolation) into save integrity before telemetry and crash correlation lessons.

Why this matters now (July 2026 branch + Cloud week)
July 2026 micro-studios merge playtest depots into fest_public the same week many teams enable Steam Cloud mid-cycle. Two failures stack:
- Slot label drift — Menu still says Slot 1 while code writes
save_slot_bafter a refactor (save-slot case study). - Cloud remote wins — Stale playtest remote overwrites newer fest local (Steam Cloud overwrite help).
Wednesday smoke catches boot paths; it rarely runs a three-slot save → quit → load matrix on installed Steam builds. Playtest isolation separates depots—this lesson adds label maps per surface so isolation receipts from Lesson 211 do not green-light wrong persistence keys. For Godot path roots per branch, pair playtest save isolation preflight and queued Lesson 238.
Beginner path (40-minute first pass)
| Step | Action | Success check |
|---|---|---|
| 1 | List UI labels vs disk keys for each slot | No orphan UI label |
| 2 | Export save_slot_label_map per build_label |
JSON committed |
| 3 | Run save/load on installed fest build | Slot 2 round-trip OK |
| 4 | Match Steamworks Cloud filenames to map | Names identical |
| 5 | File save_slot_label_receipt_v1.json |
All S1–S6 pass |
| 6 | Add BUILD_RECEIPT columns | Row visible in Thursday review |
Time: ~40 minutes with an existing three-slot menu; 55 minutes first Cloud enable + promotion week.
Developer path (gates S1–S6)
| Gate | Check | Fail when |
|---|---|---|
| S1 | save_slot_label_map exists per surface |
Missing playtest or fest_public row |
| S2 | UI label → persistence key bijection | Two UI labels → one key |
| S3 | build_label bump updates map hash |
Map stale after merge |
| S4 | Installed Steam round-trip matrix | Any slot loads empty/wrong chapter |
| S5 | Steam Cloud file list matches map | Remote name ≠ local basename |
| S6 | save_slot_label_receipt_v1.json |
promotion_allowed: false |
Per-surface save_slot_label_map
Maintain one map file per surface (not one global file that silently mixes playtest and fest):
{
"schema": "save_slot_label_map_v1",
"build_label": "fest-demo-2026-10-rc1",
"surface": "fest_public",
"slots": [
{ "ui_label": "Slot 1", "persistence_key": "save_slot_a", "cloud_filename": "save_slot_a.sav" },
{ "ui_label": "Slot 2", "persistence_key": "save_slot_b", "cloud_filename": "save_slot_b.sav" },
{ "ui_label": "Slot 3", "persistence_key": "quicksave", "cloud_filename": "quicksave.sav" }
],
"map_hash": "sha256:…"
}
Pin playtest separately:
{
"schema": "save_slot_label_map_v1",
"build_label": "playtest-2026-07-rc1",
"surface": "playtest_invite",
"slots": [
{ "ui_label": "Slot 1", "persistence_key": "pt_slot_1", "cloud_filename": "pt_slot_1.sav" }
]
}
Rule: Never reuse fest_public persistence keys on playtest surfaces—even if filenames look similar.
Promotion-day round-trip matrix (S4)
Run on two Windows accounts when Cloud is enabled:
| Slot | Save action | Quit | Relaunch | Expected |
|---|---|---|---|---|
| 1 | New game → floor 3 | Yes | Load Slot 1 | Floor 3 |
| 2 | New game → floor 7 | Yes | Load Slot 2 | Floor 7 |
| 3 | Quick save at boss | Yes | Load Slot 3 | Boss state |
Record screen capture + log line save_label=Slot 2 key=save_slot_b build_label=….
Steam Cloud crosswalk (S5)
| Steamworks field | Must equal |
|---|---|
| Cloud filename | cloud_filename in map |
save_schema_version |
Bumped when key renames |
| Conflict policy | Documented in FAQ |
If Cloud was enabled mid-cycle, follow Cloud overwrite help before S6—label maps alone do not fix stale remotes.
save_slot_label_receipt_v1.json
{
"schema": "save_slot_label_receipt_v1",
"build_label": "fest-demo-2026-10-rc1",
"surfaces": {
"playtest_invite": {
"map_path": "release-evidence/saves/PLAYTEST_SAVE_SLOT_LABEL_MAP.json",
"round_trip_pass": true
},
"fest_public": {
"map_path": "release-evidence/saves/FEST_SAVE_SLOT_LABEL_MAP.json",
"round_trip_pass": true
}
},
"steam_cloud": {
"enabled": true,
"file_list_matches_map": true,
"schema_version": 2,
"conflict_policy": "local_first_on_schema_bump"
},
"paired_receipts": {
"playtest_surface_isolation": "release-evidence/playtest/PLAYTEST_SURFACE_ISOLATION_RECEIPT.json",
"playtest_isolation": "release-evidence/playtest/PLAYTEST_ISOLATION_RECEIPT.json"
},
"gates": {
"S1_maps_per_surface": "pass",
"S2_bijection": "pass",
"S3_map_hash_after_promotion": "pass",
"S4_installed_round_trip": "pass",
"S5_cloud_filename_parity": "pass",
"S6_receipt": "pass"
},
"promotion_allowed": true
}
Pin under release-evidence/saves/SAVE_SLOT_LABEL_RECEIPT.json.
BUILD_RECEIPT columns (H1 arc start)
| Column | Source |
|---|---|
save_slot_label_receipt |
This lesson |
save_slot_label_map_fest |
Fest map path + hash |
save_slot_label_map_playtest |
Playtest map path + hash |
steam_cloud_sync_receipt |
Optional; required if Cloud on |
build_label |
VERSION spine |
playtest_surface_isolation_receipt |
Lesson 211 when playtest active |
Thursday row review — add Saves section: map hash + round-trip pass/fail.
Engine notes (same symptom, different keys)
| Engine | Persistence lane | Map field |
|---|---|---|
| Godot | user://save_slot_a.save |
persistence_key = basename |
| Unity | Application.persistentDataPath |
Full relative path in map |
| GameMaker | ini section + filename |
Section name in map |
| GDevelop | Storage key string |
Exact key in map |
| Ren'Py | persistent fields |
Field name in map |
Cross-link engine helps when round-trip fails after map looks correct: GameMaker save path, GDevelop path, Construct NW.js.
Proof table (promotion week)
| Surface | build_label | Slot 2 round-trip | Cloud list match | Promote? |
|---|---|---|---|---|
| playtest_invite | playtest-2026-07-rc1 | pass | n/a (local-only OK) | soak |
| fest_public | fest-demo-2026-10-rc1 | pass | pass | after S6 |
Key takeaways
- Branch promotion without a map diff recreates “Slot 1 empty” threads—not gameplay bugs.
- One map per surface—playtest keys must not alias fest keys on the same App ID.
- Installed Steam matrix beats editor-only QA for save trust.
- Cloud filenames are part of the label map—not an afterthought.
save_slot_label_receipt_v1.jsonis the H1 arc entry receipt—telemetry and crashes build on this row next.- Pair narrative recovery with save-slot case study—this lesson is the course implementation.
- 20-free save/cloud resource bookmarks tools; receipts prove you used them.
- Lesson 211 surface isolation does not replace slot maps—run both before fest traffic.
Prerequisites
- Lesson 211 — Q4 capstone
- Playtest isolation playbook
- Save-slot label case study
- BUILD_RECEIPT beginner pipeline
- Wednesday demo smoke
Common mistakes
- Updating UI strings without persistence key migration.
- One global map mixing playtest + fest rows.
- Testing saves only in editor dist folder.
- Enabling Cloud without Steamworks file list update.
- Skipping map hash bump on
build_labelpromotion.
Troubleshooting
| Symptom | Lane |
|---|---|
| Slot 1 empty after patch | S2 bijection + case study |
| Progress reset after Cloud on | Cloud overwrite help |
| Wrong chapter on fest only | Depot label mismatch help |
| Playtest save on fest URL | Lesson 211 surface receipt |
Mini exercise (55 minutes)
- Intentionally rename Slot 2 UI label without key change—confirm S2 fails.
- Fix map; pass round-trip on installed build.
- Add Cloud filename column; run two-account sync.
- File receipt JSON; add BUILD_RECEIPT row.
- Link receipt path in Thursday review template.
Continuity — H1 2026 arc (212–217)
| Lesson | Receipt focus |
|---|---|
| 212 (this) | Save slot labels + Cloud crosswalk |
| 213 | Privacy-safe telemetry session |
| 214 | Crash symbolicate + build_label |
| 215 | Refund signal dashboard |
| 216 | AI dialogue patch moderation |
| 217 | H1 capstone |
Prior arcs complete: July H2 200–205, Q4 206–211.
Next: Lesson 213 — privacy-safe telemetry session receipt.
FAQ
Does this replace save_slot_recovery_receipt from the case study?
No—recovery receipt documents post-incident proof. This lesson’s receipt is pre-promotion gate for the next merge.
Local-only fest demo?
Set steam_cloud.enabled: false in receipt; S5 = n/a but S4 matrix still required.
Same App ID for playtest and fest?
Yes—maps must differ by surface and persistence_key, not by hoping players use different machines.
Unity Addressables saves?
Map the logical slot to the serialized file or PlayerPrefs key actually written—document in persistence_key.
Pin slot labels on BUILD_RECEIPT before the next branch promotion—players forgive rough combat; they rarely forgive “lost” saves.