Determinism & replay

Arcade score

arcade-score · an adoption of State persistence scope, version 2

Tests included

Use in your package

The first button opens the authoring tool with these answers and numbers already filled in. You still add the rules and tuning of your own game.

What is in the zip file

The zip file holds the adoption and its acceptance tests. In the authoring tool, choose Add contract and pick this zip file. If you edit your package outside the authoring tool, unpack the zip file in your package folder. The files of the zip file go into contracts/.

The contract that this adoption uses

This contract covers which state is kept after the end of a run or session, a quit, a crash, death, the deletion of saved data, and a move to another device. It also covers local saves, manual slots, cloud copies, server profiles, backups, conflicts, and version changes. This contract does not cover time that passes while the game is closed, replay guarantees, file formats, or the shape of saved numbers. The Suspension and catch-up contract covers time while the game is closed. The Replay scope contract covers replay guarantees.

Read the full State persistence scope contract →

Questions

The supplied answer is marked on each question. Pick other answers to see what changes. Nothing is saved here: the zip file and the authoring tool use the supplied answers.

By default, what happens when saved state cannot be read?

Asked when
the copy kinds list has at least one row.
If not asked
No saved copy can be opened later, so unreadable saved state never reaches a load attempt.
Choices for By default, what happens when saved state cannot be read?
The unreadable copy stays closed. A city slot remains listed, but none of its progress becomes active.

Loading stops before any state from that copy becomes active. The copy is neither replaced nor repaired by this response.

Play starts from the normal starting state. A damaged puzzle slot opens with its first board instead of the old board.

The starting state becomes active instead of state from the unreadable copy. The response does not by itself delete or overwrite that copy.

An earlier copy can replace the unreadable copy through a route that the game supports. Restoring a file by hand is one such route. A factory game continues after the player chooses the backup that was made before the damaged autosave.

A declared manual, in-game, or automatic recovery route can activate an earlier copy. The newest usable earlier copy under the matching backup rule becomes active.

Readable progress returns and damaged parts reset. A dungeon keeps cleared floors but rebuilds one broken room record.

Readable state becomes active as one repaired result. Every unreadable part takes the starting value named by the cited repair rule.

The player can cancel or try the unreadable copy. A colony save opens only after its damage warning is accepted.

No state from the copy becomes active before the explicit choice. Accepting the warning permits an attempt but does not promise success.

Why this is asked

Lost progress is the most visible failure of a save system. This answer is the default answer. A save kind can choose another answer in its own row.

By default, what happens when an older game build wrote the copy?

Asked when
the copy kinds list has at least one row.
If not asked
No loadable copy exists, so an older build has no saved state to open.
Choices for By default, what happens when an older game build wrote the copy?
The older copy stays closed. A world from last year's rules cannot open in the current build.

Loading stops before state written by the older build becomes active.

The game updates the old state and opens it. A factory save gains the current recipe records before play resumes.

A declared conversion produces current-build state before activation.

The old copy is tried only after a warning. A racing career from an earlier patch opens after the player accepts the risk.

The player accepts a compatibility warning before old-build state becomes active. Acceptance permits an attempt but does not promise success.

Why this is asked

An update can make a player's progress impossible to load, or change it permanently. Choose how the current build protects that progress.

By default, what happens when a newer game build wrote the copy?

Asked when
the copy kinds list has at least one row.
If not asked
No loadable copy exists, so a newer build has no saved state to open.
Choices for By default, what happens when a newer game build wrote the copy?
The newer copy stays closed. A world from a later patch remains unavailable in this build.

Loading stops before state written by the newer build becomes active.

The newer copy is tried only after a warning. A sandbox world opens after the player accepts that newer content may be lost.

The player accepts a compatibility warning before newer-build state becomes active. Acceptance permits an attempt but does not promise success.

Why this is asked

Going back to an older build can erase newer content. Choose whether the player may take that risk.

Numbersno numbers

This contract has no numbers to set.

Rulesno rules

A rule is a check between the numbers. Validation reports a rule that fails.

This contract has no rules between its numbers.

Lists5 lists

Each list holds the rows this adoption supplies. A list can be empty.

State families

state-families

IdState declared inLifetimeScope starts inScope ends inLifetime event declared inSaved inAfter normal quitAfter crashAfter deathAfter slot deleteAfter local profile deleteOn another device
match-scoremechanics.match-scorerunmatch.startmatch.result-or-exit——does-not-returndoes-not-returnclears-valueno-slotno-profiledoes-not-return

Copy kinds

copy-kinds

This adoption declares no copy kinds.

Backup copies

backup-copies

This adoption declares no backup copies.

Copy conflicts

copy-conflicts

This adoption declares no copy conflicts.

Earlier point returns

earlier-point-returns

This adoption declares no earlier point returns.

Test inputsscope and seeds

Some tests need a scope or seeds from the adoption before they can run.

This adoption supplies no test inputs. Every test uses its default inputs.

Acceptance tests17 tests apply

The contract comes with 85 tests. A test that runs once per row is counted once for each row. Tests that do not apply to these answers are still listed, with the reason.

run-scope-uses-cited-events · match-score

match-score begins and ends its run only at the cited events

scenarioper state-families row

Applies to the match-score row.

The run for match-score begins at match.start and ends at match.result-or-exit. Each save, automatic write, timer tick, load, close, or relaunch the adoption can construct does not create either boundary. For an event that the adoption cannot construct, this test checks nothing. The test names the addresses and restates nothing from them.

Test steps and diagnostics
Given

a distinguishable match-score value before, during, and after its run

When
  • play crosses the attempt start at match.start, the run ending at match.result-or-exit, and each save, automatic write, timer tick, load, process close, and relaunch to the same context that the adoption can construct
Then
  • the run identity begins only at match.start and ends only at match.result-or-exit
  • each constructed save, automatic write, timer tick, load, close, and relaunch does not by itself begin or end the run; for an event that the adoption cannot construct, this test checks nothing
Diagnostics
  • arcade-score-state-boundary-trace
  • arcade-score-run-identity

session-scope-uses-cited-events

Row.id keeps one session through returns to the same context

scenarioper state-families row

Does not apply to arcade-score: no row in state-families matches lifetime session.

copy-carries-complete-identity

Row.id copies carry their complete identity and their complete state-family set

scenarioper copy-kinds row

Does not apply to arcade-score: copy-kinds has no rows.

conflict-has-two-unreplaced-accepted-copies

Row.id is a conflict only for two accepted unreplaced copies

scenarioper copy-conflicts row

Does not apply to arcade-score: copy-conflicts has no rows.

state-family-follows-maximum-lifetime · match-score

match-score follows its declared maximum lifetime

scenarioper state-families row

Applies to the match-score row.

match-score has a maximum lifetime of one run. Each boundary before the end of that lifetime follows the row's result, and the trace distinguishes a restored value from an equal value derived later. The family definition is at mechanics.match-score; the test names the address and restates nothing from it.

Test steps and diagnostics
Given

match-score changes from an earlier value to a distinguishable value

When
  • the value is read immediately before and after every declared persistence boundary that arcade-score can construct
