Files
aetherbound-guild/docs/product/SAVE_AND_FAILURE_CONTRACT.md

33 KiB

Aetherbound Guild: Save And Failure Contract

Authority: versioned persistence, pending/applied operations, exactly-once commit, retry, duplicate protection, deterministic battle resume, Recruit death, dismissal, run failure, backup, corruption, migration, rollback, cloud conflict, clock behavior, bounded offline work, delete, and recovery UX.

Revision: recruit-line-design-r2 / 2026-08-10

GAME_PRODUCT_CONTRACT.md owns the player promise. SYSTEMS_AND_BATTLE.md owns battle state and casualty predicates. ECONOMY_AND_BALANCE.md owns normal prices, rewards, pacing, and difficulty multipliers. This document is final authority for operation and gameplay-failure application.

1. Persistence Invariants

  1. A confirmed operation applies exactly once or not at all. Cost, result, receipt, and final status are one committed state.
  2. A durable pending row contains immutable intent and zero gameplay deltas. It can be safely finalized or rejected; it can never contain a spent cost.
  3. Suspend, crash, power loss, retry, duplicate input, device change, or cloud transport cannot redraw a Market/reward, duplicate an item/Renown grant, reverse a valid casualty, or change a deterministic battle.
  4. The newest valid local commit remains fully playable offline. Platform and cloud copies never become gameplay authority silently.
  5. A Fallen Recruit never returns to the same run through reload, backup choice, rehearsal, migration, reward, promotion, or clock manipulation.
  6. Death removes the Recruit but returns exactly the Recruit's equipped item IDs to run inventory. Neither half of that result can commit alone.
  7. Offline gameplay advances by zero simulation ticks and grants zero Coin, XP, Renown, offers, healing, or progress.
  8. Migration is copy-first, deterministic, sequential, and reversible until a later verified boundary. It never rerolls generated Recruit fields.
  9. Corruption recovery identifies the exact verified boundary. If later irreversible run operations cannot be reconstructed, the run is sealed rather than resumed from a state that could reverse death or duplicate value.
  10. Save, conflict, recovery, overwrite, abandon, dismissal, and delete flows support touch, mouse, keyboard, controller, localization, and accessibility.

2. Save Topology And Envelope

The product supports three independent Guild slots. Each slot holds one active commit, four rotating verified backups, one run-start checkpoint, one latest Market checkpoint, one optional pre-migration copy, and redundant closure receipt ledgers.

profile_save
  profile_schema_version
  settings_revision, language, accessibility, input mappings
  Guild_slot_index[]
  platform_service_outbox (non-authoritative, bounded)

Guild_slot
  Guild_id, lineage_id, slot_id
  save_schema_version, ruleset_version, content_revision
  commit_id, parent_commit_id, commit_sequence
  generation_root_seed
  Region/Contract progress, Renown, unlocks, Records, discoveries
  current_run or null
  operation_journal, compact_applied_ids
  closure_receipt_head
  payload_sha256, envelope_crc32

current_run
  run_id, Contract, difficulty, run_seed, status
  Market/cleared-Round/boss state
  Coin, Replacement Credit, capacity, offers, locks, refresh counters
  living Recruits, Reserve, inventory, Artifacts
  encounter or null, pending outcome/reward or null
  hired/fallen/dismissed totals

IDs and seeds are 128-bit values rendered as lowercase hexadecimal. Commit sequence is a local monotonic unsigned integer. CRC32 catches incomplete media writes; SHA-256 covers canonical payload bytes. Checksums are integrity tools, not online signatures or anti-cheat requirements.

Wall-clock fields may be stored for display and diagnostics only. They cannot select conflict winners or calculate gameplay.

3. Versioning And Compatibility

Field Changes when Compatibility rule
profile_schema_version settings/slot index layout changes profile-only ordered migration
save_schema_version Guild/run/operation serialization changes sequential copy migration required
ruleset_version combat, target, price, failure, or progression formula changes committed battle finishes under stored version
content_revision stable catalog row or localization data changes stable-ID mapping or explicit legacy handling

The current schema and previous three shipped schemas must have tested adjacent migrations. Older schemas may have a longer supported chain; absence of a chain blocks that slot with export/recovery options and never authorizes reset.

