Determinism & replay

Lives refill while away

lives-refill-while-away · an adoption of Suspension and catch-up, version 1

Tests included

Use in your game

Opens the authoring tool with these answers and settings already filled in. You still add your game's own rules and tuning.

What is in the ZIP

The ZIP holds the adoption and its acceptance tests. In the authoring tool, choose Add contract and pick this ZIP. If you keep your design as files, put them in contracts/.

The contract behind it

Covers state that pauses, catches up, or continues while a game is closed or in the background. It also covers return rewards, calendar crossings, clock changes, and what appears before play resumes. Normal rates, timer rules, inventory capacity, save lifetime, and replay guarantees stay elsewhere.

Read the full Suspension and catch-up contract →

Questions

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

What happens while the game stays open in the background?

Choices for What happens while the game stays open in the background?
Supplied answer Background time uses the same suspension rules as closed time. Ten minutes in another tab refills energy like ten minutes with the game closed.

The lifecycle treats the background interval as a suspension interval. Ordinary updates do not also apply that interval.

Ordinary play continues in the background. A browser farm keeps growing crops while the player uses another tab.

The ordinary update path continues, including platform throttling. Foregrounding adds no catch-up for the same interval.

The open game stays still, and away progress starts only after closing. A crop timer pauses in the app switcher but advances after the app closes.

The open-background interval receives neither ordinary updates nor catch-up.

Why this is asked

An open game may use away handling, keep playing, or stay still until it closes. The same time must never count twice.

What appears before ordinary play resumes?

Choices for What appears before ordinary play resumes?
No separate return surface appears. A match-3 game simply shows that three lives are ready.

The lifecycle adds no player-facing return surface. Existing game displays may show the settled state after ordinary play resumes. Developer or diagnostic instrumentation that is not player-facing is out of scope.

Supplied answer Existing displays show the change before play resumes. A life counter ticks from two lives to five on the map screen.

At least one existing player-facing display presents the affected state during return settlement.

A return surface names each result. An idle game shows energy gained and coins waiting for collection.

A player-facing return surface identifies each affected row and its settled result.

A return surface also explains the calculation. A factory screen shows six hours away, its energy ceiling, and the final gain.

The return surface reports elapsed time, applicable limits, amount rules, fidelity, and settled results.

Why this is asked

Changed state may speak for itself, animate before play, or appear in a return summary.

Can the player change the clock that measures time away?

Asked when
the clock list has at least one row.
If not asked
No clock row exists, so this adoption measures no elapsed suspension time.
Choices for Can the player change the clock that measures time away?
The game reads a clock from the device. Moving a phone clock forward by two hours gives an idle mine two more measured hours.

The elapsed interval comes from a clock the player can change through device settings. Both backward-clock questions are asked.

Supplied answer The game reads a clock outside device settings. Changing a phone clock does not refill a match-3 life sooner.

The elapsed interval comes from a clock the player cannot change through device settings. The backward-clock questions are not asked.

Why this is asked

The player can move a device clock through settings. A protected clock stays outside those settings, though the service behind it can correct it.

If the player moves the device clock backward, which state does the game keep?

Asked when
Clock source is Device clock.
If not asked
No device clock is read, either because no clock row exists or because the clock is protected, so no rollback anchor is selected. A protected-clock resync may still move its reading backward; the lifecycle then keeps the loaded state.
Choices for If the player moves the device clock backward, which state does the game keep?
The game returns to the state captured when suspension began. An idle mine returns to the ore count recorded when the app closed.

Every in-scope local item is restored from the suspension snapshot. Elapsed time for this interval is zero.

The game returns to the latest state accepted by a trusted rule. An online workshop returns to its last server-confirmed stock count.

The one rollback rule supplies a trusted state and matching clock reading. The current suspension interval contributes zero elapsed time.

The game keeps the state loaded at return but adds no away change. A newer cloud save from another device keeps its crops.

No state is restored by this contract. The loaded return state remains, and elapsed time for this interval is zero. That state may be newer than the suspension snapshot because of a cloud save, a second device, or a mid-session autosave.

Why this is asked

Zero elapsed time does not say which saved state survives a backward clock. The anchor prevents duplicate or reversed gains.

Does the game explain the backward clock result?

Asked when
Clock source is Device clock.
If not asked
No device clock is read, either because no clock row exists or because the clock is protected, so no player-caused clock-change notice is selected. A protected-clock resync may still produce zero elapsed time.
Choices for Does the game explain the backward clock result?
The game adds no clock-change notice. A life refill stays unchanged without a pop-up.

The lifecycle adds no player-facing explanation for the backward-clock branch.

The game explains that the clock change blocked away time. An idle game shows a short clock-change message beside unchanged energy.

The lifecycle presents a player-facing clock-change explanation after the anchor is applied and before ordinary play resumes.

Why this is asked

The same backward-clock outcome can be silent or visible to the player.

Several calendar boundaries passed while the game was away. Which ones count on return?

Asked when
the calendar boundaries list has at least one row.
If not asked
No calendar boundary is tracked, so return settlement creates no calendar crossing.
Choices for Several calendar boundaries passed while the game was away. Which ones count on return?
Every crossed boundary settles in time order. A farm processes Friday, Saturday, and Sunday crop resets separately.

The lifecycle inserts every crossed boundary into the return timeline in ascending time order.

Only the latest crossed boundary settles. A daily reward calendar opens on today's entry without granting missed days.

The lifecycle inserts only the latest crossed boundary for each calendar row and omits earlier crossings.

All crossings become one return event. A weekly shop refreshes once and receives the number of missed weeks.

The lifecycle inserts one return-time event per calendar row, carrying that row's number and span of crossed boundaries.

Why this is asked

A daily or weekly rule may run for every crossing, only the latest one, or one combined return event.

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.

Lists8 lists

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

Clock

clock

IdClock declared in
life-clocktime.account-time

Fixed items

fixed-items

This adoption declares no fixed items.

Catch up items

catch-up-items

IdState declared inAway amountAmount rule declared inCatch up fidelityFidelity rule declared inAway limitLimit rule declared in
livesmechanics.life-refillsame-as-playingone-derived-changelives.from-elapsed-timestate-ceilingcontracts.player-lives.max

Continuous items

continuous-items

This adoption declares no continuous items.

Choice items

choice-items

This adoption declares no choice items.

Return rewards

return-rewards

This adoption declares no return rewards.

Calendar boundaries

calendar-boundaries

This adoption declares no calendar boundaries.

Rollback rules

rollback-rules

This adoption declares no rollback rules.

Test inputsscope and seeds

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

This adoption supplies none; every test uses its defaults.

Acceptance tests28 tests apply

The contract comes with 80 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.

suspension-interval-boundaries

a suspension interval has one beginning and one return

scenarioonce

Applies to lives-refill-while-away.

A suspension interval begins once, at the earlier background transition, and ends once at return processing, at the return clock reading where a clock row exists. The interval contains no time outside those boundaries.

Test steps and diagnostics
Given

a covered close-and-return cycle after the selected background handling

When
  • the lifecycle records the interval boundaries
Then
  • the suspension interval begins once, at the earlier background transition, and ends once at return processing, at the return clock reading where a clock row exists
  • no time before its beginning or after its end enters that interval
Diagnostics
  • lives-refill-while-away-suspension-interval

suspension-snapshot-vocabulary

the suspension snapshot is one coherent starting state

scenarioonce

Applies to lives-refill-while-away.