Then
  • the earlier value affects play for no longer than one run
  • a boundary before the end of that lifetime clears it exactly when match-score's selected boundary result says so
  • the trace identifies restoration of the earlier value separately from a later derivation of an equal value
Diagnostics
  • arcade-score-state-boundary-trace
  • arcade-score-active-state-source

until-event-value-ends-at-cited-event

Row.id ends at its cited event

scenarioper state-families row

Does not apply to arcade-score: no row in state-families matches lifetime until event.

not-kept-value-never-returns

Row.id never returns after its named interruption

scenarioper state-families row

Does not apply to arcade-score: no row in state-families matches lifetime not kept.

slot-and-profile-end-at-cited-event

Row.id ends at its cited Row.lifetime boundary

scenarioper state-families row

Does not apply to arcade-score: no row in state-families matches lifetime slot or profile.

commit-starts-only-at-selected-trigger

Row.id starts a commit only at its selected trigger

scenarioper copy-kinds row

Does not apply to arcade-score: copy-kinds has no rows.

commit-loadable-only-after-complete-success

Row.id replaces its last commit only after complete success

scenarioper copy-kinds row

Does not apply to arcade-score: copy-kinds has no rows.

normal-quit-final-commit-follows-write-rule

Row.id makes a final quit commit only when its rule requires one

scenarioper copy-kinds row

Does not apply to arcade-score: copy-kinds has no rows.

normal-quit-returns-last-commit

Row.id returns its last commit after a normal quit

scenarioper state-families row

Does not apply to arcade-score: no row in state-families matches after-normal-quit returns last commit.

normal-quit-does-not-return · match-score

match-score does not return after a normal quit

scenarioper state-families row

Applies to the match-score row.

After a normal quit, the earlier match-score value does not return. Its post-quit value follows mechanics.match-score, and the record distinguishes that result from restoration. The test names the address and restates nothing from it.

Test steps and diagnostics
Given

a distinguishable match-score value before a normal quit

When
  • the game quits normally and returns through its normal flow
Then
  • the earlier value does not become active
  • match-score starts from the post-quit value governed at mechanics.match-score
  • the source record distinguishes that value from restoration of the earlier value
Diagnostics
  • arcade-score-state-boundary-trace
  • arcade-score-active-state-source

crash-returns-last-commit

Row.id returns its last completed commit after a crash

scenarioper state-families row

Does not apply to arcade-score: no row in state-families matches after-crash returns last commit.

crash-does-not-return · match-score

match-score does not return after a crash

scenarioper state-families row

Applies to the match-score row.

After a crash, the earlier match-score value does not return. Any unfinished commit fails; if none can exist, this test checks nothing about an unfinished commit. Its post-crash value follows mechanics.match-score. The test names the address and restates nothing from it.

Test steps and diagnostics
Given

a distinguishable match-score value and, where the adoption can construct one, an unfinished commit before a crash

When
  • the game crashes and returns through its normal flow
Then
  • the earlier value does not become active; where an unfinished commit exists, it does not succeed, and where none can exist, this test checks nothing about an unfinished commit
  • match-score starts from the post-crash value governed at mechanics.match-score
  • the source record distinguishes that value from restoration
Diagnostics
  • arcade-score-state-boundary-trace
  • arcade-score-active-state-source

timer-and-checkpoint-have-crash-loss-window

Row.id reports its possible crash loss window

scenarioper copy-kinds row

Does not apply to arcade-score: copy-kinds has no rows.

layout-selects-requested-copy

Row.id selects its requested copy before activation

scenarioper copy-kinds row

Does not apply to arcade-score: copy-kinds has no rows.

residence-controls-authoritative-candidates

Row.id obtains candidates from its declared residence

scenarioper copy-kinds row

Does not apply to arcade-score: copy-kinds has no rows.

candidate-identity-precedes-settlement

Row.id reads candidate identity before settlement

scenarioper copy-kinds row

Does not apply to arcade-score: copy-kinds has no rows.

missing-conflict-row-runs-no-conflict-response

Row.id has no conflict response without a matching row

scenarioper copy-kinds row

Does not apply to arcade-score: copy-kinds has no rows.

one-kind-conflict-settles-only-named-kind

Row.id settles only Row.copy kind

scenarioper copy-conflicts row

Does not apply to arcade-score: copy-conflicts has no rows.

all-synced-conflict-settles-whole-set

Row.id settles every synced save kind together

scenarioper copy-conflicts row

Does not apply to arcade-score: copy-conflicts has no rows.

conflict-select-by-rule

Row.id applies select-by-rule

scenarioper copy-conflicts row

Does not apply to arcade-score: copy-conflicts has no rows.

conflict-player-chooses-copy

Row.id applies player-chooses-copy

scenarioper copy-conflicts row

Does not apply to arcade-score: copy-conflicts has no rows.

conflict-refuse-until-resolved

Row.id applies refuse-until-resolved

scenarioper copy-conflicts row

Does not apply to arcade-score: copy-conflicts has no rows.

older-build-override-refuse-load

Row.id applies its older-build refuse-load override

scenarioper copy-kinds row

Does not apply to arcade-score: copy-kinds has no rows.

older-build-default-refuse-load

Row.id applies the default answer refuse-load for an older build

scenarioper copy-kinds row

Does not apply to arcade-score: older build default is not asked for arcade-score.

older-build-override-convert-and-load

Row.id applies its older-build convert-and-load override

scenarioper copy-kinds row

Does not apply to arcade-score: copy-kinds has no rows.

older-build-default-convert-and-load

Row.id applies the default answer convert-and-load for an older build

scenarioper copy-kinds row

Does not apply to arcade-score: older build default is not asked for arcade-score.

older-build-override-warn-and-load

Row.id applies its older-build warn-and-load override

scenarioper copy-kinds row

Does not apply to arcade-score: copy-kinds has no rows.

older-build-default-warn-and-load

Row.id applies the default answer warn-and-load for an older build

scenarioper copy-kinds row

Does not apply to arcade-score: older build default is not asked for arcade-score.

newer-build-override-refuse-load

Row.id applies its newer-build refuse-load override

scenarioper copy-kinds row

Does not apply to arcade-score: copy-kinds has no rows.

newer-build-default-refuse-load

Row.id applies the default answer refuse-load for a newer build

scenarioper copy-kinds row

Does not apply to arcade-score: newer build default is not asked for arcade-score.

newer-build-override-warn-and-load

Row.id applies its newer-build warn-and-load override

scenarioper copy-kinds row

Does not apply to arcade-score: copy-kinds has no rows.

newer-build-default-warn-and-load

Row.id applies the default answer warn-and-load for a newer build

scenarioper copy-kinds row

Does not apply to arcade-score: newer build default is not asked for arcade-score.

unreadable-copy-override-refuse-load

Row.id applies its unreadable-copy refuse-load override

scenarioper copy-kinds row

Does not apply to arcade-score: copy-kinds has no rows.

unreadable-copy-default-refuse-load

Row.id applies the default answer refuse-load for an unreadable copy