A battle, reward draft, Market board, or outcome already committed stays on its stored ruleset/content snapshot. A new ruleset becomes active only before a new Market is generated, after all prior result operations are finalized.

No shipped gameplay save exists during this design-only stage. These rules bind all later prototype and production schemas and do not imply a current runtime migration obligation.

4. Operation Identity And Status

Every state-changing action has:

operation_id = hash(Guild_id, lineage_id, commit_sequence_at_intent,
                    operation_kind, source_stable_id, local_nonce)
intent_hash = sha256(canonical_operation_inputs)
status: pending | applied | rejected

The journal row stores:

operation_id, operation_kind, intent_hash, status
pre_commit_id, result_commit_id
cost_delta, result_delta, rejection_reason
run_id, Market_index, encounter_id, simulation_tick as applicable
receipt_hash, previous_closure_receipt_hash as applicable

Rules for duplicate IDs:

  • same operation_id and same intent_hash: return stored pending status, applied receipt, or rejection without creating a new operation;
  • same ID and different hash: reject OPERATION_ID_CONFLICT, applying nothing;
  • new ID: validate and enter the transaction procedure below.

Applied IDs remain in the full journal through run close. They then compact into an immutable membership set retained for the Guild lineage. Receipt-bearing casualty, reward, boss-clear, Renown, run-close, migration, and delete operations remain in the redundant closure ledger until the Guild slot is deleted.

The detailed journal is bounded to 4,096 rows. At a Market with no pending operation, reaching 3,584 rows compacts the oldest finalized non-closure rows into sorted exact-ID segments containing operation ID, intent hash, final status, and receipt hash. The newest 512 detailed rows and every closure receipt remain expanded. Lookup checks exact segments before accepting a new ID; probabilistic membership is forbidden because a false match could reject a valid purchase or hide a duplicate.

5. Pending-To-Applied Transaction Procedure

Every durable transaction uses these steps:

  1. Look up ID/hash before current-state validation and return any existing row.
  2. Validate the active commit, legal phase, referenced IDs, target, and intent.
  3. Build a candidate child commit containing a pending row with the complete immutable intent and zero cost_delta/result_delta.
  4. Validate, write, flush, atomically point to, and read back that pending child.
  5. From the pending intent and unchanged parent facts, compute the complete cost and result. No new random draw is allowed; generated outputs were fixed in intent or derive from stored labeled seeds.
  6. Build one child that replaces pending with applied, includes all cost and result deltas, receipt, and closure link when required.
  7. Validate schema, nonnegative resources, unique ownership, Recruit/equipment consistency, Market lineage, battle hashes, and operation transition.
  8. Serialize to a new file, write integrity values, flush file and directory, atomically replace the active pointer, then read back.
  9. Rotate backups only after the applied child reads back and validates.

A precondition failure after step 3 produces a rejected child with zero deltas and one reason. It cannot return to pending. Presentation, sound, achievement, cloud upload, and screen transition occur only after step 9.

5.1 Crash And Retry Boundaries

Crash point Reachable active state Recovery/retry
Before pending pointer switch Parent only retry creates same intent; no cost exists
After pending switch Pending intent, zero deltas deterministic finalize or reject once
During applied pointer switch Pending or complete applied child validate selected pointer; finalize or return receipt
After applied switch, before backup rotation Complete applied child return stored receipt; never reapply
During backup/closure replication Applied active plus older replicas repair replicas from active receipt; no gameplay replay

Recovery may finalize a pending buy, casualty, reward, or run-close operation only from its fixed intent. It never asks the player to redraw or reconfirm a result whose confirm was already durably recorded. A pending destructive action that lacks a valid target after recovery becomes rejected with no deltas.

6. Transaction Boundaries