The suspension snapshot is one coherent starting state for every in-scope local item. Where this adoption has a clock row, the starting reading is captured in the same batch; without one, that clause asserts nothing.

Test steps and diagnostics
Given

distinguishable values for every in-scope local item when a suspension interval begins

When
  • the suspension snapshot is captured
Then
  • the snapshot contains the same-instant starting value of every in-scope local item
  • where a clock row exists, its starting reading belongs to the same snapshot batch; without a clock row, the reading clause asserts nothing
Diagnostics
  • lives-refill-while-away-suspension-snapshot

pending-reward-vocabulary-and-expiry

Row.id stays pending until acceptance or expiry

scenarioper return-rewards row

Does not apply to lives-refill-while-away: return-rewards has no rows.

expiry-deadline-enforced

Row.id is removed when its cited deadline expires

scenarioper return-rewards row

Does not apply to lives-refill-while-away: return-rewards has no rows.

background-counts-as-away

background time begins the suspension interval

scenarioonce

Applies to lives-refill-while-away.

The background transition begins the suspension interval. Ordinary updates do not also apply that interval, so the same time is counted once.

Test steps and diagnostics
Given

the game open with distinguishable covered state before it enters the background

When
  • the background transition occurs and the game later returns
Then
  • the suspension interval begins at the background transition
  • ordinary updates do not also apply any time in that interval
Diagnostics
  • lives-refill-while-away-suspension-interval
  • lives-refill-while-away-return-timeline

background-keeps-playing

background time stays on the ordinary update path

scenarioonce

Does not apply to lives-refill-while-away: while in the background is counts as away, not keeps playing.

background-waits-until-closed

background state waits until closure

scenarioonce

Does not apply to lives-refill-while-away: while in the background is counts as away, not waits until closed.

closure-captures-one-snapshot

closure begins an interval and captures one snapshot

scenarioonce

Applies to lives-refill-while-away.

Closing always begins a suspension interval. One coherent suspension snapshot is captured before covered local state can change, and the same time never follows two paths.

Test steps and diagnostics
Given

a game closure with distinguishable covered state

When
  • closure begins a suspension interval
Then
  • one coherent suspension snapshot is captured before any covered local state can change
  • the same time is never applied by two lifecycle paths
Diagnostics
  • lives-refill-while-away-suspension-snapshot
  • lives-refill-while-away-suspension-interval

clock-starting-reading · life-clock

life-clock starts at the higher available reading

scenarioper clock row

Applies to the life-clock row.

life-clock is read in the suspension snapshot batch from time.account-time. The interval starts at the higher of that reading and the committed anchor, so time below the anchor is never replayed.

Test steps and diagnostics
Given

a committed anchor and a starting reading supplied by time.account-time

When
  • the suspension snapshot and starting reading are captured in one batch
Then
  • the interval starts from the higher of the sampled reading and the committed anchor
  • time below the committed anchor is never replayed
Diagnostics
  • lives-refill-while-away-clock-readings
  • lives-refill-while-away-suspension-snapshot

fixed-item-held-at-snapshot

Row.id stays at its suspension snapshot state

scenarioper fixed-items row

Does not apply to lives-refill-while-away: fixed-items has no rows.

catch-up-item-held-at-snapshot · lives

lives waits at its suspension snapshot state

scenarioper catch-up-items row

Applies to the lives row.

lives remains at its suspension snapshot state while away. Local catch-up does not appear before return settlement.

Test steps and diagnostics
Given

lives at a distinguishable state when suspension begins

When
  • the suspension interval is still away
Then
  • lives remains at its suspension snapshot state and shows no local catch-up advance
Diagnostics
  • lives-refill-while-away-suspension-snapshot
  • lives-refill-while-away-catch-up-trace

choice-item-resolves-selection

Row.id resolves its cited selection

scenarioper choice-items row

Does not apply to lives-refill-while-away: choice-items has no rows.

continuous-item-stays-with-authority

Row.id stays under its cited authority

scenarioper continuous-items row

Does not apply to lives-refill-while-away: continuous-items has no rows.

catch-up-waits-for-return · lives

lives records reach and settles only on return

scenarioper catch-up-items row

Applies to the lives row.

While ordinary updates are stopped, lives shows no visible local advance. The interval records reach only, and settlement waits for return.

Test steps and diagnostics
Given

ordinary updates stopped with lives at its suspension snapshot state

When
  • the suspension interval advances and then return processing begins
Then
  • the away interval records clock reach only and shows no visible local advance
  • lives begins settlement only during return processing
Diagnostics
  • lives-refill-while-away-clock-readings
  • lives-refill-while-away-catch-up-trace

continuous-item-is-not-predicted

Row.id is neither predicted nor duplicated locally

scenarioper continuous-items row

Does not apply to lives-refill-while-away: continuous-items has no rows.

calendar-crossings-wait-for-return

Row.id crossings are discovered before settlement

scenarioper calendar-boundaries row

Does not apply to lives-refill-while-away: calendar-boundaries has no rows.

no-clock-return-path

the no-clock return path continues from fixed state

scenarioonce

Applies to lives-refill-while-away.

Without a clock row, this adoption measures no elapsed time, performs no local catch-up, creates no calendar crossing, and continues from unchanged fixed state at step 12. Where a clock row exists, this test asserts nothing.

Test steps and diagnostics
Given

the lives-refill-while-away adoption document and a return from suspension

When
  • return processing checks whether a clock row exists
Then
  • without a clock row, this adoption measures no elapsed time, performs no local catch-up, creates no calendar crossing, and continues from unchanged fixed state at step 12
  • where a clock row exists, this test asserts nothing
Diagnostics
  • lives-refill-while-away-return-timeline
  • lives-refill-while-away-fixed-state-trace

no-calendar-row-creates-no-crossing

the no-calendar path creates no crossing

scenarioonce

Applies to lives-refill-while-away.

Without a calendar-boundaries row, return settlement creates no calendar crossing. Where a row exists, this test asserts nothing.

Test steps and diagnostics
Given

the lives-refill-while-away adoption document and a return from suspension

When
  • return processing checks whether a calendar-boundaries row exists
Then
  • without a calendar-boundaries row, return settlement creates no calendar crossing
  • where a calendar-boundaries row exists, this test asserts nothing
Diagnostics
  • lives-refill-while-away-calendar-report
  • lives-refill-while-away-return-timeline

protected-clock-return-paths · life-clock

life-clock accepts forward time and skips negative time

scenarioper clock row

Applies to the life-clock row.

A non-negative life-clock interval uses the suspension snapshot and elapsed difference. A negative protected-clock interval keeps the loaded state, records zero elapsed time, skips steps 9 to 11, and creates no return reward. Device settings do not control this reading, so no rollback anchor or player-caused clock-change notice is selected.

Test steps and diagnostics
Given

two returns using life-clock, one with a non-negative difference and one after a protected-clock resync produces a negative difference

When
  • the return reading is sampled once in each run
Then
  • the non-negative run uses the suspension snapshot and the elapsed difference and proceeds through steps 9 to 11
  • the negative run keeps the loaded state, records zero elapsed time, skips steps 9 to 11, and creates no return reward
  • changing device settings does not change the reading supplied by this protected clock
  • no rollback anchor or player-caused clock-change notice is selected; the protected-clock resync branch keeps the loaded state
Diagnostics
  • lives-refill-while-away-clock-readings
  • lives-refill-while-away-return-timeline
  • lives-refill-while-away-reward-report