scenarioper copy-kinds row

Does not apply to arcade-score: unreadable copy default is not asked for arcade-score.

unreadable-copy-override-start-fresh

Row.id applies its unreadable-copy start-fresh override

scenarioper copy-kinds row

Does not apply to arcade-score: copy-kinds has no rows.

unreadable-copy-default-start-fresh

Row.id applies the default answer start-fresh for an unreadable copy

scenarioper copy-kinds row

Does not apply to arcade-score: unreadable copy default is not asked for arcade-score.

unreadable-copy-override-restore-earlier-copy

Row.id applies its unreadable-copy restore-earlier-copy override

scenarioper copy-kinds row

Does not apply to arcade-score: copy-kinds has no rows.

unreadable-copy-default-restore-earlier-copy

Row.id applies the default answer restore-earlier-copy for an unreadable copy

scenarioper copy-kinds row

Does not apply to arcade-score: unreadable copy default is not asked for arcade-score.

unreadable-copy-override-repair-readable-state

Row.id applies its unreadable-copy repair-readable-state override

scenarioper copy-kinds row

Does not apply to arcade-score: copy-kinds has no rows.

unreadable-copy-default-repair-readable-state

Row.id applies the default answer repair-readable-state for an unreadable copy

scenarioper copy-kinds row

Does not apply to arcade-score: unreadable copy default is not asked for arcade-score.

unreadable-copy-override-warn-and-try

Row.id applies its unreadable-copy warn-and-try override

scenarioper copy-kinds row

Does not apply to arcade-score: copy-kinds has no rows.

unreadable-copy-default-warn-and-try

Row.id applies the default answer warn-and-try for an unreadable copy

scenarioper copy-kinds row

Does not apply to arcade-score: unreadable copy default is not asked for arcade-score.

backup-retains-orders-and-restores-earlier-copies

Row.id retains and restores earlier copies

scenarioper backup-copies row

Does not apply to arcade-score: backup-copies has no rows.

readable-compatible-copy-activates-together

Row.id loads a readable compatible conflict-free copy

scenarioper copy-kinds row

Does not apply to arcade-score: copy-kinds has no rows.

load-preserves-families-from-other-kinds

loading Row.id preserves state from other save kinds

scenarioper copy-kinds row

Does not apply to arcade-score: copy-kinds has no rows.

family-without-saved-in-is-never-restored · match-score

match-score is never restored when it names no save kind

scenarioper state-families row

Applies to the match-score row.

For match-score, when it has no saved-in, no return path restores its earlier value. For match-score, when it names saved-in, only an accepted load of that save kind restores its earlier value; no other return path does.

Test steps and diagnostics
Given

a distinguishable earlier match-score value and every return path the adoption can construct

When
  • the return paths complete
Then
  • where match-score has no saved-in, none of the return paths restores its earlier value
  • where match-score names saved-in, its earlier value is restored only by an accepted load of that save kind and by no other return path
Diagnostics
  • arcade-score-state-boundary-trace
  • arcade-score-active-state-source

earlier-point-follows-after-load

Row.id follows its after-load result

scenarioper earlier-point-returns row

Does not apply to arcade-score: earlier-point-returns has no rows.

missing-earlier-point-row-returns-nowhere

Row.id returns to no earlier point without an earlier-point row

scenarioper copy-kinds row

Does not apply to arcade-score: copy-kinds has no rows.

earlier-point-mode-gate-controls-existence

Row.id exists only in its named modes

scenarioper earlier-point-returns row

Does not apply to arcade-score: earlier-point-returns has no rows.

death-follows-family-result · match-score

match-score follows its death result

scenarioper state-families row

Applies to the match-score row.

On death, match-score follows its selected result: the earlier value is removed before later play and an equal later value is recorded only as new state.

Test steps and diagnostics
Given

a distinguishable match-score value immediately before death

When
  • death settles before any death-triggered copy removal the adoption declares
Then
  • the earlier value is removed before later play and an equal later value is recorded only as new state
Diagnostics
  • arcade-score-state-boundary-trace
  • arcade-score-active-state-source

death-at-run-end-clears-run-family · match-score

match-score clears when death is its run end

scenarioper state-families row

Applies to the match-score row.

If death is the run end at match.result-or-exit, match-score clears even without a save action. The test names the address and restates nothing from it.

Test steps and diagnostics
Given

a distinguishable match-score value before a death, where the run ending at match.result-or-exit is death

When
  • death occurs without another save action
Then
  • if the cited end at match.result-or-exit is death, the earlier run-lifetime value clears before later play even without a save action; if the cited end is not death, this test checks nothing
Diagnostics
  • arcade-score-state-boundary-trace
  • arcade-score-run-identity

death-at-until-event-clears-family

Row.id clears when death is its cited event

scenarioper state-families row

Does not apply to arcade-score: no row in state-families matches lifetime until event.

slot-deletion-follows-family-result · match-score

match-score follows its slot-deletion result

scenarioper state-families row

Applies to the match-score row.

On slot deletion, match-score follows its selected result: the settlement records that this family has no related player-selected slot.

Test steps and diagnostics
Given

a distinguishable family value when it has no related player-selected slot

When
  • a slot-deletion boundary is considered
Then
  • the settlement records that this family has no related player-selected slot
Diagnostics
  • arcade-score-state-boundary-trace
  • arcade-score-settlement-report

local-profile-deletion-follows-family-result · match-score

match-score follows its local-profile-deletion result

scenarioper state-families row

Applies to the match-score row.

On local-profile deletion, match-score follows its selected result: the settlement records that this family has no related local player profile. Where a related profile exists, child slots are included only as the adoption's cited rule provides. Where no related profile exists, this test checks nothing about child slots. The test restates nothing from the cited rule.

Test steps and diagnostics
Given

a distinguishable family value when it has no related local player profile

When
  • a local-profile-deletion boundary is considered
Then
  • the settlement records that this family has no related local player profile
Diagnostics
  • arcade-score-state-boundary-trace
  • arcade-score-settlement-report
  • arcade-score-removal-log

device-lifetime-ends-at-device-data-deletion

Row.id ends when device data is deleted

scenarioper state-families row

Does not apply to arcade-score: no row in state-families matches lifetime device.

account-lifetime-ends-at-account-deletion

Row.id ends when its account is deleted or reset

scenarioper state-families row

Does not apply to arcade-score: no row in state-families matches lifetime account.

another-device-returns-family

Row.id returns on another eligible device

scenarioper state-families row

Does not apply to arcade-score: no row in state-families matches on-another-device returns.

another-device-does-not-return-family · match-score

match-score does not return on another device

scenarioper state-families row

Applies to the match-score row.

On another eligible device, the earlier match-score value does not return. Its new-device value follows mechanics.match-score. The device change does not delete any source-device copy; if none exists, this test checks nothing about a source-device copy. The test names the address and restates nothing from it.

Test steps and diagnostics
Given

a distinguishable match-score value on one device before the same eligible player continues on another

When
  • the new device settles available state