Action Pending intent contains Applied child contains
Buy Recruit offer ID, recruit output hash, Credit choice, expected price Coin/Credit cost, offer removal, Recruit ownership
Buy/sell equipment offer/item ID and expected receipt Coin delta and exactly one ownership change
Lock/refresh Market hash, selected locks, expected cost Coin cost, counters, complete fixed offer board
Equip/reorder Recruit/item/order IDs complete legal inventory and Party-line state
Promote Recruit, old/new Profession, eligibility proof one Profession replacement and derived-state refresh
Dismiss Recruit, equipment set, rebate preview, expected live_recruit_count Recruit removal, equipment return, Coin rebate, post-count at least one
Commit battle battle offer, lineup, stats, seed, risk immutable encounter start snapshot
Start retreat encounter, accepted tick one retreat timestamp; no duplicate start
Lock outcome objective and final state hashes immutable victory/defeat/timeout/retreat result
Apply casualties exact Recruit/equipment/Credit set all Recruit removals, all item returns, all Credit grants
Claim reward fixed draft/card/disposition one reward and draft removal
Close run terminal cause and persistent grants Coin/Credit clear, run result, Records/Renown once
Change difficulty/law Guild-only selected rule set next-run configuration only

An operation cannot use a presentation animation as a commit boundary. A double-tap, key repeat, controller repeat, OS lifecycle retry, or cloud retry returns the same status/receipt.

7. Market, Round, And Run Save Points

Safe commits occur after:

  • run creation and provisional Recruit selection;
  • Market generation, lock, refresh, buy, sell, equip, reorder, promote, and dismissal;
  • battle offer commit and countdown start;
  • accepted retreat start;
  • encounter outcome lock;
  • casualty application;
  • reward claim or failure application;
  • capacity increase, boss Muster, boss result, and run close.

Save and Quit completes the current tick/transaction and points to the same active commit. It does not create a selectable branch or reroll point. Loading a Market returns the exact offers, locks, refresh counter, Coin, Credit, Company, Reserve, inventory, and order.

Failed attempts increment Market_index but not cleared_rounds. The next Market still prepares the same required Round number with a new precommitted board from the run seed. A boss failure reopens boss Muster with the same fixed boss and newly derived Recruit/equipment offers. This progression cannot be reset by closing or restoring a player-selectable save.

8. Battle Interruption And Deterministic Resume

An encounter snapshot stores:

encounter_id, ruleset_version, content_revision, root_seed
simulation_tick and per-label draw counters
ordered actor IDs/footprints, fixed-point HP/stats/resources/readiness
casts, projectiles, movement intents, summons, statuses, barriers, cooldowns
objective/phase/Overtime/retreat state
automatic event journal and accepted lifecycle commands
snapshot_hash, previous_snapshot_hash

The game writes a resumable snapshot every ten ticks, at every accepted retreat command, after every Fallen/collapse batch, and at outcome lock. On normal suspend it completes the current priority batch before acknowledging suspend.

If killed earlier, recovery:

  1. loads the newest valid encounter snapshot;
  2. replays deterministic automatic events and accepted lifecycle commands from the previous valid hash to the last journaled tick;
  3. verifies all labeled draw counters and state hashes;
  4. resumes at the first unprocessed priority of the next tick.

No wall time is substituted. A week-long suspend resumes the same tick. Camera, animation, particles, and audio may restart; authoritative events may not.

If all encounter snapshots are corrupt but the committed battle start and event journal validate, replay from countdown using the same seed and retreat tick. If replay hash diverges, seal the run as recovery_incomplete under Section 12; never offer a fresh seed or pre-battle edit after observed casualties.

9. Recruit Death, Equipment Return, And Dismissal

9.1 Casualty Settlement

Fallen state exists in battle snapshots as soon as the death batch resolves. Run ownership changes only in the casualty settlement after outcome lock. The pending intent lists, for every casualty:

recruit_id, final Profession/level/Trait/history hash
equipped item IDs
original list price, Coin paid, computed Credit grant
death tick and causal event ID

Applied settlement must satisfy:

all casualty Recruit IDs absent from living Company and Reserve
all casualty equipment IDs present exactly once in run inventory
Replacement Credit equals the capped sum
fallen total increases by exact casualty count
no non-casualty Recruit or item changes ownership

If any condition fails, the candidate is rejected and the outcome remains locked pending recovery. The game cannot enter the next Market with a partial casualty result.

Casualties apply on victory, defeat, timeout, and retreat. A same-batch mutual defeat does not preserve either Fallen Recruit. Rehearsal simulations create no operation and cannot affect ownership.