device-clock-non-negative-return

Row.id uses the device-clock difference on an ordinary return

scenarioper clock row

Does not apply to lives-refill-while-away: clock source is protected clock, not device clock.

device-clock-restores-suspension-snapshot

Row.id restores the suspension snapshot after a backward reading

scenarioper clock row

Does not apply to lives-refill-while-away: clock source is protected clock, not device clock.

device-clock-restores-trusted-checkpoint

Row.id restores the trusted checkpoint after a backward reading

scenarioper clock row

Does not apply to lives-refill-while-away: clock source is protected clock, not device clock.

device-clock-keeps-return-state

Row.id keeps the loaded return state after a backward reading

scenarioper clock row

Does not apply to lives-refill-while-away: clock source is protected clock, not device clock.

backward-clock-notice-silent

a backward device clock adds no explanation

scenarioonce

Does not apply to lives-refill-while-away: backward clock notice is not asked for lives-refill-while-away.

backward-clock-notice-explain

a backward device clock is explained before the return surface

scenarioonce

Does not apply to lives-refill-while-away: backward clock notice is not asked for lives-refill-while-away.

rollback-reading-protects-next-anchor

Row.id supplies one state and matching trusted reading

scenarioper rollback-rules row

Does not apply to lives-refill-while-away: rollback-rules has no rows.

backward-return-anchor-is-monotonic

a backward return commits an anchor that cannot replay earlier time

scenarioonce

Does not apply to lives-refill-while-away: clock source is protected clock, not device clock.

non-negative-catch-up-settles · lives

lives settles an ordinary non-negative interval

scenarioper catch-up-items row

Applies to the lives row.

A non-negative interval settles lives from the suspension snapshot and elapsed difference. The trace distinguishes this accepted interval from a skipped or zero-length one.

Test steps and diagnostics
Given

lives at a distinguishable suspension snapshot state and a non-negative elapsed difference

When
  • return processing builds this row's pass timeline
Then
  • lives settles from the suspension snapshot and elapsed difference
  • the return trace distinguishes this accepted interval from a skipped or zero-length interval
Diagnostics
  • lives-refill-while-away-return-timeline
  • lives-refill-while-away-catch-up-trace

whole-interval-limit

Row.id admits the whole measured interval

scenarioper catch-up-items row

Does not apply to lives-refill-while-away: no row in catch-up-items matches away-limit whole interval.

time-limit

Row.id admits no time past its cited point

scenarioper catch-up-items row

Does not apply to lives-refill-while-away: no row in catch-up-items matches away-limit time limit.

less-after-a-point-limit

Row.id uses its later amount rule past the cited point

scenarioper catch-up-items row

Does not apply to lives-refill-while-away: no row in catch-up-items matches away-limit less after a point.

state-ceiling-limit · lives

lives stops at its cited state ceiling

scenarioper catch-up-items row

Applies to the lives row.

lives stops at the ceiling at contracts.player-lives.max; the fidelity rule this row cites owns this ceiling; return processing derives the admitted span from it for crossing selection only, and the lifecycle does not re-apply it. Where the citation says the ordinary-play maximum applies, that is the ceiling used here. The test names the address and restates nothing from it.

Test steps and diagnostics
Given

a settlement for lives whose amount would move the state below, to, and past the ceiling at contracts.player-lives.max

When
  • the declared limit is applied and the admitted span is derived
Then
  • the row reaches but never passes that ceiling
  • the fidelity rule this row cites owns this ceiling; return processing derives the admitted span from it for crossing selection only, and the lifecycle does not re-apply it
  • where the citation says the same ceiling applies during ordinary play, that ordinary maximum is the one used here
  • the test names contracts.player-lives.max and restates nothing from it
Diagnostics
  • lives-refill-while-away-return-timeline
  • lives-refill-while-away-catch-up-report

same-as-playing-amount · lives

lives uses the ordinary amount for equal time

scenarioper catch-up-items row

Applies to the lives row.

For equal elapsed time, lives uses the ordinary-play amount. Fidelity and limits remain separate decisions in the report.

Test steps and diagnostics
Given

matching ordinary-play and admitted-away segments for lives with equal elapsed time and the same starting state

When
  • the amount for each segment is computed before fidelity is applied
Then
  • both segments define the same ordinary amount
  • fidelity and limits remain separate recorded decisions
Diagnostics
  • lives-refill-while-away-catch-up-report

reduced-amount

Row.id uses only its cited reduced amount rule

scenarioper catch-up-items row

Does not apply to lives-refill-while-away: no row in catch-up-items matches away-amount reduced.

away-rule-amount

Row.id uses only its cited away amount rule

scenarioper catch-up-items row

Does not apply to lives-refill-while-away: no row in catch-up-items matches away-amount away rule.

every-missed-update-fidelity

Row.id clips each segment by its limit, where one applies

scenarioper catch-up-items row

Does not apply to lives-refill-while-away: no row in catch-up-items matches catch-up-fidelity every missed update.

larger-steps-fidelity

Row.id gives amount and limit to its larger-step rule

scenarioper catch-up-items row

Does not apply to lives-refill-while-away: no row in catch-up-items matches catch-up-fidelity larger steps.

one-derived-change-fidelity · lives

lives gives amount and limit to its one-change rule

scenarioper catch-up-items row

Applies to the lives row.

lives.from-elapsed-time receives the separately computed amount and declared limit for lives, owns the limit, and returns one change. The lifecycle applies it once and does not re-apply the limit. The test names the address and restates nothing from it.

Test steps and diagnostics
Given

the separately computed amount and declared limit for lives

When
  • both are handed to lives.from-elapsed-time
Then
  • the cited rule owns the limit and returns one change
  • the lifecycle applies that change once and does not re-apply the limit
  • the test names lives.from-elapsed-time and restates nothing from it
Diagnostics
  • lives-refill-while-away-catch-up-trace
  • lives-refill-while-away-catch-up-report

away-rule-one-change-composition

Row.id keeps amount and one-change distribution separate

scenarioper catch-up-items row

Does not apply to lives-refill-while-away: no row in catch-up-items matches away-amount away rule and catch-up-fidelity one derived change.

calendar-every-crossing-in-order

Row.id settles every crossing in ascending order

scenarioper calendar-boundaries row

Does not apply to lives-refill-while-away: crossed calendar boundaries is not asked for lives-refill-while-away.

calendar-latest-crossing-only

Row.id settles only its latest crossing

scenarioper calendar-boundaries row

Does not apply to lives-refill-while-away: crossed calendar boundaries is not asked for lives-refill-while-away.

calendar-one-combined-crossing

Row.id combines its crossings into one return event

scenarioper calendar-boundaries row

Does not apply to lives-refill-while-away: crossed calendar boundaries is not asked for lives-refill-while-away.

segments-events-and-ties-stay-ordered

row order breaks equal-timestamp ties

scenarioonce

Applies to lives-refill-while-away.

Row order breaks equal-timestamp ties before Event resolution receives them. Where no equal-timestamp tie is constructible, this test asserts nothing.

Test steps and diagnostics
Given

equal-timestamp calendar events where the adoption can construct them

When
  • the return timeline orders the events before settlement
Then
  • row order breaks an equal-timestamp tie before Event resolution receives the events
  • where no equal-timestamp tie is constructible, this test asserts nothing
Diagnostics
  • lives-refill-while-away-return-timeline
  • lives-refill-while-away-calendar-report
  • lives-refill-while-away-catch-up-report