Then
  • the earlier value does not activate and the family starts from the new-device value governed at mechanics.match-score
  • any source-device copy is not deleted by the device change; where none exists, this test checks nothing about a source-device copy
Diagnostics
  • arcade-score-state-boundary-trace
  • arcade-score-active-state-source
  • arcade-score-removal-log

removed-copy-disappears-at-selected-event

Row.id disappears at its removal event

scenarioper copy-kinds row

Does not apply to arcade-score: copy-kinds has no rows.

copy-kind-mode-gate-controls-existence

Row.id exists only in its named modes

scenarioper copy-kinds row

Does not apply to arcade-score: copy-kinds has no rows.

cleared-or-ended-value-never-affects-later-play

ended and cleared values stay absent

scenarioonce

Applies to arcade-score.

After a lifetime ends or a boundary result clears a value, that earlier value never affects later play. An equal value derived later is new state. If no such case can be constructed, this test checks nothing.

Test steps and diagnostics
Given

every event the arcade-score adoption can construct where a lifetime ends or a boundary result clears an earlier value

When
  • later play reaches points where an equal value can be derived
Then
  • the earlier value never affects later play after the end or clearing event
  • any equal value derived later is recorded as new state rather than restoration
  • where the adoption can construct no such event, this test checks nothing
Diagnostics
  • arcade-score-state-boundary-trace
  • arcade-score-active-state-source

settlement-report-is-complete

every settlement report records its complete result

scenarioonce

Applies to arcade-score.

Every settlement report carries the mechanism's full identity, boundary, commit, selection, response, result, and loss fields. Conflict and damage reports add their required facts and never claim an unattempted success. For a case that the adoption cannot construct, this test checks nothing.

Test steps and diagnostics
Given

each persistence settlement the arcade-score adoption can construct

When
  • the settlement completes or refuses
Then
  • the report identifies state-family id, save-kind id or its absence, event, scope before and after, last successful commit when read, selected copy and build when loaded, override or default answer used, state result, and any lost interval
  • a conflict report also identifies both candidate commits and the selected response
  • a damage report identifies refusal, starting state, earlier-copy recovery, repaired parts, or warning choice and never claims an unattempted load succeeded
  • where a conflict, damage case, load, or answered question does not arise, this test checks nothing about it
Diagnostics
  • arcade-score-settlement-report
  • arcade-score-conflict-report
  • arcade-score-damage-report

state-family-citation-states-its-duty · match-score

declaration check: match-score's state citation states its duty

scenarioper state-families row

Applies to the match-score row.

This declaration check reads the adoption and cited prose, not the running game. mechanics.match-score must name every field in match-score and every point where it can affect later play. For a family that answers does-not-return after a quit, a crash, or on another device, it must also name the value the family starts from; otherwise this test checks nothing about a starting value. When the cited prose has another meaning, the adoption is invalid, and this test fails. The test names the address and restates nothing from it.

Test steps and diagnostics
Given

the arcade-score adoption and the prose at mechanics.match-score, not the running game

When
  • a reviewer checks the citation's meaning
Then
  • mechanics.match-score names every field in this state family and every point where it can affect later play
  • where the row answers does-not-return after a quit, a crash, or on another device, mechanics.match-score also names the value the family starts from; where it never gives that answer for those cases, this test checks nothing about a starting value
  • when the cited prose has another meaning, the adoption is invalid, and this test fails
Diagnostics
  • arcade-score-declaration-record

saved-in-joins-a-copy-kind · match-score

declaration check: match-score names an existing save kind when saved

scenarioper state-families row

Applies to the match-score row.

This declaration check reads the adoption, not the running game. When match-score has saved-in, it must name exactly one save kind in this adoption. When it names anything else, the adoption is invalid, and this test fails. With no saved-in, this test checks nothing. It says nothing about a family held in two places at once; one family names one restoring save kind.

Test steps and diagnostics
Given

the arcade-score adoption document, not the running game

When
  • a reviewer reads match-score's saved-in field
Then
  • where saved-in is present, it names exactly one copy-kinds row in this adoption; when it names anything else, the adoption is invalid, and this test fails
  • where saved-in is absent, this test checks nothing
Diagnostics
  • arcade-score-declaration-record

backup-copy-kind-joins

declaration check: Row.id names an existing save kind

scenarioper backup-copies row

Does not apply to arcade-score: backup-copies has no rows.

conflict-copy-kind-joins

declaration check: Row.id names an existing shared save kind

scenarioper copy-conflicts row

Does not apply to arcade-score: copy-conflicts has no rows.

earlier-point-copy-kind-joins

declaration check: Row.id names an existing save kind

scenarioper earlier-point-returns row

Does not apply to arcade-score: earlier-point-returns has no rows.

account-family-names-account-ending

declaration check: Row.id names account deletion or reset as its end

scenarioper state-families row

Does not apply to arcade-score: no row in state-families matches lifetime account.

restore-earlier-override-has-backup

declaration check: Row.id's restore-earlier-copy override has a backup row

scenarioper copy-kinds row

Does not apply to arcade-score: copy-kinds has no rows.

restore-earlier-default-has-backup

declaration check: the default answer restore-earlier-copy for Row.id has a backup row

scenarioper copy-kinds row

Does not apply to arcade-score: unreadable copy default is not asked for arcade-score.

returning-family-uses-shared-authority

declaration check: Row.id returns from a cloud-synced or server-authoritative save kind

scenarioper state-families row

Does not apply to arcade-score: no row in state-families matches on-another-device returns.

one-conflict-row-per-scope

declaration check: each conflict scope has at most one row

scenarioonce

Applies to arcade-score.

This declaration check reads the adoption, not the running game. Each represented conflict scope has at most one row. When a scope has more than one row, the adoption is invalid, and this test fails. With no conflict rows, this test checks nothing.

Test steps and diagnostics
Given

the arcade-score adoption document, not the running game

When
  • a reviewer groups copy-conflicts rows by a this-copy-kind name or the all-synced-kinds scope
Then
  • no declared scope has more than one conflict row; when a scope has more than one row, the adoption is invalid, and this test fails; with no conflict rows, this test checks nothing
Diagnostics
  • arcade-score-declaration-record

one-earlier-point-row-per-copy-kind

declaration check: each save kind has at most one earlier-point row

scenarioonce

Applies to arcade-score.

This declaration check reads the adoption, not the running game. Each save kind has at most one earlier-point row. When more than one row names a save kind, the adoption is invalid, and this test fails. With no such rows, this test checks nothing.

Test steps and diagnostics
Given

the arcade-score adoption document, not the running game

When
  • a reviewer groups earlier-point-returns rows by copy-kind
Then
  • no save kind is named by more than one earlier-point-returns row; when more than one row names a save kind, the adoption is invalid, and this test fails; with no such rows, this test checks nothing
Diagnostics
  • arcade-score-declaration-record

copy-citations-state-their-duties

declaration check: Row.id's required citations state their duties

scenarioper copy-kinds row

Does not apply to arcade-score: copy-kinds has no rows.

backup-citations-state-their-duties

declaration check: Row.id's backup citations state their duties

scenarioper backup-copies row