9.2 Dismissal

Dismissal is an explicit destructive operation from a Market. The pending review shows generated name, Profession, Trait, level, survival history, returned equipment, Coin rebate, resulting deployable footprint, and no Credit grant. Default focus is cancel. Both preview and commit use the product authority's canonical count:

live_recruit_count = count(unique Recruit IDs owned by the current run
                           whose state is living,
                           across deployed party and Reserve)
dismissal_legal = Market_open and target_is_living and
                  live_recruit_count >= 2

Dismissal is rejected for a Recruit referenced by a committed battle, pending promotion, pending casualty settlement, or another dismissal. It is also rejected as FINAL_LIVE_RECRUIT when the canonical count is one.

Preview rejection creates no operation ID, pending row, or save mutation. A crafted or stale confirm is revalidated before a pending gameplay operation can apply. Its FINAL_LIVE_RECRUIT response has zero cost/result deltas: no Recruit or equipment moves, no Coin rebate, no Credit, no protected recovery offer, no dismissed-total change, and no commit-sequence advance. The implementation may retain only the rejected intent response keyed by operation ID and intent hash so duplicate confirms return the same response; this is not an applied/pending gameplay receipt and cannot later become legal after the Company changes.

A pending dismissal recovered after interruption stores its parent commit and expected count. Before transition to applied, it must prove the active parent is unchanged, pre-count is at least two, and post-count is exactly pre-count minus one and at least one. Otherwise it becomes the same zero-mutation rejected intent response. A successfully applied dismissal returns its original receipt on duplicate confirm and cannot remove another Recruit or pay a second rebate.

Explicit Abandon Run is a separate run-close operation; it does not reuse a dismissal ID or pay a rebate. Casualty settlement is also separate and remains the only path by which a final Recruit's death can create Credit, a protected offer, recovery viability evaluation, or Company collapse.

9.3 No Resurrection Boundary

Applied casualty receipts are closure receipts. An older backup that predates a casualty can become active only after the receipt chain deterministically replays through that casualty or after the affected run is sealed. A recovery UI must never present "continue run" from an older state containing a Recruit whose later valid casualty receipt is known.

10. Duplicate Purchase, Reward, And Persistent Grant Protection

UI debounce is optional convenience; operation IDs are authority.

For purchase:

if operation applied: return stored Recruit/item and receipt
if pending: finalize same fixed offer and price
if offer absent under a new operation ID: reject OFFER_ALREADY_CONSUMED

For reward:

verify card ID belongs to stored draft
pending contains exact selected card and replacement choice if required
applied removes draft and adds one item/Artifact/Coin result

For Renown, clear, and discovery facts, the stable grant ID is derived from Guild ID plus objective/content ID. A repeated boss outcome or platform retry finds the existing applied ID and grants nothing again.

Optional platform achievements derive from applied Records. The outbox holds at most 100 idempotent events. When full, older events are regenerated from Records rather than blocking play or changing gameplay resources.

11. Backup, Corruption, And Closure Receipts

11.1 Backup Set

Each Guild slot stores:

  • active: newest validated commit;
  • backup_1..4: distinct prior verified transaction boundaries;
  • run_start: current run creation commit until run close;
  • Market_checkpoint: latest Market-open commit;
  • pre_migration: source bytes before the latest migration;
  • closure_A and closure_B: alternating append-only closure receipt ledgers;
  • quarantine: corrupt bytes and diagnostics, never auto-deleted.

Backups rotate only after active read-back. Storage pressure removes diagnostic logs before any active, closure, run-start, Market, or pre-migration copy.

11.2 Validation Order

  1. envelope parse and size bounds;
  2. CRC32 and SHA-256;
  3. schema and required fields;
  4. Guild/lineage/parent/sequence links;
  5. operation status transitions and receipt chain;
  6. nonnegative Coin/Credit/Renown and cap checks;
  7. Recruit uniqueness, generated output hashes, and Profession topology;
  8. equipment ownership uniqueness and equip references;
  9. Market seed ancestry, offers, locks, and counters;
  10. encounter snapshot, event journal, draw counters, and outcome;
  11. persistent clear/discovery IDs and run-close consistency.