crossing-inside-admitted-span-settles-in-order

a crossing inside the admitted span for Row.id settles in order

scenarioper catch-up-items row

Does not apply to lives-refill-while-away: crossed calendar boundaries is not asked for lives-refill-while-away.

return-reward-created-from-source

Row.id is created only from its cited source settlement

scenarioper return-rewards row

Does not apply to lives-refill-while-away: return-rewards has no rows.

automatic-reward-delivery

Row.id is offered automatically in reward-row order

scenarioper return-rewards row

Does not apply to lives-refill-while-away: return-rewards has no rows.

player-claims-reward-delivery

Row.id stays pending until its cited claim event

scenarioper return-rewards row

Does not apply to lives-refill-while-away: return-rewards has no rows.

continuous-item-read-once-at-return

Row.id applies its authority reading once

scenarioper continuous-items row

Does not apply to lives-refill-while-away: continuous-items has no rows.

choice-item-applied-once-at-return

Row.id applies its selected branch once

scenarioper choice-items row

Does not apply to lives-refill-while-away: choice-items has no rows.

state-only-return-display

return adds no player-facing surface

scenarioonce

Does not apply to lives-refill-while-away: return display is visible before resume, not state only.

visible-before-resume-return-display

settled state is visible before ordinary play

scenarioonce

Applies to lives-refill-while-away.

After settled state and pending rewards are known, at least one existing player-facing display presents the affected state before ordinary play resumes. Any clock explanation comes first.

Test steps and diagnostics
Given

a return that affects at least one covered row

When
  • settled state and pending rewards are known
Then
  • at least one existing player-facing display presents the affected state before ordinary play resumes
  • where a clock explanation applies, it precedes this display update
Diagnostics
  • lives-refill-while-away-return-display-trace

return-summary-display

the return surface names each affected row and result

scenarioonce

Does not apply to lives-refill-while-away: return display is visible before resume, not return summary.

detailed-return-summary-display

the return surface explains each affected result

scenarioonce

Does not apply to lives-refill-while-away: return display is visible before resume, not detailed return summary.

final-state-and-anchor-commit-together

final state and the next anchor commit together

scenarioonce

Applies to lives-refill-while-away.

Final local state and the next suspension anchor commit together where an anchor exists; without one, that clause asserts nothing. Where an anchor exists, step 15 requires it never to be lower than the previous one on any return. Ordinary updates resume only after automatic return changes and required pre-resume displays settle.

Test steps and diagnostics
Given

a completed return with final local state and, where they exist, distinguishable automatic changes and required pre-resume displays

When
  • return processing reaches step 15
Then
  • final local state and the next suspension anchor commit together where an anchor exists; without one, the anchor clause asserts nothing
  • where an anchor exists, the committed anchor is never lower than the previous one on any return, as step 15 requires
  • ordinary updates resume only after every automatic return change and required pre-resume display has settled
Diagnostics
  • lives-refill-while-away-anchor-commit
  • lives-refill-while-away-return-display-trace

empty-row-sets-create-nothing

empty row sets create no suspension work

scenarioonce

Applies to lives-refill-while-away.

An empty fixed-items set fixes no covered state; an empty catch-up-items set calculates no covered state from missed time on return; an empty continuous-items set means no covered state continued elsewhere; an empty choice-items set selects no away behaviour at runtime; an empty return-rewards set creates no separately delivered reward; and an empty rollback-rules set uses no trusted checkpoint as a backward-clock anchor. Where a row exists, that clause asserts nothing.

Test steps and diagnostics
Given

the lives-refill-while-away adoption document and its optionally empty row sets

When
  • the lifecycle follows each row set's declared empty consequence
Then
  • where fixed-items is empty, no covered state is fixed by suspension; where a row exists, this clause asserts nothing
  • where catch-up-items is empty, no covered state is calculated from missed time on return; where a row exists, this clause asserts nothing
  • where continuous-items is empty, no covered state continued elsewhere during suspension; where a row exists, this clause asserts nothing
  • where choice-items is empty, no covered state selects between away behaviours at runtime; where a row exists, this clause asserts nothing
  • where return-rewards is empty, return settlement creates no separately delivered reward; where a row exists, this clause asserts nothing
  • where rollback-rules is empty, no trusted checkpoint is used as a backward-clock anchor; where a row exists, this clause asserts nothing
Diagnostics
  • lives-refill-while-away-declaration-record
  • lives-refill-while-away-return-timeline

catch-up-report-fields · lives

the catch-up report for lives carries the lifecycle fields

scenarioper catch-up-items row

Applies to the lives row.

Each catch-up report for lives records the interval readings, admitted elapsed time, row, amount, fidelity, limit, before and after state, and any crossing that split the segment.

Test steps and diagnostics
Given

each catch-up report produced for lives

When
  • a reviewer reads the report
Then
  • each report records interval start and end readings, elapsed time admitted, row id, amount branch, fidelity branch, limit reached, before state, after state, and the calendar crossing that split the segment when present
Diagnostics
  • lives-refill-while-away-catch-up-report

backward-clock-report-fields

each backward-clock report records its selected response

scenarioonce

Applies to lives-refill-while-away.

A backward-clock report records its selected anchor when one was selected and whether a notice appeared when that question applies. Where no such report exists, this test asserts nothing.

Test steps and diagnostics
Given

each backward-clock report produced by lives-refill-while-away

When
  • a reviewer reads the report
Then
  • the report records the selected anchor when one was selected and whether a notice appeared when the notice question applies
  • where this adoption produces no backward-clock report, this test asserts nothing
Diagnostics
  • lives-refill-while-away-clock-readings
  • lives-refill-while-away-anchor-commit

return-reward-report-fields

the return-reward report for Row.id carries its delivery fields

scenarioper return-rewards row

Does not apply to lives-refill-while-away: return-rewards has no rows.

trusted-checkpoint-row-shape

declaration check: last-trusted-checkpoint has exactly one rollback row

scenarioonce

Does not apply to lives-refill-while-away: backward clock anchor is not asked for lives-refill-while-away.

rollback-row-only-for-trusted-checkpoint

declaration check: rollback rows exist only for last-trusted-checkpoint

scenarioonce

Applies to lives-refill-while-away.

This declaration check reads the adoption and cites the rollback-rules description, not the running game. A rollback-rules row is legal only under the last-trusted-checkpoint anchor; that anchor's exactly-one duty is checked separately. A protected clock, another anchor, or an adoption with no clock row must have none, and an adoption with no clock row answers no anchor. Any other shape is a defect this test fails by construction.

Test steps and diagnostics
Given

the lives-refill-while-away adoption document and the rollback-rules description, not the running game

When
  • a reviewer compares its clock source, backward-clock anchor when answered, and rollback-rules rows
Then
  • a protected clock and each device-clock anchor other than last-trusted-checkpoint have no rollback-rules row
  • an adoption with no clock row answers no anchor and has no rollback-rules row
  • under last-trusted-checkpoint this check asserts nothing because the exactly-one check owns that shape
  • any other shape is a defect this test fails by construction
Diagnostics
  • lives-refill-while-away-declaration-record

at-most-one-clock-row

declaration check: clock has at most one row

scenarioonce

Applies to lives-refill-while-away.

This declaration check reads the adoption, not the running game. Clock has at most one row. More than one is a defect this test fails by construction. With no row, the check asserts nothing about elapsed time.

Test steps and diagnostics
Given