Does not apply to arcade-score: backup-copies has no rows.

conflict-citation-states-its-duties

declaration check: Row.id's resolution citation states its duties

scenarioper copy-conflicts row

Does not apply to arcade-score: copy-conflicts has no rows.

earlier-return-citation-states-its-duties

declaration check: Row.id's return citation states its duties

scenarioper earlier-point-returns row

Does not apply to arcade-score: earlier-point-returns has no rows.

copy-removal-citation-states-its-duty

declaration check: Row.id's removal citation states its duty

scenarioper copy-kinds row

Does not apply to arcade-score: copy-kinds has no rows.

copy-mode-citation-states-its-duty

declaration check: Row.id's mode citation states its duty

scenarioper copy-kinds row

Does not apply to arcade-score: copy-kinds has no rows.

player-mode-return-is-mode-gated

declaration check: Row.id's player-mode return is mode-gated

scenarioper earlier-point-returns row

Does not apply to arcade-score: earlier-point-returns has no rows.

earlier-point-mode-citation-states-its-duty

declaration check: Row.id's mode citation states its duty

scenarioper earlier-point-returns row

Does not apply to arcade-score: earlier-point-returns has no rows.

persistence-holds

the persistence mechanism holds for the whole run

generalonce

Applies to arcade-score.

Across each of quit, crash, death, slot deletion, profile deletion, and device change that the adoption can construct, plus each constructible damage and build-mismatch case, each family's value on both sides of every constructed boundary matches its row and the numbered settlement order holds. Every load the adoption can construct refuses partial state and activates all carried families together only when accepted. For a case that the adoption cannot construct, this test checks nothing. The pack's default audit seeds apply unless the adoption supplies its own.

Test steps and diagnostics
Holds

every state family's distinguishable value on both sides of every constructed boundary matched its row; every commit, candidate, conflict, build comparison, damage response, activation, earlier return, deletion, device return, and settlement report that the adoption constructed followed the mechanism's numbered order; every constructed load that was partial or refused activated no state; every accepted load activated all carried families together; where the adoption can construct no load, conflict, damage, build mismatch, or earlier return, this test checks nothing about that case

Seeds

["audit-a","audit-b"]

Scope

each of quit, crash, death, slot deletion, profile deletion, and device change that the adoption can construct, plus each constructible damage and build-mismatch case

Diagnostics
  • arcade-score-state-boundary-trace
  • arcade-score-commit-log
  • arcade-score-candidate-set
  • arcade-score-settlement-trace
  • arcade-score-load-result
  • arcade-score-active-state-source
  • arcade-score-settlement-report
  • arcade-score-first-ordering-violation
JSONthe adoption as one file

Answers you try on this page do not change this file. To change an adoption, open it in the authoring tool.