A settings error falls back to defaults without touching gameplay. Optional cloud/platform failure never labels a local save corrupt.

11.3 Recovery Algorithm

  1. Validate active, then backups newest to oldest.
  2. Validate both closure ledgers and choose the longest common valid chain.
  3. Select the newest valid gameplay commit whose ancestry matches the slot.
  4. Replay later closure receipts only when parent/result hashes form a complete chain and each result validates.
  5. If the chain reconstructs the newer state, write a recovered child and continue normally.
  6. If a missing/corrupt gap crosses a casualty, reward, boss clear, Renown grant, or run close, restore persistent Guild state only through the verified boundary and seal the current run as recovery_incomplete.
  7. Preserve all rejected bytes in quarantine and write a diagnostic manifest.

Sealing clears run-only Coin/Credit and prevents reward or casualty farming. Recovered persistent facts are never guessed beyond the last verified receipt. The UI states the boundary and any unverifiable operations; it never claims full preservation without matching hashes.

12. Failure State Model And Consequences

Failure is classified before one operation calculates consequences:

Kind Predicate Casualties Coin fee Run transition
Victory with losses objective succeeds with one or more Fallen Recruits Apply all 0 pay reward, next Market/boss/clear
Encounter defeat all deployed non-summons Fallen or objective failure before timeout Apply all difficulty table no reward; recovery Market if viable
Timeout defeat objective incomplete at tick 1500 Apply prior Fallen only difficulty table no reward; recovery Market if viable
Voluntary retreat withdrawal completes Apply all Fallen before completion difficulty table no reward; recovery Market if viable
Run abandonment player confirms from Market/result No new casualties clear all run Coin/Credit close run, no unearned boss grant
Company collapse no living Recruit and no affordable protected recovery hire already applied 0 additional close run
Process interruption suspend/crash/power loss none beyond deterministic replay 0 resume/reconstruct; not gameplay failure
Save corruption active data fails validation none invented 0 recover or seal; not gameplay failure

Difficulty fees are the sole numeric failure-fee authority:

Difficulty Encounter defeat Timeout Retreat
Wayfinder 0 Coin min(2,current Coin) min(4,current Coin)
Standard min(2,current Coin) min(4,current Coin) min(6,current Coin)
Oathbound min(4,current Coin) min(6,current Coin) min(8,current Coin)

Fee, casualty settlement, failure totals, consumed battle offer, and next-Market seed advance are one result transaction. Fees never make Coin negative and do not alter Credit. Retreat is therefore a controlled loss rather than a free way to cycle battle/Market offers.

12.1 Recovery Viability And Run Close

After failure settlement:

can_continue = living_recruit_count > 0
               or coin + replacement_credit >= protected_offer_list_price

If true, open a recovery Market using the same required cleared-Round number. If false, show Company collapse and close the run after confirmation/automatic receipt. Explicitly declining the only affordable protected offer recomputes the predicate and previews run close before confirm.

This predicate runs only after casualty/failure settlement or protected-offer decline. Dismissal cannot reduce the canonical live count below one and cannot invoke this recovery/closure path. A player who wants to end a run while one or more Recruits live must use the separate Abandon Run transaction.

Boss defeat follows the same rule. Cleared-Round count remains 12; boss Muster reopens if viable. No failure restores a consumed reward, dead Recruit, prior Market, or refresh counter.

12.2 Run Close

Run close stores terminal cause, Company history, all casualties/dismissals, boss/clear facts, discoveries, and eligible Renown. It then clears run-only Coin, Credit, Recruits, inventory, Artifacts, Market, and encounter state. Survivors become nonplayable history Records. One operation applies closure and persistent grants; retry returns the same run result.

13. Migration Contract