the lives-refill-while-away adoption document, not the running game

When
  • a reviewer counts clock rows
Then
  • the clock collection has zero or one row
  • more than one row is a defect this test fails by construction; with no clock row, the count duty still passes and this check asserts nothing about elapsed time
Diagnostics
  • lives-refill-while-away-declaration-record

at-most-one-rollback-row

declaration check: rollback-rules has at most one row

scenarioonce

Applies to lives-refill-while-away.

This declaration check reads the adoption, not the running game. Rollback-rules has at most one row. More than one is a defect this test fails by construction. With no row, the check asserts nothing about a trusted checkpoint.

Test steps and diagnostics
Given

the lives-refill-while-away adoption document, not the running game

When
  • a reviewer counts rollback-rules rows
Then
  • the rollback-rules collection has zero or one row
  • more than one row is a defect this test fails by construction; with no rollback row, this check asserts nothing about a trusted checkpoint
Diagnostics
  • lives-refill-while-away-declaration-record

player-claim-needs-return-surface

declaration check: Row.id has a claim-capable return surface

scenarioper return-rewards row

Does not apply to lives-refill-while-away: return-rewards has no rows.

clock-citation-duty · life-clock

declaration check: life-clock's clock citation states its duty

scenarioper clock row

Applies to the life-clock row.

This declaration check reads the adoption and time.account-time, not the running game. The citation must name life-clock's reading, unit, and sampling. A missing duty is a defect this test fails by construction. The test names the address and restates nothing from it.

Test steps and diagnostics
Given

the lives-refill-while-away adoption and the prose at time.account-time, not the running game

When
  • a reviewer checks the citation's meaning
Then
  • time.account-time names the reading, its unit, and when it is sampled
  • a missing duty is a defect this test fails by construction
Diagnostics
  • lives-refill-while-away-declaration-record

fixed-state-citation-duty

declaration check: Row.id's state citation names complete fixed state

scenarioper fixed-items row

Does not apply to lives-refill-while-away: fixed-items has no rows.

catch-up-state-citation-duty · lives

declaration check: lives's state citation names ordinary changes and unit

scenarioper catch-up-items row

Applies to the lives row.

This declaration check reads the adoption and mechanics.life-refill, not the running game. The citation must identify lives, its ordinary changes, and its unit. A missing duty is a defect this test fails by construction. The test names the address and restates nothing from it.

Test steps and diagnostics
Given

the lives-refill-while-away adoption and the prose at mechanics.life-refill, not the running game

When
  • a reviewer checks the citation's meaning
Then
  • mechanics.life-refill identifies lives, its ordinary changes, and its unit
  • a missing duty is a defect this test fails by construction
Diagnostics
  • lives-refill-while-away-declaration-record

amount-rule-citation-duty

declaration check: Row.id's amount citation states the complete amount and its unit

scenarioper catch-up-items row

Does not apply to lives-refill-while-away: no row in catch-up-items matches away-amount reduced or away rule.

fidelity-rule-citation-duty · lives

declaration check: lives's fidelity citation states its distribution

scenarioper catch-up-items row

Applies to the lives row.

This declaration check reads the adoption and lives.from-elapsed-time, not the running game. The citation must state the derivation of the single return change without changing the promised amount for lives. A missing duty is a defect this test fails by construction. The test names the address and restates nothing from it.

Test steps and diagnostics
Given

the lives-refill-while-away adoption and the prose at lives.from-elapsed-time, not the running game

When
  • a reviewer checks the citation's meaning
Then
  • lives.from-elapsed-time states the derivation of the single return change without changing the promised amount for lives
  • a missing duty is a defect this test fails by construction
Diagnostics
  • lives-refill-while-away-declaration-record

limit-rule-citation-duty · lives

declaration check: lives's limit citation states its complete boundary

scenarioper catch-up-items row

Applies to the lives row.

This declaration check reads the adoption and contracts.player-lives.max, not the running game. The citation must state the state ceiling that stops away gain for lives. A missing duty is a defect this test fails by construction. The test names the address and restates nothing from it.

Test steps and diagnostics
Given

the lives-refill-while-away adoption and the prose at contracts.player-lives.max, not the running game

When
  • a reviewer checks the citation's meaning
Then
  • contracts.player-lives.max states the state ceiling that stops away gain for lives
  • a missing duty is a defect this test fails by construction
Diagnostics
  • lives-refill-while-away-declaration-record

continuous-state-citation-duty

declaration check: Row.id's state citation supplies its return reading

scenarioper continuous-items row

Does not apply to lives-refill-while-away: continuous-items has no rows.

choice-rule-citation-duty

declaration check: Row.id's choice citation states both paths and selection

scenarioper choice-items row

Does not apply to lives-refill-while-away: choice-items has no rows.

reward-rule-citation-duty

declaration check: Row.id's reward citation states source and derivation

scenarioper return-rewards row

Does not apply to lives-refill-while-away: return-rewards has no rows.

destination-rule-citation-duty

declaration check: Row.id's destination citation states acceptance

scenarioper return-rewards row

Does not apply to lives-refill-while-away: return-rewards has no rows.

claim-rule-citation-duty

declaration check: Row.id's claim citation names one event

scenarioper return-rewards row

Does not apply to lives-refill-while-away: return-rewards has no rows.

expiry-rule-citation-duty

declaration check: Row.id's expiry citation states one deadline

scenarioper return-rewards row

Does not apply to lives-refill-while-away: return-rewards has no rows.

calendar-boundary-citation-duty

declaration check: Row.id's boundary citation states the calendar test

scenarioper calendar-boundaries row

Does not apply to lives-refill-while-away: calendar-boundaries has no rows.

calendar-settlement-citation-duty

declaration check: Row.id's settlement citation states immediate effects

scenarioper calendar-boundaries row

Does not apply to lives-refill-while-away: calendar-boundaries has no rows.

trusted-state-citation-duty

declaration check: Row.id's trusted-state citation supplies a matched pair

scenarioper rollback-rules row

Does not apply to lives-refill-while-away: rollback-rules has no rows.

away-limit-declaration-is-visible

declaration check: Row.id declares a state ceiling when one governs away gain

scenarioper catch-up-items row

Does not apply to lives-refill-while-away: no row in catch-up-items matches away-limit whole interval.

suspension-holds

suspension and catch-up hold for the whole run

generalonce

Applies to lives-refill-while-away.

Across every suspend-and-return cycle of the audited run, including a background transition, a backward device clock where the adoption has one, and a crossed calendar boundary where it has one, no elapsed time is counted twice; each catch-up row follows its amount, fidelity, and limit; each selected crossing settles once in order; each reward reaches its destination or stays pending; skipped intervals create no reward; and the next anchor never falls below the prior one. A case this adoption cannot construct asserts nothing.

Test steps and diagnostics
Holds

across every covered cycle, no elapsed time was counted twice; every catch-up row followed its amount, fidelity, and limit; every selected crossing settled once in its selected order; every created reward reached its destination or stayed pending as declared; every skipped interval created no reward; and each next anchor never fell below its prior anchor; a case the adoption cannot construct asserts nothing

Seeds

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

Scope

every suspend-and-return cycle of the audited run, including a background transition, a backward device clock where the adoption has one, and a crossed calendar boundary where it has one

Diagnostics
  • lives-refill-while-away-suspension-interval
  • lives-refill-while-away-return-timeline
  • lives-refill-while-away-catch-up-report
  • lives-refill-while-away-calendar-report
  • lives-refill-while-away-reward-report
  • lives-refill-while-away-anchor-commit
  • lives-refill-while-away-first-ordering-violation