{
  "contract": "state-persistence-scope",
  "version": 2,
  "summary": "This contract covers which state is kept after the end of a run or session, a quit, a crash, death, the deletion of saved data, and a move to another device. It also covers local saves, manual slots, cloud copies, server profiles, backups, conflicts, and version changes. This contract does not cover time that passes while the game is closed, replay guarantees, file formats, or the shape of saved numbers. The Suspension and catch-up contract covers time while the game is closed. The Replay scope contract covers replay guarantees.",
  "questions": {
    "unreadable-copy-default": {
      "asks": "By default, what happens when saved state cannot be read?",
      "rationale": "Lost progress is the most visible failure of a save system. This answer is the default answer. A save kind can choose another answer in its own row.",
      "when": {
        "row-count": {
          "copy-kinds": "non-empty"
        }
      },
      "otherwise": "No saved copy can be opened later, so unreadable saved state never reaches a load attempt.",
      "options": {
        "refuse-load": {
          "meaning": "The unreadable copy stays closed. A city slot remains listed, but none of its progress becomes active.",
          "semantics": "Loading stops before any state from that copy becomes active. The copy is neither replaced nor repaired by this response."
        },
        "start-fresh": {
          "meaning": "Play starts from the normal starting state. A damaged puzzle slot opens with its first board instead of the old board.",
          "semantics": "The starting state becomes active instead of state from the unreadable copy. The response does not by itself delete or overwrite that copy."
        },
        "restore-earlier-copy": {
          "meaning": "An earlier copy can replace the unreadable copy through a route that the game supports. Restoring a file by hand is one such route. A factory game continues after the player chooses the backup that was made before the damaged autosave.",
          "semantics": "A declared manual, in-game, or automatic recovery route can activate an earlier copy. The newest usable earlier copy under the matching backup rule becomes active."
        },
        "repair-readable-state": {
          "meaning": "Readable progress returns and damaged parts reset. A dungeon keeps cleared floors but rebuilds one broken room record.",
          "semantics": "Readable state becomes active as one repaired result. Every unreadable part takes the starting value named by the cited repair rule."
        },
        "warn-and-try": {
          "meaning": "The player can cancel or try the unreadable copy. A colony save opens only after its damage warning is accepted.",
          "semantics": "No state from the copy becomes active before the explicit choice. Accepting the warning permits an attempt but does not promise success."
        }
      }
    },
    "older-build-default": {
      "asks": "By default, what happens when an older game build wrote the copy?",
      "rationale": "An update can make a player's progress impossible to load, or change it permanently. Choose how the current build protects that progress.",
      "when": {
        "row-count": {
          "copy-kinds": "non-empty"
        }
      },
      "otherwise": "No loadable copy exists, so an older build has no saved state to open.",
      "options": {
        "refuse-load": {
          "meaning": "The older copy stays closed. A world from last year's rules cannot open in the current build.",
          "semantics": "Loading stops before state written by the older build becomes active."
        },
        "convert-and-load": {
          "meaning": "The game updates the old state and opens it. A factory save gains the current recipe records before play resumes.",
          "semantics": "A declared conversion produces current-build state before activation."
        },
        "warn-and-load": {
          "meaning": "The old copy is tried only after a warning. A racing career from an earlier patch opens after the player accepts the risk.",
          "semantics": "The player accepts a compatibility warning before old-build state becomes active. Acceptance permits an attempt but does not promise success."
        }
      }
    },
    "newer-build-default": {
      "asks": "By default, what happens when a newer game build wrote the copy?",
      "rationale": "Going back to an older build can erase newer content. Choose whether the player may take that risk.",
      "when": {
        "row-count": {
          "copy-kinds": "non-empty"
        }
      },
      "otherwise": "No loadable copy exists, so a newer build has no saved state to open.",
      "options": {
        "refuse-load": {
          "meaning": "The newer copy stays closed. A world from a later patch remains unavailable in this build.",
          "semantics": "Loading stops before state written by the newer build becomes active."
        },
        "warn-and-load": {
          "meaning": "The newer copy is tried only after a warning. A sandbox world opens after the player accepts that newer content may be lost.",
          "semantics": "The player accepts a compatibility warning before newer-build state becomes active. Acceptance permits an attempt but does not promise success."
        }
      }
    }
  },
  "declares": {
    "values": {},
    "rows": {
      "state-families": {
        "description": "List each state family. A state family is a group of state that can change later play. For each state family, give how long it lasts and what happens to it at a quit, a crash, death, a deletion, and a move to another device.",
        "when-empty": "No changing game state is covered, so this adoption makes no persistence promise.",
        "record": {
          "id": {
            "type": "string",
            "required": true,
            "pattern": "kebab-case",
            "unique": true,
            "description": "A short name for the state family in your game's words, such as campaign-progress, run-inventory, unlocks, or settings."
          },
          "state-declared-in": {
            "type": "citation",
            "required": true,
            "description": "Where your game's rules say which fields belong to this family and at which points the family can affect later play. For a family that does not return after a quit, after a crash, or on another device, the rules also name the value that the family starts from."
          },
          "lifetime": {
            "type": "string",
            "required": true,
            "options": [
              "run",
              "session",
              "slot",
              "profile",
              "device",
              "account",
              "until-event",
              "not-kept"
            ],
            "description": "How long this state lasts. Run: the state belongs to one attempt; a roguelike's carried items are lost when that attempt ends. Session: the state belongs to one continuous visit; a co-op lobby choice is lost when the party leaves that lobby. Slot: the state belongs to one player-selected save; campaign progress is lost when that slot is deleted. Profile: the state belongs to one local player profile; unlocks are lost when that profile is deleted. Device: the state belongs to one installation; local settings are lost when that device data is erased. Account: the state belongs to one account; online settings are lost when that account is deleted or reset. Until event: the state lasts to one named game event; dropped currency is lost at the next death. Not kept: the state is lost at its first named interruption; an enemy's alert meter does not return after quitting."
          },
          "scope-starts-in": {
            "type": "citation",
            "when": {
              "row": {
                "lifetime": [
                  "run",
                  "session"
                ]
              }
            },
            "description": "Where your game's rules say when this attempt or continuous visit starts. The player can observe the start. A run may start when the character enters the dungeon; a session may start when the party joins a lobby."
          },
          "scope-ends-in": {
            "type": "citation",
            "when": {
              "row": {
                "lifetime": [
                  "run",
                  "session",
                  "slot",
                  "profile",
                  "device",
                  "account",
                  "not-kept"
                ]
              }
            },
            "description": "Where your game's rules say when this lifetime ends. The player can observe the ending. It may be an attempt result, leaving a lobby, deleting a player-selected slot, deleting a local profile, erasing device data, deleting an account, or the first interruption that discards the value."
          },
          "lifetime-event-declared-in": {
            "type": "citation",
            "when": {
              "row": {
                "lifetime": [
                  "until-event"
                ]
              }
            },
            "description": "Where your game's rules say which named game event destroys the value that the family had before the event. A recovery point may last until the next death after it appears."
          },
          "saved-in": {
            "type": "string",
            "pattern": "kebab-case",
            "description": "The save kind that restores this family. When the field is absent, no copy restores the family."
          },
          "after-normal-quit": {
            "type": "string",
            "required": true,
            "options": [
              "returns-last-commit",
              "does-not-return"
            ],
            "description": "Whether this state returns after a normal quit. The answer returns-last-commit means that the state returns as it was at the last successful save. A campaign returns its last checkpoint; an unsaved match score does not."
          },
          "after-crash": {
            "type": "string",
            "required": true,
            "options": [
              "returns-last-commit",
              "does-not-return"
            ],
            "description": "Whether this state returns after a crash. The answer returns-last-commit means that the state returns as it was at the last successful save before the crash. A world that is saved on a timer may lose the changes made since its last save, and still return that save."
          },
          "after-death": {
            "type": "string",
            "required": true,
            "options": [
              "keeps-value",
              "clears-value"
            ],
            "description": "Whether death keeps this value. Roguelike unlocks may remain while the dead run's inventory clears."
          },
          "after-slot-delete": {
            "type": "string",
            "required": true,
            "options": [
              "keeps-value",
              "clears-value",
              "no-slot"
            ],
            "description": "Whether this value is kept when the related player-selected slot is deleted. The storage rule of a save kind with several player-selected slots names that slot. For a save kind with one current copy, answer no-slot."
          },
          "after-local-profile-delete": {
            "type": "string",
            "required": true,
            "options": [
              "keeps-value",
              "clears-value",
              "no-profile"
            ],
            "description": "Whether this value is kept when the related local player profile is deleted. The storage rule of the save kind that restores this family names that profile. For a family with no such profile, answer no-profile."
          },
          "on-another-device": {
            "type": "string",
            "required": true,
            "options": [
              "returns",
              "does-not-return"
            ],
            "description": "Whether the value returns when the same eligible player continues on another device. A server profile may return; local settings may not."
          }
        }
      },
      "copy-kinds": {
        "description": "List each save kind. A save kind is one kind of saved copy that is written and loaded independently, such as campaign slots, profile data, cloud settings, or a server profile.",
        "when-empty": "No saved copy can be opened later. Loading cannot restore state, compare builds, recover damage, or resolve a conflict.",
        "record": {
          "id": {
            "type": "string",
            "required": true,
            "pattern": "kebab-case",
            "unique": true,
            "description": "A short name for this save kind in your game's words, such as campaign-slots, unlock-profile, or account-settings."
          },
          "contains-declared-in": {
            "type": "citation",
            "required": true,
            "description": "Where your game's rules say which state families this save kind writes and restores together. The rules list the state-family ids."
          },
          "write-trigger": {
            "type": "string",
            "required": true,
            "options": [
              "player-save",
              "checkpoint",
              "continuous",
              "timer",
              "service-owned"
            ],
            "description": "What starts a save: a save action, a named checkpoint, each accepted change, a timer, or the service's own rule."
          },
          "write-rule-declared-in": {
            "type": "citation",
            "required": true,
            "description": "Where your game's rules say exactly when a save begins, what makes it successful, and which later changes a quit or a crash can lose."
          },
          "residence": {
            "type": "string",
            "required": true,
            "options": [
              "local-device",
              "cloud-synced",
              "server-authoritative"
            ],
            "description": "Where the copy that decides the restored state is kept: on this device, in copies that are synced between devices, or on a game service. Synced copies can disagree. A profile that the service controls uses the state that the service accepted."
          },
          "layout": {
            "type": "string",
            "required": true,
            "options": [
              "one-current",
              "manual-slots"
            ],
            "description": "How the save is presented: one current copy or several player-selected slots. A slot is one named load choice in the storage rule; retained earlier copies belong in the backup list."
          },
          "storage-rule-declared-in": {
            "type": "citation",
            "required": true,
            "description": "Where your game's rules say where the copy is kept, which sync or service has authority over it, and how the player selects it. For a save kind with several player-selected slots, the rules also name the identity of each slot, and that identity does not change. For a save kind with one current copy, the rules name no player-selected slot."
          },
          "unreadable-copy": {
            "type": "string",
            "required": true,
            "options": [
              "use-game-default",
              "refuse-load",
              "start-fresh",
              "restore-earlier-copy",
              "repair-readable-state",
              "warn-and-try"
            ],
            "description": "What happens when a copy of this save kind cannot be read. Use the default answer, or choose another answer for this row."
          },
          "older-build-copy": {
            "type": "string",
            "required": true,
            "options": [
              "use-game-default",
              "refuse-load",
              "convert-and-load",
              "warn-and-load"
            ],
            "description": "What happens when an older game build wrote a copy of this save kind. Use the default answer, or choose another answer for this row."
          },
          "newer-build-copy": {
            "type": "string",
            "required": true,
            "options": [
              "use-game-default",
              "refuse-load",
              "warn-and-load"
            ],
            "description": "What happens when a newer game build wrote a copy of this save kind. Use the default answer, or choose another answer for this row."
          },
          "mode-gated": {
            "type": "string",
            "options": [
              "only-in-named-modes"
            ],
            "description": "This save kind exists only in named player modes. A restore point may exist in a practice mode and a standard mode but not in a challenge mode."
          },
          "mode-rule-declared-in": {
            "type": "citation",
            "when": {
              "row": {
                "mode-gated": [
                  "only-in-named-modes"
                ]
              }
            },
            "description": "Where your game's rules say in which player modes this save kind exists. The rules name every such mode and confirm that the save kind is absent in every other mode."
          },
          "removed-at": {
            "type": "string",
            "options": [
              "normal-quit",
              "crash",
              "death",
              "slot-delete",
              "local-profile-delete",
              "device-data-delete",
              "account-delete"
            ],
            "description": "The event that removes this copy from later load choices. A permadeath save may be removed when the character dies. For a save kind with several player-selected slots, this event removes the copy of the deleted slot, not the whole save kind."
          },
          "removal-rule-declared-in": {
            "type": "citation",
            "when": {
              "row": {
                "removed-at": [
                  "normal-quit",
                  "crash",
                  "death",
                  "slot-delete",
                  "local-profile-delete",
                  "device-data-delete",
                  "account-delete"
                ]
              }
            },
            "description": "Where your game's rules say exactly which event removes the copy and what the player sees afterward."
          }
        }
      },
      "backup-copies": {
        "description": "List save kinds that retain an earlier copy for supported recovery.",
        "when-empty": "No save kind promises an earlier usable copy, so damage cannot be answered by restoring one.",
        "record": {
          "id": {
            "type": "string",
            "required": true,
            "pattern": "kebab-case",
            "unique": true,
            "description": "A short name for this earlier copy in your game's words, such as previous-campaign-checkpoint."
          },
          "copy-kind": {
            "type": "string",
            "required": true,
            "pattern": "kebab-case",
            "description": "The save kind protected by this earlier copy."
          },
          "backup-rule-declared-in": {
            "type": "citation",
            "required": true,
            "description": "Where your game's rules say when an earlier copy is kept, how many earlier copies remain, and which later progress a recovery loses."
          },
          "restore-rule-declared-in": {
            "type": "citation",
            "required": true,
            "description": "Where your game's rules say which supported route opens an earlier copy. The rules say whether recovery is automatic, happens in the game, or is a file step outside the game."
          }
        }
      },
      "copy-conflicts": {
        "description": "List shared save kinds that can present two complete but disagreeing copies. One adoption has at most one row per conflict scope: one for each named save kind and one for all synced kinds.",
        "when-empty": "No shared save kind can present two disagreeing copies, so no conflict response runs.",
        "record": {
          "id": {
            "type": "string",
            "required": true,
            "pattern": "kebab-case",
            "unique": true,
            "description": "A short name for the conflict in your game's words, such as campaign-slot-sync."
          },
          "scope": {
            "type": "string",
            "required": true,
            "options": [
              "this-copy-kind",
              "all-synced-kinds"
            ],
            "description": "Whether the response settles one save kind or every synced save kind together. One cloud dialog may settle campaign slots and quicksaves as a set."
          },
          "copy-kind": {
            "type": "string",
            "when": {
              "row": {
                "scope": [
                  "this-copy-kind"
                ]
              }
            },
            "pattern": "kebab-case",
            "description": "The one cloud-synced or service save kind whose copies can disagree."
          },
          "response": {
            "type": "string",
            "required": true,
            "options": [
              "select-by-rule",
              "player-chooses-copy",
              "refuse-until-resolved"
            ],
            "description": "What happens before any disagreeing state becomes active. A fixed rule may always prefer the copy the game service holds; the player may choose between dated copies; or neither copy may open until sync is repaired."
          },
          "resolution-declared-in": {
            "type": "citation",
            "required": true,
            "description": "Where your game's rules say how this conflict is handled. The rules say how disagreement is detected. They say how the identities of the copies are compared. They say which copies are selected or kept. They name the event that clears the conflict."
          }
        }
      },
      "earlier-point-returns": {
        "description": "List save kinds that can return to an earlier point of the same continuous play, whether or not the game names a run. One adoption has at most one row per save kind.",
        "when-empty": "No save kind can return to an earlier point of the same continuous play, so the player cannot load a save to try again.",
        "record": {
          "id": {
            "type": "string",
            "required": true,
            "pattern": "kebab-case",
            "unique": true,
            "description": "A short name for the earlier return point in your game's words, such as dungeon-retry or quicksave."
          },
          "copy-kind": {
            "type": "string",
            "required": true,
            "pattern": "kebab-case",
            "description": "The save kind that can return play to the earlier point."
          },
          "after-load": {
            "type": "string",
            "required": true,
            "options": [
              "resume-stays",
              "resume-is-used-once",
              "player-mode-decides"
            ],
            "description": "Whether the same earlier point remains available after a successful load. A practice mode may keep a boss checkpoint while an iron mode allows one return."
          },
          "return-rule-declared-in": {
            "type": "citation",
            "required": true,
            "description": "Where your game's rules say what the earlier point is, when activation is successful, and whether another load remains possible."
          },
          "mode-gated": {
            "type": "string",
            "options": [
              "only-in-named-modes"
            ],
            "description": "This earlier return point exists only in named player modes. A quicksave may be available in a practice mode but absent in a challenge mode."
          },
          "mode-rule-declared-in": {
            "type": "citation",
            "when": {
              "row": {
                "mode-gated": [
                  "only-in-named-modes"
                ]
              }
            },
            "description": "Where your game's rules say in which player modes this earlier return point exists. The rules name every such mode and confirm that the point is absent in every other mode."
          }
        }
      }
    }
  },
  "rules": {},
  "origin": "https://opengdd.org/contracts/state-persistence-scope-2",
  "mechanism": [
    "This text decides the order of the steps for state-family identity, scope starts and endings, commits, loads, damage, build mismatch, copy conflicts, deletion, and device change. The questions and rows supply choices and cited game rules. They do not change the order.",
    "A **run** begins at the player-observable attempt start named by `scope-starts-in` and ends at the result or exit named by `scope-ends-in`. Victory, death, and abandonment are common endings, but only the cited game rules decide. A save or load does not start or end a run. A **session** begins when play enters the world, match, lobby, or profile context named by `scope-starts-in` and ends at the exit, switch, or reset named by `scope-ends-in`. It continues through a pause, background interval, closure, or relaunch that returns to that same context; a missing commit may still prevent its earlier value from returning.",
    "A **save kind** is one row of `copy-kinds`. A **commit** is one coherent accepted write of a save kind. A **copy** is a load candidate with its save-kind id, copy identity, any slot identity, build identity, commit identity, and complete state-family set. A **conflict** exists when the cited rule finds two accepted shared copies in one declared conflict scope that disagree and neither has already replaced the other. An **earlier copy** is a retained older commit named by a backup row.",
    "### Change and commit",
    "1. When state changes, keep each `state-families` row as one family under its cited game rule. The row's lifetime is the longest time for which a value of the family is kept. An `until-event` value ends at its cited game event. A boundary result may clear a value before its lifetime ends only when the row says so.",
    "2. Evaluate run and session starts and endings from their separate cited player-observable events. A save action, automatic write, timer tick, process launch, process close, or load does not create a new run or session by itself.",
    "3. Start a commit of a save kind only at its selected write trigger. A player save follows the cited save action. A checkpoint follows its named game event. Continuous writing follows each accepted change named by its rule. A timer follows the cited interval. A service-owned copy follows its authority's cited acceptance event.",
    "4. A commit becomes loadable only when `write-rule-declared-in` reports success for the complete `contains-declared-in` set. A failed or partial write never replaces the last successful commit. Earlier copies are retained and ordered only by their backup and storage rules.",
    "### Quit, crash, and return",
    "5. On a normal quit, perform a final commit only when the cited write rule of the save kind requires one. For each state row, `returns-last-commit` restores its latest successful commit on return; `does-not-return` starts from the cited post-quit value. Uncommitted changes do not return. A copy removed at normal quit is no longer a later load choice.",
    "6. On a crash, no unfinished commit succeeds. For each state row, `returns-last-commit` restores the last commit that succeeded before the crash; `does-not-return` starts from the cited post-crash value. With a timer or a checkpoint, a crash can therefore lose the changes made since the last successful commit. These changes are the possible loss window, and the cited write rule states it. A copy removed at crash is no longer a later load choice.",
    "7. Select the requested slot or profile before activating state. A `one-current` save kind selects its current copy. `manual-slots` selects the player's named slot. A retained earlier copy is not another layout; the damage response and matching backup rule may select it later, and a backup row's cited restore route is also available to the player directly, independent of any damage response.",
    "8. A local-device copy is available only on that device unless another cited transfer exists outside this adoption. A cloud-synced save kind obtains every accepted device and cloud candidate before conflict handling. A server-authoritative save kind obtains the service's accepted copy and does not treat an unaccepted local cache as equal authority. Read each candidate's save-kind, slot, build, and commit identity now. A candidate whose identity cannot be read is not a conflict candidate. If no candidate remains, run the damage response of the requested save kind immediately, without conflict or build comparison. A readable header with an unreadable gameplay body remains a candidate for steps 9 and 10, then reaches the damage response in step 11.",
    "### Conflict, compatibility, and damage",
    "9. If readable shared candidates conflict, apply the matching row's response before reading their gameplay state. A one-kind row settles only that save kind; a whole-set row settles every synced save kind together. `select-by-rule` chooses one complete copy. `player-chooses-copy` waits for one complete-copy choice. `refuse-until-resolved` activates neither. No response merges fields.",
    "10. Compare the selected copy's writing build with the running build. Apply the save-kind override when present; otherwise apply the matching default answer for an older or a newer build. Refusal activates no state. Conversion produces one current-build copy before activation. A warning precedes the attempt and does not promise that it succeeds. If conversion fails, refuse the load and leave the source copy unchanged unless its cited rule says otherwise.",
    "11. Read the selected copy as one coherent unit. If it is unreadable, apply the save-kind override or `unreadable-copy-default`. Refusal activates nothing. `start-fresh` activates the cited starting state without silently deleting the unreadable copy. Earlier-copy recovery follows the matching backup row, selects the newest usable earlier copy, and reports the lost interval. Repair activates one coherent repaired result and resets every unreadable part under the cited rule; failure to produce a coherent result refuses the load. Warning waits for the player's choice before any attempted activation.",
    "12. Activate all state families in the accepted copy together. State families saved in another save kind keep the already active value from that kind; a state family without `saved-in` is never restored by this load.",
    "13. If the accepted copy has an `earlier-point-returns` row, return continuous play to its cited earlier point. If that point is inside a run, continue the same run identity. Apply `after-load` only after successful activation: keeping the resume leaves it available, using it once makes it unavailable, and a player-mode answer follows the mode that was set before the load.",
    "### Death, deletion, and another device",
    "14. On death, apply every state's `after-death` result. `clears-value` removes the earlier value before later play. `keeps-value` retains it. If death is also the cited run end, every run-lifetime value clears even if no save action occurs. If it is the cited event for an `until-event` value, that value clears as well. Longer-lived unlocks, settings, and profile state follow their own rows. A copy with `removed-at: death` disappears from the load choices after these state results are settled.",
    "15. On slot deletion, clear each `clears-value` row tied to that slot and keep each `keeps-value` row. `no-slot` records that the state has no related player-selected slot. Deleting a local profile applies `after-local-profile-delete` the same way and includes any slots whose cited rule makes them children of that profile. Device-data deletion ends device-lifetime state under its cited rule. Account deletion or reset ends account-lifetime state through `scope-ends-in`. At each event, remove every copy whose `removed-at` value names it.",
    "16. On another eligible device, `returns` obtains the family from its cloud-synced or server-authoritative copy before activation and still runs conflict handling. `does-not-return` starts from the cited new-device value. A device change does not by itself delete the source device's copy.",
    "17. After every event, no earlier value affects later play once its lifetime has ended or its selected boundary result cleared it. A later game rule may derive a new value, but that is new state rather than restoration of the cleared value. A mode-gated copy or earlier return point exists only while the cited player mode permits it.",
    "Every settlement report identifies the state-family id, save-kind id or its absence, event, scope before and after, last successful commit when read, selected copy and build when loaded, override or default answer used, state result, and any lost interval. Conflict reports also identify both candidate commits and the selected response. Damage reports identify refusal, starting state, earlier-copy recovery, repaired parts, or warning choice without claiming an unattempted load succeeded."
  ],
  "pack": "sha256:4ec64b865a0ade721bc27b72550f9d1d57e3e93d9a060e72b6eba5890aea75fd",
  "answers": {},
  "values": {},
  "rows": {
    "state-families": [
      {
        "id": "match-score",
        "state-declared-in": "mechanics.match-score",
        "lifetime": "run",
        "scope-starts-in": "match.start",
        "scope-ends-in": "match.result-or-exit",
        "after-normal-quit": "does-not-return",
        "after-crash": "does-not-return",
        "after-death": "clears-value",
        "after-slot-delete": "no-slot",
        "after-local-profile-delete": "no-profile",
        "on-another-device": "does-not-return"
      }
    ],
    "copy-kinds": [],
    "backup-copies": [],
    "copy-conflicts": [],
    "earlier-point-returns": []
  }
}