Migration runs only at startup or a Market with no pending operation. It never runs during countdown, battle, outcome, casualty settlement, reward selection, or run close.

  1. Validate and copy source bytes to pre_migration.
  2. Build an adjacent-version plan; skipping a required step is illegal.
  3. Resolve stable content IDs and list legacy/missing rows.
  4. Apply each pure deterministic migration to a copy.
  5. Preserve generated name parts, rendered-name snapshot, appearance seed, attributes, Trait, Profession, level, and survival history for living Recruits; do not draw replacements.
  6. Recompute derived stats from preserved source fields under the boundary rule.
  7. Validate operation, ownership, Market, battle, and closure chains.
  8. Write/read a new lineage child, then switch active pointer.
  9. Preserve pre-migration bytes until two later valid Market commits and run close or 30 launches, whichever is later.

A removed equipment/Artifact ID becomes a sealed legacy record with its stable ID and original ownership. At the next safe Market, the player chooses one of two deterministic same-band behavior replacements or leaves it sealed. A removed Profession keeps its stored ruleset for the current committed battle; afterward a deterministic parent-compatible replacement review is required.

If migration fails, discard the candidate and leave the source active under its last supported ruleset where possible. Mark only that slot migration_blocked and offer details/export. Never reset, auto-sell, reroll, or auto-equip.

14. Local, Cloud, Multi-Device, And Rollback

Cloud transports complete validated commits plus closure ledgers. It does not merge Coin, Recruits, equipment, offers, casualties, rewards, or operation rows.

Commit topology Resolution
Identical commit no action
Local ancestor of cloud offer verified cloud descendant; retain local backup
Cloud ancestor of local keep local; queue upload
Different Guild/lineage import only into an empty slot after validation
Same lineage, divergent descendants never merge; choose one active branch, archive the other read-only

Conflict comparison shows commit boundary, Region/Contract, Market/encounter, cleared Rounds, living/fallen counts, Renown, and closure receipt head. Wall clock is context only. Default is cancel/inspect.

The unchosen divergent branch cannot be duplicated into a second playable slot, because that would duplicate generated offers and allow alternate casualty or reward outcomes. It remains exportable/read-only until explicitly deleted.

A developer/support rollback follows the same recovery algorithm as corruption. It may activate an older commit only after replaying later valid closure receipts or sealing the affected run. No debug, platform, or user-facing path may resume an older branch that reverses a known applied casualty or reopens a known claimed reward.

15. Clock Rollback And Bounded Offline Behavior

All gameplay time uses simulation ticks or monotonic in-session duration. Wall clock is display/diagnostic data.

wall_delta = current_wall_clock - last_seen_wall_clock
if wall_delta < -300 seconds or wall_delta > 180 days:
    record CLOCK_ANOMALY
    change no gameplay state

While closed, the game advances exactly zero ticks and generates no Market, offer, Coin, Credit, XP, Renown, healing, promotion, unlock, enemy outcome, or reward. Return opens the saved state plus a compact recap.

Background work is bounded:

  • at most one local save flush at a time;
  • at most three complete cloud payloads queued per Guild slot;
  • at most 100 idempotent platform events;
  • at most 30 seconds of best-effort upload after an explicit foreground save, cancellable without affecting the local commit;
  • no retry loop blocks exit, local play, or a new local commit.

Changing clock, timezone, locale, sleep duration, or network availability cannot change generated names, offers, difficulty, results, or Records.

16. Confirmation, Overwrite, Delete, And Reset

These actions require an effect review and a separate confirm: dismissal with history, promotion, Artifact replacement, protected recovery-offer decline that would close a run, run abandonment, difficulty/Oath-law change, cloud branch choice, migration fallback, Guild-slot overwrite, and Guild-slot delete.

Delete moves the complete Guild slot and closure ledgers to recoverable local trash for seven successful launches or until explicit empty-trash confirmation. A profile-wide reset additionally requires selecting a displayed localized phrase in a second dialog; typing is not required. Default focus is cancel, and destructive actions cannot share a single repeated input with the opener.

Every accepted confirm receives an operation ID. Repeating it returns the first receipt and cannot dismiss, grant, overwrite, close, or delete twice.