JSONthe adoption as one file

The adoption file as supplied. Answers you try on this page are not written into it; to change an adoption, open it in the authoring tool.

{
  "contract": "suspension-and-catch-up",
  "version": 1,
  "summary": "Covers state that pauses, catches up, or continues while a game is closed or in the background. It also covers return rewards, calendar crossings, clock changes, and what appears before play resumes. Normal rates, timer rules, inventory capacity, save lifetime, and replay guarantees stay elsewhere.",
  "questions": {
    "while-in-the-background": {
      "asks": "What happens while the game stays open in the background?",
      "rationale": "An open game may use away handling, keep playing, or stay still until it closes. The same time must never count twice.",
      "options": {
        "counts-as-away": {
          "meaning": "Background time uses the same suspension rules as closed time. Ten minutes in another tab refills energy like ten minutes with the game closed.",
          "semantics": "The lifecycle treats the background interval as a suspension interval. Ordinary updates do not also apply that interval."
        },
        "keeps-playing": {
          "meaning": "Ordinary play continues in the background. A browser farm keeps growing crops while the player uses another tab.",
          "semantics": "The ordinary update path continues, including platform throttling. Foregrounding adds no catch-up for the same interval."
        },
        "waits-until-closed": {
          "meaning": "The open game stays still, and away progress starts only after closing. A crop timer pauses in the app switcher but advances after the app closes.",
          "semantics": "The open-background interval receives neither ordinary updates nor catch-up."
        }
      }
    },
    "return-display": {
      "asks": "What appears before ordinary play resumes?",
      "rationale": "Changed state may speak for itself, animate before play, or appear in a return summary.",
      "options": {
        "state-only": {
          "meaning": "No separate return surface appears. A match-3 game simply shows that three lives are ready.",
          "semantics": "The lifecycle adds no player-facing return surface. Existing game displays may show the settled state after ordinary play resumes. Developer or diagnostic instrumentation that is not player-facing is out of scope."
        },
        "visible-before-resume": {
          "meaning": "Existing displays show the change before play resumes. A life counter ticks from two lives to five on the map screen.",
          "semantics": "At least one existing player-facing display presents the affected state during return settlement."
        },
        "return-summary": {
          "meaning": "A return surface names each result. An idle game shows energy gained and coins waiting for collection.",
          "semantics": "A player-facing return surface identifies each affected row and its settled result."
        },
        "detailed-return-summary": {
          "meaning": "A return surface also explains the calculation. A factory screen shows six hours away, its energy ceiling, and the final gain.",
          "semantics": "The return surface reports elapsed time, applicable limits, amount rules, fidelity, and settled results."
        }
      }
    },
    "clock-source": {
      "asks": "Can the player change the clock that measures time away?",
      "rationale": "The player can move a device clock through settings. A protected clock stays outside those settings, though the service behind it can correct it.",
      "when": {
        "row-count": {
          "clock": "non-empty"
        }
      },
      "otherwise": "No clock row exists, so this adoption measures no elapsed suspension time.",
      "options": {
        "device-clock": {
          "meaning": "The game reads a clock from the device. Moving a phone clock forward by two hours gives an idle mine two more measured hours.",
          "semantics": "The elapsed interval comes from a clock the player can change through device settings. Both backward-clock questions are asked."
        },
        "protected-clock": {
          "meaning": "The game reads a clock outside device settings. Changing a phone clock does not refill a match-3 life sooner.",
          "semantics": "The elapsed interval comes from a clock the player cannot change through device settings. The backward-clock questions are not asked."
        }
      }
    },
    "backward-clock-anchor": {
      "asks": "If the player moves the device clock backward, which state does the game keep?",
      "rationale": "Zero elapsed time does not say which saved state survives a backward clock. The anchor prevents duplicate or reversed gains.",
      "when": {
        "flag": {
          "clock-source": [
            "device-clock"
          ]
        }
      },
      "otherwise": "No device clock is read, either because no clock row exists or because the clock is protected, so no rollback anchor is selected. A protected-clock resync may still move its reading backward; the lifecycle then keeps the loaded state.",
      "options": {
        "suspension-snapshot": {
          "meaning": "The game returns to the state captured when suspension began. An idle mine returns to the ore count recorded when the app closed.",
          "semantics": "Every in-scope local item is restored from the suspension snapshot. Elapsed time for this interval is zero."
        },
        "last-trusted-checkpoint": {
          "meaning": "The game returns to the latest state accepted by a trusted rule. An online workshop returns to its last server-confirmed stock count.",
          "semantics": "The one rollback rule supplies a trusted state and matching clock reading. The current suspension interval contributes zero elapsed time."
        },
        "keep-return-state": {
          "meaning": "The game keeps the state loaded at return but adds no away change. A newer cloud save from another device keeps its crops.",
          "semantics": "No state is restored by this contract. The loaded return state remains, and elapsed time for this interval is zero. That state may be newer than the suspension snapshot because of a cloud save, a second device, or a mid-session autosave."
        }
      }
    },
    "backward-clock-notice": {
      "asks": "Does the game explain the backward clock result?",
      "rationale": "The same backward-clock outcome can be silent or visible to the player.",
      "when": {
        "flag": {
          "clock-source": [
            "device-clock"
          ]
        }
      },
      "otherwise": "No device clock is read, either because no clock row exists or because the clock is protected, so no player-caused clock-change notice is selected. A protected-clock resync may still produce zero elapsed time.",
      "options": {
        "silent": {
          "meaning": "The game adds no clock-change notice. A life refill stays unchanged without a pop-up.",
          "semantics": "The lifecycle adds no player-facing explanation for the backward-clock branch."
        },
        "explain": {
          "meaning": "The game explains that the clock change blocked away time. An idle game shows a short clock-change message beside unchanged energy.",
          "semantics": "The lifecycle presents a player-facing clock-change explanation after the anchor is applied and before ordinary play resumes."
        }
      }
    },
    "crossed-calendar-boundaries": {
      "asks": "Several calendar boundaries passed while the game was away. Which ones count on return?",
      "rationale": "A daily or weekly rule may run for every crossing, only the latest one, or one combined return event.",
      "when": {
        "row-count": {
          "calendar-boundaries": "non-empty"
        }
      },
      "otherwise": "No calendar boundary is tracked, so return settlement creates no calendar crossing.",
      "options": {
        "every-crossing-in-order": {
          "meaning": "Every crossed boundary settles in time order. A farm processes Friday, Saturday, and Sunday crop resets separately.",
          "semantics": "The lifecycle inserts every crossed boundary into the return timeline in ascending time order."
        },
        "latest-crossing-only": {
          "meaning": "Only the latest crossed boundary settles. A daily reward calendar opens on today's entry without granting missed days.",
          "semantics": "The lifecycle inserts only the latest crossed boundary for each calendar row and omits earlier crossings."
        },
        "one-combined-crossing": {
          "meaning": "All crossings become one return event. A weekly shop refreshes once and receives the number of missed weeks.",
          "semantics": "The lifecycle inserts one return-time event per calendar row, carrying that row's number and span of crossed boundaries."
        }
      }
    }
  },
  "declares": {
    "values": {},
    "rows": {
      "clock": {
        "description": "The elapsed-time rule for this behaviour family. At most one row is valid.",
        "when-empty": "No elapsed suspension time is measured by this adoption.",
        "record": {
          "id": {
            "type": "string",
            "required": true,
            "pattern": "kebab-case",
            "unique": true,
            "description": "A stable name for the elapsed clock, such as away-clock."
          },
          "clock-declared-in": {
            "type": "citation",
            "required": true,
            "description": "The game rule naming the reading, its unit, and when it is sampled."
          }
        }
      },
      "fixed-items": {
        "description": "State that remains at its suspension anchor until ordinary play resumes.",
        "when-empty": "No covered state is fixed by suspension.",
        "record": {
          "id": {
            "type": "string",
            "required": true,
            "pattern": "kebab-case",
            "unique": true,
            "description": "The state item in game language, such as round-score or player-position."
          },
          "state-declared-in": {
            "type": "citation",
            "required": true,
            "description": "The game rule that identifies the complete state held fixed."
          }
        }
      },
      "catch-up-items": {
        "description": "State calculated from elapsed time when the suspension interval returns.",
        "when-empty": "No covered state is calculated from missed time on return.",
        "record": {
          "id": {
            "type": "string",
            "required": true,
            "pattern": "kebab-case",
            "unique": true,
            "description": "The state item in game language, such as energy, crops, or lives."
          },
          "state-declared-in": {
            "type": "citation",
            "required": true,
            "description": "The game rule identifying the state, its ordinary changes, and its unit."
          },
          "away-amount": {
            "type": "string",
            "required": true,
            "options": [
              "same-as-playing",
              "reduced",
              "away-rule"
            ],
            "description": "The amount earned from equal elapsed time: the ordinary amount, a lower amount, or an amount from a separate away rule. An energy refill may keep its normal one point per minute."
          },
          "amount-rule-declared-in": {
            "type": "citation",
            "when": {
              "row": {
                "away-amount": [
                  "reduced",
                  "away-rule"
                ]
              }
            },
            "description": "The rule for the lower or separate away amount. It states the complete amount calculation and its unit. Only the cited rule binds this row; uncited game prose does not."
          },
          "catch-up-fidelity": {
            "type": "string",
            "required": true,
            "options": [
              "every-missed-update",
              "larger-steps",
              "one-derived-change"
            ],
            "description": "How missed play changes this state between calendar events: every missed update, larger catch-up steps, or one derived change. A crop may replay every growth tick while a life refill may derive one total."
          },
          "fidelity-rule-declared-in": {
            "type": "citation",
            "when": {
              "row": {
                "catch-up-fidelity": [
                  "larger-steps",
                  "one-derived-change"
                ]
              }
            },
            "description": "The rule naming step size and order, or the rule deriving the single return change. It distributes the promised amount and does not change it. Only the cited rule binds this row; uncited game prose does not."
          },
          "away-limit": {
            "type": "string",
            "required": true,
            "options": [
              "whole-interval",
              "time-limit",
              "less-after-a-point",
              "state-ceiling"
            ],
            "description": "How much time or gain is allowed away: all time, a hard time stop, a lower amount past a point, or a state ceiling. A normal full-energy maximum is a valid state ceiling. A maximum that limits away gain is declared here as a state ceiling, not left under whole-interval."
          },
          "limit-rule-declared-in": {
            "type": "citation",
            "when": {
              "row": {
                "away-limit": [
                  "time-limit",
                  "less-after-a-point",
                  "state-ceiling"
                ]
              }
            },
            "description": "The time point, later amount rule, or state ceiling. A state ceiling may be the same maximum that applies during ordinary play. Only the cited rule binds this row; uncited game prose does not."
          }
        }
      },
      "continuous-items": {
        "description": "State that continued under another authority while the local game was suspended.",
        "when-empty": "No covered state continued elsewhere during suspension.",
        "record": {
          "id": {
            "type": "string",
            "required": true,
            "pattern": "kebab-case",
            "unique": true,
            "description": "The state item in game language, such as skill-training or life-service."
          },
          "state-declared-in": {
            "type": "citation",
            "required": true,
            "description": "The external or protected rule that continued the state and supplies its return reading."
          }
        }
      },
      "choice-items": {
        "description": "State whose away behaviour is selected by a setting, platform, unlock, or other game rule.",
        "when-empty": "No covered state selects between away behaviours at runtime.",
        "record": {
          "id": {
            "type": "string",
            "required": true,
            "pattern": "kebab-case",
            "unique": true,
            "description": "The state item in game language, such as harvest-mode."
          },
          "choice-declared-in": {
            "type": "citation",
            "required": true,
            "description": "The game rule naming both possible behaviours, their complete data, the condition selecting one, and the time at which the selection is made."
          }
        }
      },
      "return-rewards": {
        "description": "Rewards created by catch-up or a crossed calendar boundary, then delivered or left for the player to claim.",
        "when-empty": "Return settlement creates no separately delivered reward.",
        "record": {
          "id": {
            "type": "string",
            "required": true,
            "pattern": "kebab-case",
            "unique": true,
            "description": "The reward in game language, such as offline-coins or daily-gift."
          },
          "reward-declared-in": {
            "type": "citation",
            "required": true,
            "description": "The game rule deriving the reward and identifying its source settlement."
          },
          "destination-declared-in": {
            "type": "citation",
            "required": true,
            "description": "The destination rule that accepts the complete reward or leaves it pending."
          },
          "delivery": {
            "type": "string",
            "required": true,
            "options": [
              "automatic",
              "player-claims"
            ],
            "description": "Whether this reward enters its destination automatically or waits on a return surface for the player to claim it. A player-claims reward requires a return-display answer that supplies a return surface: return-summary or detailed-return-summary."
          },
          "claim-declared-in": {
            "type": "citation",
            "when": {
              "row": {
                "delivery": [
                  "player-claims"
                ]
              }
            },
            "description": "The player event that claims this pending reward from the return surface."
          },
          "expires-declared-in": {
            "type": "citation",
            "required": false,
            "description": "The optional rule naming when an unclaimed reward expires and is removed. Omission means an unclaimed reward never expires."
          }
        }
      },
      "calendar-boundaries": {
        "description": "Daily, weekly, seasonal, or other calendar boundaries that may pass during suspension.",
        "when-empty": "Return settlement creates no calendar crossing.",
        "record": {
          "id": {
            "type": "string",
            "required": true,
            "pattern": "kebab-case",
            "unique": true,
            "description": "The boundary in game language, such as daily-reset or new-week."
          },
          "boundary-declared-in": {
            "type": "citation",
            "required": true,
            "description": "The rule naming the calendar, time zone, boundary test, exact crossing time, and what happens when local civil time skips or repeats a boundary."
          },
          "settlement-declared-in": {
            "type": "citation",
            "required": true,
            "description": "The game rule that receives each selected crossing and states its immediate effects."
          }
        }
      },
      "rollback-rules": {
        "description": "The trusted checkpoint rule used by the backward-clock checkpoint answer. Exactly one row under last-trusted-checkpoint; no row under any other anchor, a protected clock, or no clock.",
        "when-empty": "No trusted checkpoint is used as a backward-clock anchor.",
        "record": {
          "id": {
            "type": "string",
            "required": true,
            "pattern": "kebab-case",
            "unique": true,
            "description": "A stable name for the trusted checkpoint rule."
          },
          "trusted-state-declared-in": {
            "type": "citation",
            "required": true,
            "description": "The rule that supplies one coherent state snapshot and its matching trusted clock reading."
          }
        }
      }
    }
  },
  "origin": "https://opengdd.org/contracts/suspension-and-catch-up-1",
  "mechanism": [
    "This is the one normative authority for suspension state, elapsed time, catch-up, calendar crossings, return rewards, and report order. Questions and rows supply choices and game rules to this lifecycle; they do not define a second sequence.",
    "A **suspension interval** begins at game closure, or at the earlier open-background transition selected by `counts-as-away`, and ends at return processing, at the return clock reading when a clock row exists. A **suspension snapshot** contains every in-scope local item and the starting clock reading. A **return timeline** is the ordered elapsed span, split at the calendar crossings selected by the calendar answer. A **pending reward** has been created but not accepted by its destination.",
    "An unclaimed reward with an expiry rule is removed at its cited deadline. The lifecycle checks that deadline at return and immediately before a claim.",
    "### Suspend",
    "1. **Closing the game always begins a suspension interval.** Route an earlier open-background transition through `while-in-the-background`. `counts-as-away` begins the interval at that transition. `keeps-playing` continues ordinary play and creates no return catch-up for background time. `waits-until-closed` holds state unchanged until closure begins the interval. 2. When an interval begins, capture one coherent suspension snapshot before any covered state can change. If a clock row exists, read its starting value in the same batch; the interval's starting value is the higher of that reading and the committed anchor from the previous return, so time below the anchor is never replayed. A protected authority may keep changing a `continuous-items` row after the snapshot. 3. Hold every fixed and catch-up item at its snapshot state. A choice row resolves its cited selection at the time named by that rule. A selected continuous branch stays under its cited authority; other selected branches follow their matching lifecycle paths.",
    "### While away",
    "4. Local catch-up items do not advance visibly while ordinary updates are stopped. The interval records clock reach only; settlement waits for return. 5. Continuous items keep changing under their cited authority. This lifecycle neither predicts those changes nor duplicates them locally. 6. Calendar crossings are discovered from the cited calendar and clock. They are not settled yet. Return processing selects and orders them.",
    "### Return",
    "7. If a clock row exists, read the return clock once. A non-negative interval uses the suspension snapshot and the elapsed difference. Any negative protected-clock interval counts as zero elapsed with the loaded state retained, because a service resync can move even a protected clock back; skip steps 9–11 and continue at step 12. That skipped interval creates no return reward. Without a clock row, local catch-up and calendar crossings are absent; continue with the unchanged fixed state at step 12. 8. If a device clock reads earlier, apply `backward-clock-anchor` before any catch-up. Restore the suspension snapshot, restore the cited trusted checkpoint, or keep the loaded return state. The current interval then has zero elapsed time. A skipped interval creates no return reward. Apply the selected clock notice, skip steps 9–11, and continue at step 12. 9. Build one calendar return timeline and one pass timeline for each catch-up row. A calendar row admits the full non-negative measured interval because it declares no away limit. For a catch-up row, this lifecycle derives the admitted span from the row's declared limit for crossing selection only; that derivation is not the limit application step 10 assigns, and a state-dependent ceiling discovers its endpoint during the chronological pass. Crossings inside that admitted span settle against the row, and crossings beyond it do not. A state-dependent ceiling discovers its endpoint during the chronological pass, so no later crossing enters that row's pass. A catch-up row's limit does not cancel an independent calendar event: the row shape declares no such link, and the event settles once on the calendar timeline. `every-crossing-in-order` keeps all remaining crossings in ascending time order. `latest-crossing-only` keeps the last remaining crossing for each calendar row. `one-combined-crossing` creates one return-time event per calendar row with its count and span. Row order breaks a tie between equal timestamps before **Event resolution** receives them. 10. Compute each catch-up row's segments as step 11 hands them over, keeping amount separate from fidelity; step 11 owns the ordering. `same-as-playing` defines the ordinary amount for equal elapsed time; `reduced` and `away-rule` use only their cited amount rule. Under `every-missed-update`, this lifecycle applies the away limit as a per-segment clip and then feeds only the remaining missed updates to the game rules. Under `larger-steps`, the cited fidelity rule receives the amount and limit, owns both the larger-step distribution and the limit, and returns the ordered changes. Under `one-derived-change`, the cited fidelity rule receives the amount and limit, owns the limit, and returns one change. The lifecycle does not re-apply a limit owned by a cited fidelity rule. When `away-rule` and `one-derived-change` compose, the amount rule defines the total and the fidelity rule distributes it. 11. Settle each selected calendar event once through `settlement-declared-in`. For every catch-up row containing that crossing, settle the preceding segment first, then the event's immediate effect, then the following segment. The row's clip already excluded later crossings, so this order cannot re-admit time or events beyond its limit. Reports keep the same segment-then-event-then-segment order. 12. Check every existing pending reward's cited deadline at return. An expired unclaimed reward is removed at that deadline. A pending reward is in-scope local state and survives this and later suspensions until the player claims it or its cited deadline expires. 13. Read every continuous item once. Apply each selected choice branch once. Create each return reward from its cited source settlement; a reward row with no settled source produces nothing. A skipped interval therefore creates no return reward. Check a new reward's cited deadline at return. Offer an `automatic` reward to its destination in reward-row order. A `player-claims` reward remains pending until its cited claim event. Destination refusal keeps the same pending reward, and a refused `automatic` reward is offered again at each later return until its destination accepts it or its cited deadline removes it. 14. Present `return-display` after settled state and pending rewards are known. A clock explanation precedes the general return surface. Existing displays used by `visible-before-resume` update before ordinary play. A claim event checks the cited deadline before offering its reward. An expired unclaimed reward is removed; each accepted reward is reported once. 15. Commit the final local state and the next suspension anchor together. On every return the committed anchor is never lower than the previous one. After any backward-clock return, commit the highest of the suspension reading, the return reading, and any trusted-checkpoint reading as the next suspension anchor, retaining the trusted state under that anchor, so repeated clock rollback cannot replay time below the prior anchor. Resume ordinary updates only after all automatic return changes and every required pre-resume display have settled. A later player claim is a new event and never repeats catch-up.",
    "Every catch-up report carries the interval start and end readings, elapsed time admitted, row id, amount branch, fidelity branch, limit reached, before state, after state, and calendar crossing that split the segment when present. A backward-clock report also carries the selected anchor when one was selected, and whether a notice appeared when the notice question applies. A return-reward report carries its source, destination, delivery, pending, accepted, or expired status, deadline when present, and claim event when present."
  ],
  "answers": {
    "while-in-the-background": "counts-as-away",
    "return-display": "visible-before-resume",
    "clock-source": "protected-clock"
  },
  "values": {},
  "rows": {
    "clock": [
      {
        "id": "life-clock",
        "clock-declared-in": "time.account-time"
      }
    ],
    "catch-up-items": [
      {
        "id": "lives",
        "state-declared-in": "mechanics.life-refill",
        "away-amount": "same-as-playing",
        "catch-up-fidelity": "one-derived-change",
        "fidelity-rule-declared-in": "lives.from-elapsed-time",
        "away-limit": "state-ceiling",
        "limit-rule-declared-in": "contracts.player-lives.max"
      }
    ],
    "fixed-items": [],
    "continuous-items": [],
    "choice-items": [],
    "return-rewards": [],
    "calendar-boundaries": [],
    "rollback-rules": []
  },
  "pack": "sha256:d1ec8edb7c86cda9f923cacba4a1d4a0c6c4af5ee4302842865d39636cd8a81d"
}