147 lines
4.9 KiB
Markdown
147 lines
4.9 KiB
Markdown
---
|
|
id: LSN-0005
|
|
ticket: legacy-runtime-learn-import
|
|
title: Historical Snapshot - Game Memcard Slots Surface and Semantics
|
|
created: 2026-03-27
|
|
tags: [migration, tech-debt]
|
|
---
|
|
|
|
# Historical Snapshot - Game Memcard Slots Surface and Semantics
|
|
|
|
Status: historical snapshot in `learn`, not normative.
|
|
Current normative anchors:
|
|
|
|
- [`../specs/08-save-memory-and-memcard.md`](../../specs/08-save-memory-and-memcard.md)
|
|
- [`../specs/16-host-abi-and-syscalls.md`](../../specs/16-host-abi-and-syscalls.md)
|
|
- [`../specs/16a-syscall-policies.md`](../../specs/16a-syscall-policies.md)
|
|
- [`mental-model-save-memory-and-memcard.md`](LSN-0015-mental-model-save-memory-and-memcard.md)
|
|
- [`mental-model-status-first-and-fault-thinking.md`](LSN-0016-mental-model-status-first-and-fault-thinking.md)
|
|
|
|
## Status
|
|
|
|
Accepted
|
|
|
|
## Context
|
|
|
|
Games need predictable, portable persistence for save/config/blob data without broad filesystem access.
|
|
|
|
Agenda `013-game-memcard-slots-surface-and-semantics.md` consolidated the product inputs:
|
|
|
|
- `game` uses `32` slots per game;
|
|
- maximum payload per slot is `32KB`;
|
|
- the game accesses only slots owned by its own `app_id`;
|
|
- persistence is explicit, with atomic `commit` per slot;
|
|
- save copy/export/import happens outside the game (Hub/OS).
|
|
|
|
This decision closes the v1 contract for the `game` profile while using decision `003` as the byte data plane.
|
|
|
|
## Decision
|
|
|
|
### 1. Base capacity and ownership contract
|
|
|
|
- each game has exactly `32` logical slots (`0..31`);
|
|
- each slot offers up to `32 * 1024` bytes of usable payload;
|
|
- userland access is restricted to the `app_id` that owns the loaded cart;
|
|
- there is no path-based access for games in v1.
|
|
|
|
### 2. Minimum memcard surface (status-first)
|
|
|
|
- `slot_count() -> (status:int, count:int)`
|
|
- `slot_stat(slot:int) -> (status:int, state:int, used_bytes:int, generation:int, checksum:int)`
|
|
- `slot_read(slot:int, buf:HeapRef<Bytes>, offset:int, max_bytes:int) -> (status:int, bytes_read:int)`
|
|
- `slot_write(slot:int, buf:HeapRef<Bytes>, offset:int, len:int) -> (status:int, bytes_written:int)`
|
|
- `slot_commit(slot:int) -> status:int`
|
|
- `slot_clear(slot:int) -> status:int`
|
|
|
|
Rules:
|
|
|
|
- `status` is always the first return value;
|
|
- byte operations use `HeapRef<Bytes>` plus an explicit window (`offset`, `max_bytes`/`len`);
|
|
- shape follows the policy in `16a`.
|
|
|
|
### 3. Runtime-owned digital envelope per slot
|
|
|
|
Each slot keeps canonical runtime-owned metadata:
|
|
|
|
- owning `app_id`;
|
|
- `slot_index`;
|
|
- `save_uuid`;
|
|
- `generation`;
|
|
- `payload_size`;
|
|
- `checksum`;
|
|
- `state` (`EMPTY`, `STAGED`, `COMMITTED`, `CORRUPT`).
|
|
|
|
`generation` and `checksum` are not manually controlled by userland:
|
|
|
|
- `generation` increments on each successful `slot_commit`;
|
|
- `checksum` is recalculated by the runtime on the persisted payload.
|
|
|
|
### 4. Visual envelope for Hub/OS
|
|
|
|
The slot exposes visual metadata for UX outside the game:
|
|
|
|
- `label`;
|
|
- `subtitle`;
|
|
- `updated_at` (or equivalent logical counter);
|
|
- `icon_ref` (static in v1, extensible to future animation).
|
|
|
|
### 5. Write policy and atomicity
|
|
|
|
- `slot_write` changes staging (it does not persist to the final destination);
|
|
- `slot_commit` applies per-slot atomic persistence;
|
|
- silent partial success is forbidden;
|
|
- on commit failure, it returns error `status` and preserves the atomicity invariant.
|
|
|
|
### 6. Fault-class boundary
|
|
|
|
`memcard(game)` follows `16a`:
|
|
|
|
- `Trap`: structural error (slot out of range, invalid/dead `HeapRef`, invalid window, invalid ABI shape);
|
|
- `status`: operational domain error;
|
|
- `Panic`: internal invariant break only.
|
|
|
|
Minimum domain status catalog:
|
|
|
|
- `OK`;
|
|
- `EMPTY`;
|
|
- `NOT_FOUND`;
|
|
- `NO_SPACE`;
|
|
- `ACCESS_DENIED`;
|
|
- `CORRUPT`;
|
|
- `CONFLICT`;
|
|
- `UNAVAILABLE`;
|
|
- `INVALID_STATE`.
|
|
|
|
### 7. Copy/export/import policy (outside the game)
|
|
|
|
- copy/export/import is a Hub/OS responsibility;
|
|
- the game does not receive an API to copy slots between apps;
|
|
- v1 validates ownership by `app_id` on import;
|
|
- conflict and corruption must be reflected through domain status and Hub UX.
|
|
|
|
### 8. Integration with host storage
|
|
|
|
- physical namespace isolated by `app_id`;
|
|
- physical per-slot representation is internal to the runtime/host;
|
|
- persistence should use an atomic strategy (temporary file + flush + platform-equivalent rename).
|
|
|
|
## Consequences
|
|
|
|
### Positive
|
|
|
|
- closes the `game` save contract without opening broad FS access;
|
|
- makes copy/export/import a system flow (Hub/OS), not a userland flow;
|
|
- standardizes slot integrity/versioning through `generation` + `checksum`.
|
|
|
|
### Costs
|
|
|
|
- requires per-slot staging/atomic commit implementation;
|
|
- requires explicit specification of the export/import format in Hub/OS;
|
|
- requires alignment with memcard and ABI specs.
|
|
|
|
## Required Follow-up
|
|
|
|
- agenda `013-game-memcard-slots-surface-and-semantics.md` should be considered closed and removed;
|
|
- `specs/08-save-memory-and-memcard.md` should absorb this contract (`32 x 32KB`, slots by `app_id`, atomic commit);
|
|
- `specs/16-host-abi-and-syscalls.md` and `specs/16a-syscall-policies.md` should absorb the surface and fault/status matrix.
|