17. Accessibility, Localization, And Input Requirements

  • Saving, Saved, Pending, Recovered, Sealed, Conflict, and Blocked use text plus distinct icons, never color/spinner alone.
  • Closing during Pending is legal; recovery finalizes/rejects from intent.
  • Recovery and conflict dialogs support 130% UI scale, screen-reader order, complete touch path, keyboard/controller focus, and reduced motion.
  • Casualty review lists generated name, Profession, equipment returned, Credit, death tick, and first cause; it does not rely on animation or sound.
  • Technical IDs/hashes are hidden by default but available in details and an exportable diagnostic manifest.
  • Localized messages use semantic IDs and typed values, never string fragments.
  • Controller disconnect or focus loss pauses presentation and commits nothing until a new explicit confirm is accepted.
  • Haptic/audio feedback may acknowledge a save or casualty but cannot be the only state indication.

18. Required Recovery And Failure Matrix

Test Fault/input Expected invariant
Pending purchase crash after pending pointer zero cost until deterministic applied child; one Recruit after retry
Applied purchase duplicate same operation ID one cost, one Recruit, same receipt
Refresh crash crash after Coin validation old complete board or new complete board and cost; never mixed
Legal dismiss duplicate start with two live Recruits; repeat confirm one Recruit removal, one equipment return set, one rebate; final live Recruit remains
Final dismiss preview one live Recruit, low Coin, no protected offer FINAL_LIVE_RECRUIT; no operation ID or mutation; Abandon Run remains separate
Stale final dismiss preview at two, another serialized dismissal leaves one, submit old confirm zero-delta FINAL_LIVE_RECRUIT; no rebate, protected offer, or count change
Interrupted final dismiss recover pending intent against active count one reject without apply/receipt beyond rejected intent response; one Recruit remains
Battle suspend suspend during projectile at tick 417 identical next event/draw counters/final hash
Death snapshot crash one tick after front Recruit falls replay contains same death and collapse
Outcome crash crash before casualty settlement locked result reopens; settlement applies exact set once
Casualty partial candidate omit one equipped item in candidate validation rejects; no Recruit removal/Credit commits
Reward duplicate double-tap selected card one item/Artifact/Coin result and draft removal
Boss grant duplicate replay same boss-clear result one clear fact and Renown grant
Three deaths fixed economy fixture Recruits removed, all equipment returned, 54 Credit capped
Standard retreat 10 Coin at/after tick 70 6 Coin fee, all earlier casualties apply, no reward
Wayfinder timeout 1 Coin at tick 1500 1 Coin fee, living Recruits survive, no reward
Company collapse zero living, 9 Coin+Credit, protected price 14 run closes; no free Recruit invented
Recoverable company zero living, 14 Coin+Credit, protected price 14 recovery Market opens; purchase still requires confirm
Clock rollback wall clock moves back 24 hours diagnostic only; zero gameplay delta
Thirty-day offline close mid-run and return zero tick/resource/offer progress; exact recap
Active corruption invalid active checksum, intact receipts/backups reconstruct newest valid chain or seal run at stated boundary
Casualty receipt beyond backup selected backup contains later-Fallen Recruit replay casualty or seal run; never resume with Recruit
All battle snapshots corrupt valid start/journal remains replay same seed/retreat tick or seal on hash divergence
Migration failure stable-ID mapping invalid source stays active; candidate discarded; export offered
Cloud divergence same lineage, different descendants one chosen active, other read-only; no merge/duplicate slot
Run-close retry crash after persistent grants before screen transition same result, Coin/Credit remain cleared, no duplicate Renown

19. Acceptance Checklist

  • Schema, ruleset, content, lineage, commit, and operation versions are separate and migration is copy-first.
  • Pending/applied/rejected operations preserve zero-delta pending and atomic cost/result receipts.
  • Market, battle, casualty, reward, and run-close retries are idempotent.
  • Battle suspend/crash reproduces deterministic state or seals the run on an unverifiable divergence.
  • Death, equipment return, Credit, dismissal, replacement, defeat, timeout, retreat, and Company collapse are explicit and testable.
  • Preview, stale confirm, duplicate confirm, and interruption cannot dismiss the final living Recruit or route dismissal through casualty recovery.
  • Backups, closure receipts, corruption, migration, rollback, and cloud conflicts cannot knowingly reverse casualties or duplicate rewards.
  • Clock rollback and bounded offline behavior produce zero gameplay progress.
  • Confirmation, delete, accessibility, localization, and input rules cover every destructive and recovery state.