Files
aetherbound-guild/docs/02_GAMEPLAY_AND_BALANCE.md
T

2704 lines
135 KiB
Markdown

# Aetherbound Guild - Gameplay, Battle, Economy And Save
Complete gameplay rules, deterministic battle, progression, economy, pacing, failure, persistence and recovery.
## Owner Review / Owner 审核
重点确认招募、自动排阵、战斗、击倒与下关恢复、装备、持续成长、经济、难度、失败与存档规则。这里回答“每一步怎么玩、怎么算、输赢后发生什么”。
本文件是当前六份审核文档之一。历史拆分文件仅保留在 Git 历史中;文内规则、表格和 ID 仍是完整开发依据。
## P0 Consolidation Role
This document is the systems authority inside the consolidated project
contract. It defines what each recurring decision changes, which state owns
the result, how Coin/XP/Equipment/Company/Contract values persist, and how
failure, recovery and Save receipts behave. It does not define page layout or
art treatment; those belong to `03_PAGES_AND_UX.md` and
`04_ART_ANIMATION_AUDIO.md`.
## 2026-08-17 Continuous-Growth Owner Override
This override is the current authority everywhere this file conflicts with the
older FG-4 implementation model below:
- one Guild save owns one persistent Expedition Company across Contract runs;
- New Game creates exactly four stable provisional offers and commits exactly
two of them once as the initial persistent roster;
- a zero-HP Recruit is knocked out for the current battle, remains owned and is
battle-ready again for the next stage with the same identity and growth;
- Contract clear or withdrawal closes only Contract-local progress, formation,
encounter and temporary-effect state, then returns to the Guild; and
- death-funded Replacement Credit, Company-collapse roster deletion, Recruit
archiving and loss of levels/XP/equipment at Contract close are superseded.
The exact next-stage HP amount is intentionally frozen for the later balance
conversion. DEV-2 guarantees persistent ownership and next-stage availability;
it must not invent a full-heal rule or permanent casualty.
The older sections that still name `Fallen`, `Replacement Credit`,
`run_creation_draft`, short-lived `current_run` Recruits, Company collapse or
roster archiving are `FROZEN_LEGACY_FG4` evidence. Their deterministic battle,
economy and Save-v11 details remain preserved until the named later player-order
conversion, but those conflicting lifecycle effects are not current product
authority. Battle-local `Fallen` event/code identifiers may temporarily remain
as implementation names only; product-facing meaning is `Knocked Out` and never
permanent roster removal.
For DEV-2, the current persistence boundary is:
```text
Guild_slot
initial_company_draft or null
expedition_company or null
initial_company_draft
draft_id, seed, status, source_revision
offers[0..3], offer_set_hash
selected_offer_ids[0..2], selection_revision
expedition_company
company_id, created_from_draft_id
owned_recruits[2..], reserve_recruit_ids[]
persistent inventory, Coin, levels, XP, Professions, Traits and history
active_contract or null
```
An uncommitted draft and a committed initial Company are mutually exclusive.
The same saved seed always produces the same four IDs in the same positions.
Selection may contain zero, one or two distinct offer IDs; only exactly two can
enter review. Commit atomically removes the draft, writes one Company containing
only the selected two Recruit records and stores an idempotent receipt. A retry
returns that receipt and never creates a second Company or rerolls the offers.
<!-- Historical section source: SYSTEMS_AND_BATTLE; retained in Git history. -->
## Aetherbound Guild: Systems And Battle Authority
> Authority: generated-Recruit state, Profession grammar, Market/run handoffs,
> ordered Party-line geometry, deterministic auto-battle, equipment and Artifact
> hooks, targeting, movement, knockout, recovery, and causal diagnosis.
>
> Revision: recruit-line-design-r2 / 2026-08-10
>
> Product intent is defined in
> [01_GAME_DESIGN.md](01_GAME_DESIGN.md). Prices, rewards,
> progression rates, capacity selection, and pacing are defined in
> [02_GAMEPLAY_AND_BALANCE.md](02_GAMEPLAY_AND_BALANCE.md). Durable commit and recovery
> semantics are defined in
> [02_GAMEPLAY_AND_BALANCE.md](02_GAMEPLAY_AND_BALANCE.md).
### 1. System Invariants
1. Each side occupies one ordered horizontal line. Position zero is front; a
larger position is farther rearward. There is no second spatial axis.
2. Allied simultaneous footprint begins at four and reaches ten. Reserve and
cumulative hires are separate and never redefine this limit.
3. Battle is automatic. The player may start, pause, change speed, inspect, or
retreat, but cannot activate attacks/skills, reorder, equip, or promote while
resolving.
4. A Recruit has one generated identity, one current fixed Profession, five
rolled attributes from an equal level budget, exactly one Trait, equipment,
level, and survival history.
5. Zero HP means Knocked Out. The Recruit leaves the resolving line immediately,
never respawns in that battle, and is never replaced by Reserve during that
battle. The Recruit remains in the persistent Expedition Company and is
battle-ready again for the next stage with equipment and growth unchanged.
6. The same start state, ruleset, content revision, seed, and retreat timestamp
produces the same state hashes and result.
7. Display frame rate and pause/1x/2x/4x do not change simulation time or draws.
8. Every targeting exception, failed action, movement conflict, retarget,
immunity, proc suppression, and outcome has one inspectable reason.
9. All damage has a declared type, power source, defense, rounding boundary,
critical rule, modifier groups, and floor/nullification behavior.
10. No standard battle exceeds 150 simulation seconds.
### 2. Authoritative State Model
```text
profile
settings, accessibility, language, input mappings
Renown, unlock breadth, Region/Contract progress, Records, collection facts
persistent Expedition Company, inventory, Coin and Recruit growth
Contract expedition
contract_run_id, contract_id, difficulty_id, ruleset_version, content_revision
generation_seed, Market index, cleared Rounds, Line capacity
route, refresh/lock counters, deployed formation, temporary Artifacts
knockout events, boss state, Contract result
Recruit
recruit_id, generated_name_parts, rendered_name_snapshot, appearance_seed
origin_profession_id, current_profession_id, promotion_state
level, xp, five base attributes, Trait id
equipped_item_id or null, effective stats, battles entered, victories,
hire receipt
encounter
encounter_id, battle_offer_id, root_seed, phase, tick
ordered allied/enemy actor sequences and footprints
HP, resources, readiness, cooldowns, casts, statuses, barriers
event queue, draw counters, retreat state, objective, outcome, event journal
presentation only
camera, interpolation, animation frame, particles, selected inspector,
expanded tooltip, pointer, audio playback cursor
```
Presentation never authors state. Battle arithmetic uses signed 64-bit integer
fixed point with 100 units per displayed point. Percentages use basis points
(`10,000 = 100%`). A formula rounds only where this document says `floor`,
`ceil`, or `round_half_up`.
### 3. Run And Market State Machine
```text
Contract_created
-> Market_open
-> battle_offer_committed
-> countdown
-> resolving
-> outcome_locked
-> diagnosis
-> reward_or_failure_applied
-> next_Market | boss_Muster | Contract_return_to_Guild
```
The Market snapshot contains three Recruit offers, four equipment offers, and
three battle offers. Offer generation, prices, locks, and refreshes are owned by
the economy authority. This document owns these system rules:
- leaving and reopening a Market preserves all offer IDs and generated values;
- buying removes exactly that offer and never silently fills the empty slot;
- locked unbought offers persist to the next Market as declared;
- equipment, order, promotion, and dismissal are legal only while Market is
open or during boss Muster;
- battle commit stores an immutable deployed lineup, inventory ownership,
effective-stat snapshot, enemy lineup, seed, risk, and result rules;
- no Market edit after commit can affect that encounter;
- result inspection never applies or skips a reward operation.
### 4. Generated Recruit Contract
#### 4.1 Generation
Recruit generation uses labeled deterministic streams so a name draw cannot
change attributes or Trait:
```text
recruit_name, recruit_appearance, recruit_profession,
recruit_attributes, recruit_trait
```
For offer level `L` from 1 through 10:
```text
attribute_budget(L) = 30 + 5*L
attribute_floor(L) = 3 + L
attribute_cap(L) = 12 + 3*L
```
`Might + Finesse + Insight + Vigor + Resolve` equals the exact budget. Every
attribute stays within floor/cap. The generator first assigns the floor to all
five, then distributes the remaining points one at a time by the labeled stream
without exceeding cap. Because totals are equal at a level, no Recruit is
strictly superior only because its random total is larger.
Every Recruit row stores its inputs and output hash. Generated name components
and modular visual parts create identity and readable variation but no hidden
mechanical modifier.
#### 4.2 Trait
Each Recruit receives exactly one of 36 authored Traits. A Trait row has one
visible benefit and one visible cost, both mechanical. It cannot be removed,
stacked, upgraded, rerolled, or suppressed by changing language or appearance.
A Trait is invalid if its cost never changes a decision in either of its two
fixed fixtures, or if its benefit is only a larger unconditional value. Trait
tags may participate in hidden-hybrid eligibility only when that condition is
shown after discovery.
#### 4.3 Survival History
The persistent Recruit records battles entered, victories, retreats survived,
allies ahead at commit, first knocked-out ally witnessed, promotion, knockout
events and final dismissal if any. History may unlock a title or catalog fact
but cannot grant a hidden stat. Contract close adds an immutable summary Record
without making that Recruit unplayable or replacing its stable ID.
### 5. Profession Catalog And Promotion
The fixed catalog contains 12 base, 24 regular advanced, and 6 hidden hybrid
Professions. Content owns identities and instances; this document owns schema
and topology.
#### 5.1 Topology
- Every base Profession has exactly two regular advanced descendants.
- Every regular advanced Profession has exactly one parent base.
- Every hidden hybrid declares exactly two compatible base-family IDs and an
original battlefield responsibility distinct from both parents.
- A Recruit has exactly one current Profession. Equipment never changes the
stored Profession ID.
- A Recruit's origin base is immutable even after promotion.
#### 5.2 Promotion
A base Recruit becomes eligible after reaching level 4 and surviving three
completed battles. Promotion is one-way and can happen once. The Market review
shows new action rules, lost rules, target/reach changes, resource loop, price
or opportunity cost, and the two validation fixtures before confirm.
The two regular descendants are always deterministic choices once eligible.
A hidden hybrid appears as an additional choice only when its revealed catalog
predicate is satisfied. Allowed predicate inputs are origin base, Trait tags,
equipped behavior tags, current Artifact tags, adjacent deployed Profession
families, and cleared Contract facts. A predicate cannot read generated name,
appearance, playtime, wall clock, or social state.
Promotion preserves that Recruit's individual level and XP, HP percentage
outside battle, Trait, equipped item, survival history, and recruit ID. It
replaces current Profession, action table, resource meter, and derived
Profession bases atomically. It is not a free job switch or reversible respec.
#### 5.3 Automatic Action Unlocks
A Profession action kit unlocks by that Recruit's individual level. Levels are
bounded to `1..10`; there is no Company level and another Recruit's XP cannot
unlock these rules:
| Recruit level | Available automatic rules |
|---:|---|
| 1 | basic action, resource rule, identity passive |
| 2 | first technique and its priority row |
| 3 | second technique and its priority row |
| 4 | signature and its priority row; promotion becomes possible after the survival requirement |
Promoting replaces the action kit with the promoted Profession's level-valid
kit. A direct regular-advanced hire uses the same level schedule. Falling below
an unlock level is impossible, and equipment cannot unlock a later action
early. Every unlocked action is automatic; the player prepares through hire,
equipment, deployment, and order rather than selecting it during battle.
#### 5.4 Profession Row
```text
profession_id, tier (base | regular_advanced | hidden_hybrid)
parent_base_ids, unlock/promotion predicate
battlefield responsibility, preferred depth, failure mode, counter tags
base HP, base Tempo, base defenses, resource schema
basic action, two techniques, signature, passives
automatic priority table and target policies
movement/reach/protection/projectile/healing rules
two decision-opposed validation fixtures
presentation and accessibility event requirements
```
An advanced or hidden Profession must transform target, timing, position,
resource flow, protection, or risk ownership. A stronger scalar copy fails.
### 6. Party-Line Geometry
#### 6.1 Ordered Footprint
Each actor declares footprint 1 or 2. Standard Recruits use footprint 1. An
actor with footprint 2 is one unit ID, one HP pool, and one action state
occupying a contiguous block.
The authoritative line is an ordered actor sequence with no interior empty
space after normalization. For actor `A`:
```text
depth(A) = sum(footprint(X) for every living actor X ahead of A)
front_cell(A) = depth(A)
rear_cell(A) = depth(A) + footprint(A) - 1
```
Allied deployed footprint cannot exceed current Line capacity. Recruits left
out of the sequence are Reserve. Order ties and simultaneous insertion use
stable actor ID only after all player-visible rules are equal.
#### 6.2 Cross-Line Distance And Same-Line Distance
For opposing source `S` and target `T`:
```text
cross_line_distance(S,T) = 1 + depth(S) + depth(T)
```
For allied actors:
```text
same_line_distance(S,T) =
max(0, max(front_cell(S), front_cell(T)) -
min(rear_cell(S), rear_cell(T)) - 1)
```
Adjacent footprints therefore have same-line distance zero. Every action
declares a maximum cross-line or same-line reach. No visual closeness overrides
the integer distance.
#### 6.3 Protection
```text
protection_stacks(T) = min(2, depth(T))
```
Each occupied footprint ahead contributes one stack up to two. Protection is
not a generic defense stat; actions use one declared mode:
| Mode | Rule |
|---|---|
| `frontbound` | Only targets with `depth=0` are candidates |
| `screened` | Deeper targets may be candidates, but resolved damage is multiplied by `max(0.70, 1 - 0.15*protection_stacks)` |
| `intercepted_projectile` | Projectile collides with the first eligible footprint in its current path |
| `lobbed` | Ignores collision but uses screened damage |
| `bypass_protection` | Ignores stacks; must be declared in preview and telegraph |
Healing and boons do not gain a hidden bonus from protection. The inspect view
shows stacks, contributing actors, target legality, and expected multiplier.
### 7. Encounter Lifecycle
| Phase | Legal player actions | Commit behavior |
|---|---|---|
| Preview | Inspect enemy order, actions, risk, reward, timeout, retreat | Offer is stable; no spend |
| Prepare | Equip, promote, dismiss, deploy, reorder | Changes remain reversible until battle confirm |
| Countdown | Pause, speed, inspect | Three simulation seconds; lineup locked |
| Resolve | Pause, speed, inspect, retreat | Automatic action events and retreat timestamp journaled |
| Outcome locked | Inspect | Victory, defeat, timeout, or retreat cannot change |
| Diagnose | Inspect causal timeline | No reward applied by viewing/skipping |
| Settle | Choose fixed reward if eligible | One atomic result operation |
| Finalized | Continue to Market/run result | Casualties, equipment return, Coin/Credit, and reward agree |
### 8. Deterministic Time, Events, And Draws
#### 8.1 Fixed Tick And Priority
One simulation tick is exactly 100 milliseconds. At each tick:
1. accept lifecycle control stamped for this tick, including retreat start;
2. expire statuses and barriers whose end tick equals this tick;
3. resolve periodic status events due this tick;
4. complete casts, channels, projectile arrivals, and summon expiries;
5. select actions for actors already ready and legal;
6. resolve voluntary and forced movement intents;
7. apply simultaneous damage, healing, barrier, and resource deltas by batch;
8. finalize action-outcome records, mark zero-HP actors Fallen, and run declared
outcome/death triggers once;
9. compact lines, insert legal summons, and evaluate objective/retreat/phase;
10. advance readiness, cooldown, cast, status, timeout, and retreat clocks.
Events in the same numeric batch read one pre-batch state and apply together. An
actor alive at batch start completes its already-scheduled event even if the
same batch makes it Fallen. A death trigger cannot restore the source above
zero HP and cannot recursively produce another death trigger in the same tick.
An outcome-triggered derived action is queued no earlier than priority 4 of the
next tick; it cannot join or reopen the batch that produced its outcome.
#### 8.2 Labeled Determinism
The encounter root seed supplies independent labels:
```text
target_tie, critical, status_apply, proc_choice,
summon_variant, reward
```
For a draw:
```text
roll_bp = hash64(root_seed, label, actor_id, action_sequence,
target_id, draw_index) mod 10,000
```
The event journal records label and draw index. Adding an equipment proc cannot
shift target, critical, status, or reward results in another stream. There is
no random damage variance or hidden miss chance.
### 9. Attributes And Derived Statistics
| Attribute | Primary responsibility |
|---|---|
| Might | physical power and Guard contribution |
| Finesse | Tempo, physical precision, and critical pressure |
| Insight | arcane power and status Potency |
| Vigor | maximum HP, Guard, and movement stability |
| Resolve | healing power, Ward, and status Tenacity |
At level `L`:
```text
PhysicalPower = WeaponPower + 2.00*Might + 0.50*Finesse
ArcanePower = FocusPower + 2.00*Insight + 0.50*Resolve
HealingPower = FocusPower + 1.20*Insight + 1.20*Resolve
MaxHP = ProfessionBaseHP + 8*L + 10*Vigor
Guard = ArmorGuard + 1.20*Vigor + 0.60*Might
Ward = ArmorWard + 1.20*Resolve + 0.60*Insight
Potency = Insight + 0.50*Finesse + FlatPotency
Tenacity = Resolve + 0.50*Vigor + FlatTenacity
Tempo = clamp(ProfessionBaseTempo + 0.40*Finesse + FlatTempo, 20, 100)
Initiative = clamp(ProfessionBaseInitiative + 0.25*Finesse + FlatInitiative,
0, 95)
```
Derived values round down to 1/100 after the full expression. Profession bases
must stay within `BaseHP 140-420`, `BaseTempo 24-55`, and `BaseInitiative 0-45`
unless a fixed exception fixture is approved. A MaxHP change outside battle
preserves current HP percentage and does not heal.
### 10. Readiness And Automatic Action Selection
At battle start, `Readiness = Initiative`. Each tick while an actor can act:
```text
Readiness = min(150, Readiness + Tempo*0.1)
```
At `Readiness >= 100`, the actor scans its Profession priority rows from top to
bottom. The first row with a legal action, true visible condition, and legal
target starts. It subtracts 100 Readiness and retains overflow. If none is
legal, readiness remains capped and the actor checks again next tick.
Priority conditions may read only current HP bands, resource, status, casts,
depth, protection, ally/enemy counts, summon space, cooldown, phase, and preview
tags. They cannot read future draws. Every Profession has a legal fallback or
an explicit `NO_REACHABLE_TARGET` idle result.
| Timing field | Boundary |
|---|---|
| Wind-up | 0.1-1.5 s |
| Major telegraph | at least 1.5 s; first boss use at least 2.5 s |
| Channel/cast | 0.3-4.0 s |
| Recovery | 0-2.0 s |
| Cooldown | 0-30 s |
The automatic table is part of Profession identity. Players compare it but do
not script arbitrary condition programs.
### 11. Target Selection, Reach, And Retarget
An action constructs candidates in this order:
1. required side and living/Fallen/summon state;
2. cross-line or same-line reach;
3. protection mode;
4. required status, cast, HP band, or tag;
5. exclusion, immunity, and maximum targets;
6. target policy.
Core target policies are `frontmost`, `rearmost`, `lowest_hp_ratio`,
`highest_power`, `active_cast`, `marked`, and `self`. Numeric ties use depth in
the policy's natural direction, then `target_tie`. The default hostile policy is
frontmost. Enemy-specific score tables must be shown in preview when they can
select something other than the declared policy result.
At action start the target unit ID is reserved. At completion, the action uses
one declared invalid-target policy:
- `retarget_same_policy`: rebuild candidates and choose again;
- `fizzle`: apply no primary effect and use the stated cooldown refund;
- `ground_resolve`: resolve at the stored depth and affect current occupants.
A target moving to another depth remains the same target if still legal. Every
retarget or fizzle logs the first rejecting filter.
#### 11.1 Forecast Is Not Resolution
A forecast is a diagnostic prediction made from the committed preview or the
state at automatic action selection. It stores predicted target IDs, depth,
timing, and the rules that could change them. It never reserves a target and
never fires a battle trigger.
At action start, target selection creates the authoritative reservation. If its
target differs from forecast because an earlier action, movement, collapse, or
status changed state, the journal records `FORECAST_DIVERGED` with the first
changed input. That is not a hit, miss, dodge, resistance, or action outcome.
The reserved action then resolves normally.
Likewise, if a reserved target moves before completion but remains targetable
and inside all declared reach/protection filters, the action follows the same
unit ID. Its terminal component outcome can be `AO_APPLIED`; the detail reason
is `TARGET_MOVED_STILL_LEGAL`. Movement alone never creates an avoidance roll.
#### 11.2 Terminal Outcome IDs
Every started action produces one or more terminal outcome records. Records are
per effect component and actual recipient; routing/invalidity records can name
the intended target, and action-level cancellation records have no recipient.
An area or multi-component action can therefore produce mixed outcomes without
collapsing them into an invented accuracy value.
| Outcome ID | Exact precondition | Ordering point | Player-visible causal label |
|---|---|---|---|
| `AO_APPLIED` | Legal component changes HP, barrier, status, resource, order, or another declared authoritative field on its actual recipient | Finalized after priority-7 deltas | `Applied`; use `Followed target` detail for `TARGET_MOVED_STILL_LEGAL` |
| `AO_NO_CHANGE` | Component reaches a legal recipient but caps/current state yield zero primary change | Finalized after priority-7 deltas | `No change` plus cap/current-state reason |
| `AO_INVALID_REACH` | Reserved target is living/targetable but current reach or protection filter is illegal | Completion validation at priority 4 | `Out of reach` plus old/current depth and required reach |
| `AO_INVALID_UNTARGETABLE` | Reserved target is living but a declared Untargetable rule excludes it | Completion validation at priority 4 | `Untargetable` plus rule ID and remaining duration |
| `AO_INVALID_FALLEN` | Reserved target is absent from the living line because it became Fallen before completion | Completion validation at priority 4 | `Target already Fallen` plus death event ID |
| `AO_INVALID_FILTER` | Reserved target fails another declared side/state/tag candidate filter after the three specific invalid checks | Completion validation at priority 4 | `Target no longer eligible` plus filter ID |
| `AO_INTERCEPTED` | Current projectile/protection path deterministically redirects the intended target's component to an eligible interceptor | Routing after target validation at priority 4 | `Intercepted by {actor}` plus path/protection rule ID |
| `AO_BLOCKED` | An explicit Block charge or named deterministic component blocker nullifies the component before its primary delta | Defense allocation at priority 4; charge/delta commits in priority 7 | `Blocked` plus blocker actor/rule ID |
| `AO_IMMUNE` | A declared immunity tag fully rejects the component before any resistance draw | Component defense at priority 4 | `Immune` plus immunity tag/source ID |
| `AO_RESISTED` | A resistible status component is not immune and its stored `status_apply` roll is at/above `ApplyChanceBp` | Component defense at priority 4 | `Resisted` plus roll, threshold, Potency, and Tenacity |
| `AO_FIZZLED` | Invalid-target policy is `fizzle`, retarget finds no legal candidate, or ground resolution has no eligible occupant | After invalid-target policy at priority 4 | `Action fizzled` plus invalid outcome/filter and refund |
| `AO_INTERRUPTED` | A started wind-up/channel is cancelled by a declared interrupt before completion | When interrupt commits at priority 4 | `Interrupted` plus interrupter/rule and refund |
`AO_BLOCKED` is not generic zero damage: barrier absorption is `AO_APPLIED`
because barrier state changes. `AO_IMMUNE` requires a named immunity. A legal
heal into full HP is `AO_NO_CHANGE`, not resistance. `AO_RESISTED` is limited to
the existing deterministic status-application check and never implies an
accuracy/evasion stat or a general attack miss.
An action summary is derived only for display:
```text
APPLIED_ALL = every primary component record is AO_APPLIED
APPLIED_PARTIAL = at least one primary component is AO_APPLIED and at least one is not
APPLIED_NONE = no primary component is AO_APPLIED
```
Summary values are not trigger IDs. The terminal records remain authoritative.
#### 11.3 Resolution And Routing Order
At priority 4, each completing action uses this order:
1. If a committed interrupt already cancelled the action, emit one
`AO_INTERRUPTED` action record and stop.
2. Revalidate each reserved target against current state in this first-failure
order: Fallen/absent, Untargetable, other declared candidate filter, then
reach/protection. Emit the corresponding `AO_INVALID_*` reservation record.
3. Apply the skill's declared invalid-target policy. A successful
`retarget_same_policy` reserves a new target and continues without refund;
`ground_resolve` snapshots eligible current occupants at the stored depth;
otherwise emit `AO_FIZZLED` and stop that action.
4. Resolve current projectile/protection paths. Emit `AO_INTERCEPTED` for each
redirected intended target, then create component records for the actual
interceptor. Interception itself never counts as an applied component.
5. Expand components and area recipients in current depth then stable-ID order.
A footprint-2 actor is expanded once unless the skill explicitly says once
per footprint.
6. Allocate finite Block charges and deterministic blockers. Same-tick claims
use completion tick, target depth, source stable ID, then action sequence; one
charge cannot block two components. Emit `AO_BLOCKED` for each allocation.
7. Check declared immunity before consuming a resistance or critical draw. Emit
`AO_IMMUNE` on rejection.
8. For remaining resistible status components, consume and record exactly one
`status_apply` draw. Emit `AO_RESISTED` when the roll fails the threshold.
9. Queue remaining numeric/status/movement deltas for priority 7. After the
simultaneous batch, emit `AO_APPLIED` when primary state changed or
`AO_NO_CHANGE` when it did not.
10. At priority 8, derive the action summary, apply at most one declared refund,
claim outcome triggers, log suppressed duplicates, and queue derived actions
for the next tick.
An invalid reservation record remains terminal evidence even when retarget or
ground resolution continues. The redirected/new recipient receives its own
terminal component record, so diagnosis can say both why the original target
was not affected and what actually happened.
#### 11.4 Journal, Refund, And Proc Contract
Every outcome record stores:
```text
root_action_id, action_instance_id, action_sequence, skill_id, source_id
selection_tick, completion_tick, effect_component_id, component_tags
forecast_target_ids, reserved_target_id, reserved_depth
actual_recipient_id, current_depth, outcome_id, reason_id
retarget_policy, resolution_disposition, redirected_action_or_component_id
interceptor_id, blocker_id, immunity_tag as applicable
roll_label, draw_index, roll_bp, threshold_bp as applicable
raw_delta, effective_delta, barrier_delta, status_or_movement_delta
resource_spent, cooldown_committed, refund_resource, refund_cooldown_ticks
trigger_claim_keys, fired_trigger_ids, suppressed_trigger_ids
parent_action_id, derived_depth
```
Resource cost, Readiness, and cooldown are committed when the action starts.
Default refund/proc behavior is:
| Outcome/disposition | Default refund | Primary effect procs |
|---|---|---|
| Invalid record followed by successful retarget/ground resolution | none | evaluate only the new actual recipient's component outcomes |
| `AO_FIZZLED` | skill's declared `fizzle_resource_refund_bp` and `fizzle_cooldown_refund_bp` | no on-hit/on-damage/on-heal/on-status proc |
| `AO_INTERRUPTED` | skill's declared interrupt refund fields | `on_interrupt` and matching `on_outcome` only |
| `AO_INTERCEPTED` | none | intended target can observe `AO_INTERCEPTED`; primary procs evaluate on actual recipient only |
| `AO_BLOCKED`, `AO_IMMUNE`, `AO_RESISTED` | none unless exact ID appears in `refund_on_outcome_ids` | matching defensive hook and `on_outcome` only; no primary effect proc |
| `AO_NO_CHANGE` | none | `on_outcome` only; no effective-delta proc |
| `AO_APPLIED` | none | only hooks whose required effective delta/status/movement actually occurred |
An outcome refund is evaluated once per root action after every component record
is final. The skill declares `refund_match = all_primary | any_primary`; default
is `all_primary`. Refund totals clamp to the originally committed cost/cooldown
and never multiply by targets, components, or repeated outcome records.
Outcome triggers use:
```text
trigger_id, observed_role (source | intended_target | actual_recipient)
outcome_ids[], component_tags[], once_scope (action | recipient | component)
allow_derived (default false), resulting_action_or_hook
```
Default `once_scope` is `action`. Before firing, create:
```text
trigger_claim_key = hash(root_action_id, trigger_id,
scope_recipient_or_component)
```
The first claim fires in outcome-record order; later identical claims log
`DUPLICATE_TRIGGER_CLAIM` and do nothing. Derived actions keep the same
`root_action_id`, increment `derived_depth`, and cannot trigger a row with
`allow_derived=false`. No derived chain can exceed depth 4 or the hook's declared
per-action cap.
### 12. Projectile And Area Resolution
A projectile declares speed in footprints per second, collision mode, pierce
count, and damage retention. At launch:
```text
arrival_ticks = max(1, ceil(cross_line_distance / projectile_speed / 0.1))
```
At arrival, an intercepted projectile traverses the **current** opposing order
from front to its reserved target and collides with the first eligible actor.
After each pierced actor, remaining damage is multiplied by the declared
retention in `0.50-0.90`. A reserved target that moved behind a new protector
can therefore be intercepted. Lobbed and bypassing projectiles follow their
declared protection mode instead.
Areas are contiguous footprint intervals, not visual circles. An area row
declares start policy, length, falloff, and whether a footprint-2 actor is hit
once or once per footprint; default is once per actor.
### 13. Healing, Support, And Protection Access
Support actions use same-line distance. Default heal selection is lowest HP
ratio among legal allies; exact ratio ties choose the frontmost, then
`target_tie`. A heal cannot pass its declared same-line reach because the
animation appears to span the line.
Barrier applies after healing in the same batch and is capped at 40% of target
MaxHP across all generic barriers. Excess is logged as waste. `Block` is an
explicit charge that nullifies one eligible hit after interception and before
barrier. Area, true, or `unblockable` events must declare ineligibility.
Protection stacks do not redirect heals. A support Profession may explicitly
target `nearest_ally_ahead`, `nearest_ally_behind`, or a contiguous interval;
the predicted support path updates when order changes.
### 14. Movement, Push, Pull, And Summons
#### 14.1 Reordering Actions
Battle movement is an automatic action, never free dragging:
- `advance`: swap the actor block with the adjacent actor block ahead;
- `withdraw`: swap with the adjacent actor block behind;
- `push N`: move the target toward rear through up to `N` adjacent block swaps;
- `pull N`: move the target toward front through up to `N` swaps;
- `insert`: add a legal summon before/after its source;
- `return`: remove a temporary summon or restore an explicitly stored order.
Footprint-2 actors move as indivisible blocks. Simultaneous conflicting intents
resolve larger declared movement priority first, then higher source Potency,
then stable source ID. Losing intents fail with `DESTINATION_RESERVED`.
Forced movement stops before an `Immovable` actor. Each unresolved forced step
at a line boundary or blocker deals:
```text
collision_damage = max(1, floor(0.08 * source_PhysicalPower))
```
once per action, not once per blocked step. `Rooted` blocks voluntary movement;
`Immovable` blocks forced movement. Rejections name `ROOTED`, `IMMOVABLE`,
`NO_ADJACENT_ACTOR`, `DESTINATION_RESERVED`, or `CAST_LOCKED`.
#### 14.2 Summon Footprint
Each side has temporary summon allowance of two footprint beyond its committed
deployment. A summon declares footprint 1 or 2 and insertion side relative to
its source. It is legal only when:
```text
current_living_footprint + summon_footprint
<= committed_deployment_footprint + 2
```
A failed insertion spends no summon resource and returns `NO_SUMMON_SPACE`.
Summons never enter Reserve, hold equipment, create Replacement Credit, count
as cumulative hires, or persist after battle.
#### 14.3 Death Collapse
After every death batch, Fallen actors are removed and both lines compact while
preserving the relative order of survivors. New depths become authoritative
before summon insertion and objective checks at priority 9. A scheduled action
that reserved a Fallen target follows its retarget policy; it does not target
the actor that happened to collapse into the old depth unless it uses
`ground_resolve`.
Reserve Recruits never fill a collapsed position during battle. At the next
Market the player explicitly chooses any replacement and new order.
### 15. Damage, Critical, Healing, And Floors
#### 15.1 Damage
Skills declare `Base`, `Coefficient`, `PowerSource`, type, pierce, protection
mode, and whether critical is legal.
```text
RawDamage = max(0, Base + Coefficient * selected_power)
EffectiveDefense = clamp(relevant_defense - pierce, -75, 500)
if EffectiveDefense >= 0:
DefenseMultiplier = 100 / (100 + EffectiveDefense)
else:
DefenseMultiplier = 2 - 100 / (100 - EffectiveDefense)
CritChanceBp = clamp(skill_base_crit_bp
+ 20*(source_Finesse - target_Resolve)
+ explicit_crit_bp,
0, 5000)
crit = can_crit and critical_roll_bp < CritChanceBp
CritMultiplier = clamp(declared_crit_multiplier, 1.25, 2.00) if crit else 1.00
OutgoingGroup = clamp(1 + sum(outgoing_modifiers), 0.25, 3.00)
IncomingGroup = clamp(1 + sum(incoming_modifiers), 0.25, 3.00)
EncounterGroup = clamp(1 + sum(encounter_modifiers), 0.25, 3.00)
GroupProduct = clamp(OutgoingGroup * IncomingGroup * EncounterGroup, 0.10, 4.00)
Mitigated = RawDamage * DefenseMultiplier * CritMultiplier * GroupProduct
Screened = Mitigated * max(0.70, 1 - 0.15*protection_stacks)
DamageFloor = ceil(0.04 * RawDamage)
FinalDamage = 0 if explicit_nullification else
max(1, DamageFloor, floor(Screened))
HPDamage = max(0, FinalDamage - barrier_absorbed)
```
Use `Screened=Mitigated` for non-screened modes. Guard defends physical, Ward
defends arcane, and true damage uses zero defense, cannot critical, ignores
screening, and is capped at 25% target MaxHP per non-boss event. Barrier absorbs
after the floor; an explicit nullification can produce zero. No other ordinary
reduction bypasses the four-percent floor.
Fixed boundary: `RawDamage=125`, defense `500`, GroupProduct `0.10`, no critical,
and no screening yields `2.083...` before the floor. `DamageFloor=5`, so the
resolved damage is `5` before barrier. Explicit nullification yields zero.
#### 15.2 Healing
```text
RawHeal = max(0, Base + Coefficient * HealingPower)
FinalHeal = floor(RawHeal * OutgoingHealGroup * ReceivedHealGroup)
EffectiveHeal = min(FinalHeal, MaxHP - current_HP)
```
Healing does not critical unless the skill declares a visible healing-critical
rule. Overheal has no effect unless one named hook converts it; conversion is
capped at 50% of overheal and still respects the barrier cap.
### 16. Status Effects And Resolution Order
Each status declares:
```text
status_id, category, source_id, target_id
base_chance_bp, potency_source, resist_stat
duration_ticks, interval_ticks, max_stacks
stack_rule (add | refresh | replace | independent)
dispel/immunity tags, on_apply, on_tick, on_expire
presentation and causal-report events
```
For a resistible status:
```text
ApplyChanceBp = clamp(BaseChanceBp + 25*(Potency - Tenacity), 500, 9500)
applies = status_apply_roll_bp < ApplyChanceBp
```
Guaranteed effects bypass the roll but not declared immunity. If simultaneous
effects apply the same status, resolve `replace`, then `add`, then `refresh`,
then independent instances by source ID. Expiry occurs before periodic ticks,
so a status with `end_tick == current_tick` does not tick again.
Hard control effective duration is:
```text
duration = clamp(BaseDuration * 100/(100 + Tenacity), 0.2, 3.0)
```
After hard control ends, the actor gains four seconds of Control Guard, reducing
new hard-control duration by 75%. No actor may be unable to act for more than
50% of any rolling ten-second window. Boss caps and immunities are previewed.
### 17. Equipment, Artifacts, And Hooks
Each Recruit has exactly one universal active equipment slot. An item may be
presented as armament, guard, garb, charm, or kit, but those forms describe
eligibility, attachment and visual language rather than simultaneous body
slots. A Recruit therefore owns zero or one `equipped_item_id`.
Equipping a candidate is one atomic replacement: the candidate leaves run
inventory, the previous item returns to inventory if present, and the Recruit's
single slot points to the candidate. An Artifact is a shared run rule and never
occupies this slot. An item may react to an allied Recruit's item event, but no
effect may require its wearer to hold a second item.
All 320 equipment rows must alter a decision and declare one named hook. Scalar
budget may support the hook but cannot be the row's only identity. One item ID
cannot be owned by two Recruits. Company-unique effects declare a unique group;
later copies retain local behavior but show the shared portion dormant.
Artifacts are shared run rules. A run holds at most three active Artifacts. A
fourth acquisition requires replacing one or declining the new row after an
exact before/after preview. All 60 must change formation, targeting, market,
risk, resource, promotion, summon, or reward valuation and must name a conflict.
Legal battle hooks are:
```text
before_target, after_target, before_cast, on_cast_complete, on_interrupt,
before_damage, after_damage, on_critical, before_heal, after_heal,
on_barrier_break, on_status_apply, on_status_expire,
on_advance, on_withdraw, on_push, on_pull, on_protection_change,
on_resource_gain, on_signature, on_summon, on_fallen, after_collapse
```
Each hook has a trigger limit, minimum 0.5-second cooldown unless a fixed loop
proof allows otherwise, and recursion declaration. Derived events cannot
retrigger their source by default.
### 18. Death, Dismissal, And Replacement
#### 18.1 Battle Death
At zero HP, a non-summon Recruit becomes Fallen:
- it cannot act, target, protect, receive healing, or be promoted;
- its death trigger resolves once at priority 8;
- it is removed for line collapse;
- no timer, item, Profession, Artifact, difficulty rule, or Reserve state can
respawn, resurrect, or substitute for it during the encounter;
- its recruit ID and optional equipped item ID enter the pending outcome
casualty set;
- it remains visible in diagnosis with the preceding five seconds of causes.
At outcome application, each casualty and all returned equipment commit with
Replacement Credit as one transaction. Victory, defeat, timeout, and retreat
do not change the permanence of already Fallen Recruits.
#### 18.2 Dismissal
Dismissal is a Market-only operation. It is illegal for a deployed Recruit in a
committed battle, a Fallen Recruit awaiting outcome, or a Recruit referenced by
a pending promotion. It also uses the product authority's canonical count:
```text
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
```
The preview and commit independently evaluate `dismissal_legal`. Review shows
equipment return, rebate, lost level/history, and resulting deployable
footprint. A legal confirm removes the Recruit once and asserts the post-state
has `live_recruit_count >= 1`.
If the count is one, or a once-valid preview becomes stale after another
serialized operation leaves one living Recruit, reject `FINAL_LIVE_RECRUIT`.
The rejection removes no Recruit or equipment, grants no Coin or Replacement
Credit, changes no counters, and creates no protected recovery offer. Repeated
confirm returns the same rejection. An interrupted pending dismissal must
revalidate the active count before it can apply and is rejected without mutation
when that count is one.
Dismissal never creates Replacement Credit or a Meta reward. Explicit run
abandonment is a distinct Market/result transaction, and casualty-driven
replacement/Company collapse remains exclusive to casualty settlement.
#### 18.3 Replacement
Replacement is an ordinary hire using Coin and eligible Replacement Credit.
After a casualty, the next Market protects one base-Profession recovery offer
from refresh until bought or explicitly declined. Reserve never fills the line
automatically. A run closes only when the player cannot legally deploy one
living Recruit after all available recovery purchases or explicitly abandons.
A blocked final dismissal never creates or refreshes this offer and never invokes
the casualty recovery predicate.
### 19. Battle Control, Retreat, Timeout, And Outcome
Pause, 1x, 2x, and 4x affect presentation only. Inspection while paused can
open actor state, target reasoning, current action, queued major event, line
depths, protection, status order, and causal log.
Retreat becomes legal at tick 50 (5.0 seconds). Confirm begins a 20-tick global
withdrawal. Automatic actions continue and Recruits can fall during it. At the
end of the 20th tick, living allied non-summon actors escape and outcome becomes
retreat. Repeating retreat confirm returns the same command receipt.
At 120 seconds, Overtime begins:
```text
for each completed 10-second Overtime band:
all damage dealt multiplier += 0.25
all healing and new barrier multiplier -= 0.20
damage bonus caps at +0.75; healing/barrier penalty caps at -0.60
```
At tick 1500, if the objective is not satisfied, the result is timeout defeat.
Living Recruits survive; Fallen Recruits remain lost. Timeout grants no battle
reward and applies the failure transaction in the save authority.
Victory is evaluated after simultaneous damage, death, collapse, and summon
events. If both victory and all-allied-Fallen predicates become true in the same
batch, the declared objective decides: elimination objectives are a mutual
defeat; survival/objective-complete battles are victory with zero living
deployed Recruits and can close the run if no Reserve remains.
### 20. Enemy And Battle-Offer Contract
Every ordinary/elite enemy and boss row declares:
```text
enemy_id, ordinary | elite | boss, Region/Contract placement
footprint, preferred depth, movement rules
basic/technique/signature actions and automatic priority
target/protection/reach/projectile/heal/summon rules
first-major-action window and telegraph
phase predicates, immunities, counter tags, two rational responses
interaction with at least two older mechanics
first-failure contribution and fixed fixtures
```
Before commitment, a battle offer shows exact enemy order and footprint, target
policies, protection bypass, earliest major windows, movement, summon allowance,
resistances, objective, timeout, retreat consequence, Coin reward, and reward
family. A hidden visual identity may remain concealed for discovery; a mechanic
needed for a fair choice may not.
The complete envelope is 100 ordinary/elite identities plus 16 bosses across 8
Regions. Presentation variants do not count toward those totals.
### 21. Causal Diagnosis
The result report presents, in order:
1. committed allied and enemy order;
2. predicted and actual first targets;
3. first divergence from predicted action/target/timing;
4. first protection change, projectile interception, or reach failure;
5. first decisive major action and available pre-battle counter categories;
6. every Fallen Recruit with the preceding five simulation seconds;
7. collapse, displacement, summon-space, retarget, and proc-suppression events;
8. effective damage, prevention, healing, control, and resource totals;
9. reward/failure/casualty operation IDs in technical details.
The report can compare one alternate order or equipment removal against the same
seed in a no-reward rehearsal. It may explain categories but cannot auto-equip a
supposed universal answer.
### 22. Content Schemas
#### 22.1 Skill Row
```text
skill_id, owner_profession_id, tags, timing
candidate filters, target policy, protection mode, retarget policy
invalid-target outcome mapping, fizzle/interrupt refund fields
reach, projectile/area/movement/summon fields
Base, Coefficient, PowerSource, type, pierce, critical rule
resource cost/gain, cooldown, status rows, proc hooks
refund_on_outcome_ids, refund_match
outcome triggers (trigger_id, observed_role, AO_* outcome_ids,
component_tags, once_scope, allow_derived, resulting action/hook)
automatic priority condition, illegal reasons, causal labels
animation/VFX/SFX/caption/reduced-motion requirements
fixed input and expected event output
```
#### 22.2 Equipment Row
```text
equipment_id, form, acquisition band, list-price inputs
eligible Profession/action tags
changed decision and named behavior hook
stat support budget, trigger, cooldown, recursion
order/target/timing/resource/risk tradeoff
two interaction fixtures and one loss/comparison fixture
presentation/accessibility/localization needs
```
#### 22.3 Artifact Row
```text
artifact_id, unlock/offer placement, shared hook
run rule changed, conflict group, replacement comparison
Market/battle/risk inputs and outputs
two builds whose valuation changes in opposite directions
fixed seed fixture and causal event
```
#### 22.4 Battle Offer Row
```text
battle_offer_id, Region, Contract, Round, risk tier
enemy lineup/order/levels, seed labels, objective
preview fields, Coin reward, reward family, retreat/failure receipt
expected duration, profile assertion, causal-report requirements
```
### 23. Static Validation Gates
- all Profession topology counts and parent links reconcile to 42;
- all 36 Traits have one benefit, one cost, and two decision fixtures;
- all 320 equipment and 60 Artifact rows name a behavior change;
- all 100 ordinary/elite and 16 boss rows create a distinct counter question;
- no deployed footprint exceeds current Line capacity;
- target preview equals the first simulated target for fixed fixtures;
- identical state/seed/retreat inputs yield identical hashes at every tick;
- all actions stay inside reach, critical, defense, barrier, control, proc, and
timeout bounds;
- every projectile and movement conflict produces the declared collision/order;
- every death returns the exact owned equipment once and never introduces a
Reserve automatically;
- invalid actions spend no resource and return one enumerated reason;
- every started action/component has a declared terminal `AO_*` record and every
outcome trigger references only IDs in Section 11.2;
- duplicate trigger claims are journaled and cannot fire twice;
- all standard fixtures resolve or timeout at/before tick 1500.
### 24. Required Fixed Test Vectors
| Vector | Setup | Expected result |
|---|---|---|
| Frontbound | Three enemies; target policy frontmost | Only depth-zero enemy is a candidate |
| Screened rear | Target has two footprint ahead | Legal screened hit uses `0.70` multiplier |
| Cross-line reach | source depth 1, target depth 2, reach 4 | distance `1+1+2=4`, legal; reach 3 is illegal |
| Heal tie | Two allies at equal HP ratio in reach | Frontmost wins, then deterministic tie draw |
| Projectile intercept | Reserved rear target gains a new front summon before arrival | Current front summon is hit unless bypass/lobbed |
| Push footprint | Push a footprint-2 target one step | Whole block swaps; it never splits |
| Blocked push | Forced move has two remaining steps at boundary | One collision event, not two |
| Summon full | committed footprint 10 plus temporary footprint 2 | another footprint-1 summon fails `NO_SUMMON_SPACE` |
| Simultaneous lethal | Two actors complete lethal actions in one batch | Both events apply, both actors become Fallen |
| Death collapse | Front actor falls before a reserved cast completes | Survivors compact; cast follows its declared retarget rule |
| Reserve behavior | Deployed actor falls while living Reserve exists | Reserve does not enter until next Market preparation |
| Damage floor | Raw 125, defense 500, group product 0.10 | damage 5 before barrier; explicit nullification 0 |
| Critical boundary | roll 2499 with chance 2500 bp | critical; roll 2500 is not |
| Status expiry | periodic tick equals end tick | expiry occurs; no final periodic tick |
| Retreat | confirm at tick 50, no terminal predicate | outcome retreat after tick 70 batch; later duplicate has no effect |
| Speed parity | identical battle at 1x and 4x | all authoritative hashes equal |
| Timeout | objective incomplete at tick 1200/1500 | Overtime applies, then timeout defeat at tick 1500 |
| Casualty equipment | Fallen Recruit owns one item | Recruit removed and that item ID returned exactly once; null slot returns nothing |
| Final dismissal | one living Recruit across deployed plus Reserve | preview/commit reject `FINAL_LIVE_RECRUIT`; no state or recovery-offer change |
| Moved target remains legal | Reserved target changes depth but remains targetable and in reach | same ID receives `AO_APPLIED`; detail `TARGET_MOVED_STILL_LEGAL`; no refund |
| Moved target leaves reach | Reserved target moves from distance 2 to 3 against reach 2; policy `fizzle` | `AO_INVALID_REACH` then `AO_FIZZLED`; declared fizzle refund once |
| Target becomes Untargetable | Living reserved target gains declared Untargetable before completion | `AO_INVALID_UNTARGETABLE`; row retarget/fizzle policy applies; no resistance draw |
| Target falls first | Reserved target becomes Fallen in an earlier tick | `AO_INVALID_FALLEN` names death event; collapsed-depth actor is not substituted |
| Interception | New front protector intercepts projectile aimed at legal rear target | intended target `AO_INTERCEPTED`; protector gets its own component outcome |
| Explicit Block | Actual recipient owns one eligible Block charge | `AO_BLOCKED`; one charge consumed; no primary proc or default refund |
| Immunity | Status target has matching immunity tag | `AO_IMMUNE`; no `status_apply` draw and no status proc |
| Resistance | No immunity; stored roll 7000, threshold 6500 | `AO_RESISTED` with roll/threshold; no general attack miss |
| Mixed area | Three recipients: one changed, one immune, one already capped | `AO_APPLIED`, `AO_IMMUNE`, `AO_NO_CHANGE`; summary `APPLIED_PARTIAL` |
| Duplicate trigger suppression | Mixed area yields three matching records for one once-per-action trigger | first claim fires; later claims log `DUPLICATE_TRIGGER_CLAIM`; one derived action next tick |
### 25. Acceptance Checklist
- [ ] One ordered line owns protection, reach, projectiles, healing, movement,
summon footprint, death collapse, and replacement behavior.
- [ ] Battle formulas define tick order, random labels, damage floor, critical,
status order, timeout, and fixed vectors.
- [ ] Deterministic outcomes distinguish forecast divergence, target
invalidation, interception, Block, immunity, resistance, application, and
duplicate trigger suppression without accuracy/evasion.
- [ ] Generated Recruit and fixed Profession schemas are complete without
requiring authored individual narrative content.
- [ ] Fully automatic combat exposes only the approved diagnostic controls and
retreat.
- [ ] Content counts and no-filler tests match the product authority.
- [ ] Save/failure owns durable death and operation recovery; economy owns all
purchase, reward, capacity, and pacing numbers.
<!-- Historical section source: ECONOMY_AND_BALANCE; retained in Git history. -->
## Aetherbound Guild: Economy And Balance Authority
> Authority: Market offers, prices, locks, refreshes, Coin and Replacement
> Credit, rewards, progression rates, Line-capacity selection, difficulty
> parameters, first-clear pacing, solvency, recovery, and anti-dominance tests.
>
> Revision: anti-dominance-fixture-r1 / 2026-08-11
>
> [01_GAME_DESIGN.md](01_GAME_DESIGN.md) owns the player promise
> and content envelope. [02_GAMEPLAY_AND_BALANCE.md](02_GAMEPLAY_AND_BALANCE.md) owns
> battle and content schemas. [02_GAMEPLAY_AND_BALANCE.md](02_GAMEPLAY_AND_BALANCE.md)
> owns exactly-once application, run failure, interruption, and recovery.
### 1. Economy Invariants
1. All gameplay value is earned in play. There is no paid currency, paid
reroll, ad reward, energy, streak, offline income, or real-time wait.
2. Coin and Replacement Credit exist only in the current run. Renown and
progression facts persist, but cannot buy raw combat stats, starting Coin,
or starting Line capacity.
3. Every Market contains one steady battle offer. A random offer or refresh is
never required to obtain a baseline counter for a mandatory boss.
4. Death is costly through lost level, Profession, Trait, history, and order,
but returned equipment and Credit make ordinary replacement recoverable.
5. Dismissal, selling, refresh, lock, reward, and purchase formulas cannot
create Coin-positive loops.
6. All 320 equipment and 60 Artifact rows change a decision. Economy tiers and
prices may not turn a scalar variant into a gameplay identity.
7. Reward, Market, Recruit-generation, and combat random streams are separated.
Reload, speed, input repeat, and unrelated draws do not change offers.
8. The Standard first clear is solvent on deterministic steady offers without
Meta power, random high-tier equipment, or repeated failed-run farming.
### 2. Resource Ledger
| ID | Player name | Persistence/cap | Sources | Sinks or closure |
|---|---|---|---|---|
| `coin` | Coin | Current run; nonnegative; no hard cap | Run start, battle rewards, reward cards, equipment sales, dismissal rebate | Recruit/equipment purchase, refresh, lock; cleared when run closes |
| `replacement_credit` | Replacement Credit | Current run; cap 54 | Fallen non-summon Recruits | Optional reduction on a later Recruit hire; cleared when run closes |
| `xp` | Experience | Current Recruit; level cap 10 | Completed battles entered by that Recruit | Level thresholds; disappears with Recruit/run close |
| `renown` | Renown | Guild; cap 9,999 | first Contract clears, first discoveries, mastery goals, limited repeat objective | Catalog breadth, previews, challenge laws, hidden-hybrid discovery clues |
| `clear_fact` | Region/Contract clear | Guild fact, not spendable | first valid boss result | Region order, ending, postgame access |
| `discovery_fact` | Catalog discovery | Guild fact, not spendable | first valid inspection/acquisition/encounter | Collection, deterministic future pool access, completion |
There is no material or crafting currency in the base contract. Equipment is
bought, rewarded, equipped, returned after death, sold, or closed with the run.
Adding a persistent resource requires an Owner-approved contract revision.
### 3. Run Opening And Capacity Economy
A run begins with:
- two of four provisional generated Recruits selected at zero Coin price;
- `52 Coin`;
- zero Replacement Credit;
- Line capacity four;
- exactly one starting item on each provisional Recruit and no extra equipped
slots;
- no active Artifact.
The first Market contains at least two base-Profession Recruit offers at level
1. The two together cost 28 Coin, leaving 24 for an equipment, lock, refresh,
or reserve decision.
For `C` cleared ordinary Rounds in `0..12`:
```text
line_capacity(C) = min(10, 4 + floor(C/2))
```
Capacity therefore becomes `4,5,6,7,8,9,10` at clear counts
`0,2,4,6,8,10,12`. It is granted automatically after result settlement, is
not sold, and resets next run. Purchases and cumulative hires do not change it.
### 4. Market Board And Offer Generation
Every ordinary Market contains:
| Row | Slots | Generation constraint |
|---|---:|---|
| Recruit | 3 | At least one base Profession; after a casualty, one protected recovery offer |
| Equipment | 4 | At least three different behavior tags and two legal current users unless Company is empty |
| Battle | 3 | Exactly one steady offer and two distinct higher-risk or different-counter offers |
Boss Muster uses the same Recruit/equipment counts but one fixed boss offer.
It cannot refresh or replace the boss.
Each Market is generated from:
```text
hash(run_seed, contract_id, Market_index, row_id, slot_index, refresh_index)
```
Labeled substreams are `recruit`, `equipment`, `battle`, and `reward`. An offer
stores its complete generated fields and hash before presentation. Reopening,
changing language, inspecting another screen, or closing the application does
not redraw it.
#### 4.1 Offer Level And Pool
For ordinary Round `R` in `1..12`:
```text
offer_level(R) = min(10, 1 + floor((R-1)/3))
```
Boss Muster uses the Round-12 offer level. Base Professions remain in every
pool. Regular advanced direct hires may appear from Round 7 after their catalog
unlock. Hidden hybrids are promotion outcomes and cannot be bought directly.
Equipment uses Region band `B` in `0..7` and item tier `T` in `0..3`. At least
one tier-0/1 legal item remains possible in every ordinary Market. A boss
counter has one deterministic Region objective or fixed Muster offer; it is
never refresh-gated.
#### 4.2 Lock
The player may lock at most three unbought Recruit/equipment offers. Each newly
locked offer costs `2 Coin` once and persists through the next Market or until
bought/declined. Unlocking does not refund Coin. Battle offers can be locked
only within the current Market so the player can refresh other rows; they never
carry beyond a committed battle.
The protected recovery offer needs no paid lock, survives refresh, and expires
only when bought, explicitly declined, or the run closes.
#### 4.3 Refresh
Refreshing replaces every unlocked, unbought offer except a fixed boss and a
protected recovery offer. Let `N` be prior refreshes in the current Market and
`L` the number of paid locked offers:
```text
refresh_cost(N,L) = min(24, 6 + 3*N + 2*L)
```
`N` starts at zero each Market and increments atomically with the new board.
The first no-lock refresh costs 6, the second 9, and the seventh and later cost
24. Insufficient Coin rejects the operation without changing offers or `N`.
### 5. Prices, Sales, And Dismissal
#### 5.1 Recruit Price
For offer level `L` and Profession tier `P` (`0` base, `1` regular advanced):
```text
hire_list_price(L,P) = 10 + 4*L + 10*P
```
Trait and rolled distribution do not add a hidden price because level totals
are equal-budget and every Trait has a cost. A Recruit's generated offer stores
the list price.
Replacement Credit is optional at confirm. If `use_credit` is true:
```text
credit_used = min(replacement_credit_balance, hire_list_price)
coin_cost = hire_list_price - credit_used
```
If false, `credit_used=0`. Coin cost, Credit spend, Recruit creation, and offer
removal apply atomically. Credit cannot buy equipment, refreshes, locks, or
Meta progression.
#### 5.2 Equipment Price And Sale
For Region band `B` in `0..7` and item tier `T` in `0..3`:
```text
equipment_list_price(B,T) = 12 + 3*B + 6*T
sale_value = floor(0.30 * coin_price_paid)
```
An equipment reward stores `coin_price_paid=0` and therefore sells for zero;
its value is the behavior it enables. Purchased copies never sell above 30%.
Locked, equipped, or battle-committed items must be released before sale.
#### 5.3 Dismissal Rebate
The product authority's canonical count and precondition apply before rebate
calculation:
```text
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
```
Only for a legal dismissal:
```text
if survived_battles >= 2:
dismissal_rebate = min(10, floor(0.25 * coin_price_paid))
else:
dismissal_rebate = 0
```
`coin_price_paid` excludes Credit. Equipment returns before the Recruit is
removed, all in one operation. Dismissal creates no Credit, Renown, XP transfer,
offer reroll, or reward. A same-level replacement always costs more Coin than
the rebate unless Credit came from an unrelated death.
When `live_recruit_count == 1`, preview and commit reject
`FINAL_LIVE_RECRUIT` before the rebate formula. The blocked action grants zero
Coin, zero Credit, no protected recovery offer, and no economic or roster
mutation. `Abandon Run` remains a separately confirmed run-close operation and
does not pay a dismissal rebate.
Counterexample trace:
```text
state: Market open; one living Recruit; Coin=3; Credit=0;
no protected recovery offer; Recruit paid 32 Coin and survived 3 battles
attempt: preview dismissal, then submit the same confirm after interruption
result: FINAL_LIVE_RECRUIT at preview and commit
after: one living Recruit; Coin=3; Credit=0; no protected offer;
dismissed_total unchanged; calculated dismissal rebate is not applied
next legal terminal choice: continue Market play or confirm Abandon Run
```
The hypothetical eight-Coin rebate cannot make the final dismissal legal.
Casualty settlement may later create Credit or a protected offer if the Recruit
falls; it is not simulated by dismissal.
### 6. Battle Income And Reward Drafts
Battle risk `Q` is `0` steady, `1` pressured, or `2` elite. For Region band
`B` in `0..7` and ordinary Round `R`:
```text
battle_coin(B,R,Q) =
floor((18 + 2*R + 2*B + 7*Q) * difficulty_reward_multiplier)
reward_budget(B,R,Q) = 12 + 2*R + 3*B + 6*Q
```
Coin pays once on victory before the reward choice. Defeat, timeout, and retreat
pay no battle Coin. Economic consequences after those outcomes are owned only
by the save/failure authority.
The victory reward draft is stable:
| Risk | Cards | Required composition |
|---|---:|---|
| Steady | 2 choose 1 | one legal equipment/tag card; one `floor(0.50*reward_budget)` Coin card |
| Pressured | 3 choose 1 | two distinct legal equipment/tag cards; one Coin or Recruit-discount card |
| Elite | 3 choose 1 | one Artifact-eligible card, one equipment/tag card, one Coin/recovery card |
An Artifact card offers three fixed Artifact IDs and the player chooses one.
If three Artifacts are already active, comparison/replacement is part of the
same operation. Declining a reward is legal and grants nothing. A reward card
cannot be rerolled by leaving the result.
The boss awards no spendable Coin because the run closes. A first boss clear
grants the persistent values in Section 9 and one fixed discovery choice.
### 7. Death Credit And Recovery Solvency
When a hired or provisional Recruit becomes Fallen, use its original list price
and actual Coin paid:
```text
credit_grant = min(original_list_price,
10 + floor(coin_price_paid/2))
new_credit_balance = min(54, old_credit_balance + credit_grant)
```
Credit, permanent Recruit removal, and equipment return apply in one casualty
operation. A provisional Recruit has `coin_price_paid=0` and an implicit level-1
base list price of 14, so it grants 10 Credit. Summons grant none.
After one or more casualties, the next Market reserves one base-Profession
recovery offer with all of these properties:
- its level is `max(1, offer_level-1)`;
- its Profession supplies at least one missing primary responsibility among
protection, damage, reach, or sustain when such a catalog row is unlocked;
- it is not replaced by refresh and needs no lock fee;
- its list price uses the normal formula and Credit can reduce it to zero;
- buying or explicitly declining it clears protection, not remaining Credit.
The guarantee makes a replacement visible, not automatically optimal. A lost
advanced or promoted Recruit returns as a cheaper base Recruit unless the
player chooses to spend more on another offer.
#### 7.1 Three-Death Fixture
At Round 7, three base Recruits originally bought for 18 Coin each fall. The
current base list price is 22, the player has 12 Coin, and Credit cap is 54:
```text
grant per Recruit = min(22, 10 + floor(18/2)) = 19
balance after three grants = min(54, 57) = 54
replacement Coin costs = 0, 0, 12
ending Coin = 0; ending Credit = 0
```
Three legal replacements are therefore purchasable in the next Market. The
player has lost their levels, Traits, promotion progress, history, and all
liquid flexibility, so the loss is recoverable but not profitable.
### 8. XP, Levels, And Promotion Pace
For Recruit level `L` from 1 through 9:
```text
xp_to_next(L) = 20 + 10*L
battle_xp(R,Q) = 10 + 2*R + 4*Q
```
Living deployed Recruits receive 100% on victory, 50% on defeat/timeout when at
least one deployed Recruit survives, and 50% on completed retreat. Reserve and
Fallen Recruits receive zero. Difficulty and display speed do not multiply XP.
XP is applied after casualty determination. A Recruit can gain multiple levels
from one award, checking the formula at each new level, but cannot exceed 10.
At cap, excess is discarded and shown. Promotion eligibility at level 4 plus
three survived battles is owned by the systems authority.
There is no permanent Recruit level, inheritance, XP item, or Meta stat. New
hires catch up through offer level and price rather than receiving another
Recruit's progress.
#### 8.1 Enemy Rank And Within-Run Scaling
Regions add mechanics, not a permanent vertical stat ladder. Within a run,
enemy stat pressure follows the current offer level and risk:
```text
encounter_rank(R,Q,boss) =
min(6, offer_level(R) + Q + (1 if boss else 0))
rank_multiplier = 1 + 0.08*(encounter_rank - 1)
```
Apply `rank_multiplier` once to an enemy row's base MaxHP, power, Guard, Ward,
Potency, and Tenacity, rounding half up. Tempo, Initiative, reach, target rules,
cooldowns, footprint, and status durations do not scale. Difficulty then applies
its declared HP and damage/healing multipliers once.
Identity base budgets are content-authored within the systems ranges and must
pass fixed same-rank comparisons. A later Region cannot substitute a larger
base budget for a new mechanic, and no mandatory battle assumes a prior run's
Recruit or equipment.
### 9. Renown And Meta Progression
For Region number `G` in `1..8` and Contract rank `K` in `{1,2}`:
```text
first_clear_renown(G,K) = 8 + 2*G + 4*K
repeat_objective_renown = min(2, unclaimed_repeat_goals_for_contract)
```
First discovery of a Profession, Trait, equipment behavior family, Artifact,
ordinary/elite enemy, or boss grants one Renown once. A row's presentation
variants grant none. Repeat objectives are finite checklist entries; replaying
an unchanged clear after they are claimed grants zero Renown.
Renown purchases only breadth:
| Unlock | Cost range | Allowed effect |
|---|---:|---|
| Catalog pool packet | 4-8 | Add sidegrade equipment/Trait/Artifact rows to future eligible pools |
| Advanced Profession clue/trial | 8-12 | Reveal requirements and enable a deterministic trial |
| Hidden-hybrid clue/trial | 14-18 | Reveal one predicate and enable its mastery trial |
| Threat archive upgrade | 6-10 | Expose additional exact preview fields for encountered families |
| Oath law | 12-20 | Add a postgame optional run modifier and its completion Record |
Forbidden effects include percentage power, HP, defense, Coin gain, cheaper
shops, free refreshes, starting equipment, starting Recruits, extra reward
cards, larger starting Line capacity, or casualty reversal.
All mandatory Standard counters are in the default or deterministic Contract
pool. A player never needs a Renown purchase to finish the first clear.
#### 9.1 Guild Charter Transaction
`PRO-002` presents the existing Renown authority as the **Guild Charter**. It is
not a new economy, currency, progression tree, or permanent-stat layer. The
board contains exactly eight horizontal Region seals in campaign order. A seal
opens only its authored packet cards and prerequisite tracks; no radial layout
or copied node graph is permitted.
Each claim uses one stable `charter_claim_id` and the existing cost range above:
```text
preview = (charter_claim_id, region_id, prerequisite_ids[], renown_cost,
effect_family, future_run_effect, balance_revision)
commit = verify preview + debit Renown + append one claim receipt atomically
retry = return the original receipt; never debit or grant twice
```
The preview must show current Renown, remaining Renown, every prerequisite,
whether each prerequisite is satisfied, the exact future-run breadth change,
and the statement `No permanent combat power`. A failed prerequisite, stale
balance revision, insufficient balance, duplicate claim, or interrupted write
cannot partially reveal a packet or consume Renown. Respec and refund are not
part of the contracted board; claims are permanent breadth unlocks.
The first-clear choice after the sixteenth boss creates no new currency and
grants no choice-specific combat value. Both branches settle the same earned
Renown and first-clear Records exactly once, archive the surviving Company, and
close Coin/Credit with the run. **Complete the First Chronicle** changes only
the next destination to Final Ledger/Title. **Open the Oath Season** additionally
sets postgame eligibility and enters rule review; its first playable state is a
new generated run after that review. Ordinary New Run remains available and
uses the same persistent breadth already owned.
### 10. Equipment And Artifact Distribution
The 320 equipment entries are divided into eight Region bands of 40. Every band
contains all five item forms (armament, guard, garb, charm, and kit), all
primary responsibilities, and at least three opposed order/reach/protection
plans. Every Recruit has one universal active slot, so form controls eligibility
and presentation rather than granting simultaneous slots. Tier describes price
and availability, not strict power. A lower tier must lead one fixed comparison
in which it is the rational choice.
Each item has a numeric support budget separate from its required behavior:
```text
item_stat_budget(T) = 30 + 8*T
```
The behavior hook reserves 12-24 budget points. At most 50% of the total budget
may be generic HP, power, Guard, or Ward. Remaining numeric support uses:
| Stat | Cost |
|---|---:|
| +1 Might, Finesse, Insight, Vigor, or Resolve | 2 |
| +1 WeaponPower, FocusPower, ArmorGuard, or ArmorWard | 1 |
| +6 MaxHP | 1 |
| +1 Potency or Tenacity | 1 |
| +0.20 Tempo or Initiative | 1 |
The one equipped item's numeric support is added before derived-stat rounding.
Region band does not increase this budget; later bands introduce new
interactions rather than automatic vertical power.
All 320 entries pass the systems behavior fixture. No scalar-only allowance or
minimum subset exists.
The 60 Artifacts are distributed across first-clear, elite, boss, mastery, and
postgame pools. Every run sees at least one deterministic Artifact choice by the
end of Round 6 and at least one additional choice by boss Muster. No Artifact is
required for a mandatory boss. Duplicate active Artifact IDs are illegal.
Bad-luck protection uses behavior tags:
```text
if two consecutive eligible equipment drafts omit the selected behavior tag:
the third eligible draft contains that tag
```
The counter resets when offered, not accepted. The selected tag may be changed
at any Market for zero Coin before a draft is generated. It cannot name an
undiscovered exact item.
### 11. Capacity Selection Simulation
The `10-12` target range was evaluated with an explicit analytical workload
model. It is a design simulation, not human readability evidence.
For profile `P` and candidate capacity `C`:
```text
prep_seconds(P,C) = ceil(base_P + inspect_P*C +
reorder_P*C*(C-1)/2)
battle_p95_seconds(C) = ceil(52 + 5.4*C)
causal_events_per_second(C) = 1.2 + 0.24*C
```
The pair term models order comparisons, not every permutation. The gate is
`prep <= 150 s` for every profile, battle p95 `<=120 s`, causal events `<=4.0/s`,
and the three-death fixture recoverable in at most one Market.
| Profile | `base` | `inspect` | `reorder` | C=10 prep | C=11 prep | C=12 prep |
|---|---:|---:|---:|---:|---:|---:|
| Normal | 20 | 4.0 | 0.8 | 96 s | 108 s | 121 s |
| Active | 25 | 5.0 | 1.4 | 138 s | 157 s | 178 s |
| Efficient | 15 | 3.0 | 0.5 | 68 s | 76 s | 84 s |
| Adversarial | 20 | 4.5 | 1.3 | 124 s | 141 s | 160 s |
| Returning | 30 | 5.5 | 1.3 | 144 s | 162 s | 182 s |
| Candidate | Profiles passing prep | Battle p95 | Causal events/s | Three-death recovery | Result |
|---:|---:|---:|---:|---:|---|
| 10 | 5/5 | 106 s | 3.60 | 1 Market | Pass |
| 11 | 3/5 | 112 s | 3.84 | 1 Market | Fail profile gate |
| 12 | 2/5 | 117 s | 4.08 | 1 Market | Fail profile and event gates |
Ten is the largest candidate passing every declared gate. Human testing still
owns whether ten simultaneous allies plus enemies remain visually legible.
### 12. Contract And First-Clear Pacing
Each Region contains two Contracts. Every Contract is twelve ordinary Rounds,
boss Muster, and one boss. The Normal authored means are:
| Region | Contract 1 | Contract 2 | Region total | Content pressure |
|---:|---:|---:|---:|---|
| 1 | 50 min | 54 min | 104 min | recruit valuation, front/rear, reach |
| 2 | 54 min | 58 min | 112 min | protection bypass and healing access |
| 3 | 56 min | 60 min | 116 min | projectiles and interception |
| 4 | 58 min | 62 min | 120 min | movement, push, and pull |
| 5 | 60 min | 64 min | 124 min | summons and footprint pressure |
| 6 | 62 min | 66 min | 128 min | status ordering and control |
| 7 | 64 min | 68 min | 132 min | promotion and Artifact conflicts |
| 8 | 66 min | 70 min | 136 min | full-system synthesis |
Successful Contract time sums to `972 minutes`. This time is active Market,
battle, result, and boss play; it excludes loading and forced waiting.
#### 12.1 Five Full-Clear Profiles
| Profile | Successful Contract minutes | Failed/abandoned-run minutes | Guild/onboarding/recap minutes | Total | First-clear hours |
|---|---:|---:|---:|---:|---:|
| Efficient | 768 | 120 | 90 | 978 | 16.30 h |
| Normal | 972 | 120 | 78 | 1,170 | 19.50 h |
| Active | 1,152 | 150 | 90 | 1,392 | 23.20 h |
| Adversarial | 928 | 300 | 92 | 1,320 | 22.00 h |
| Returning | 1,024 | 120 | 161 | 1,305 | 21.75 h |
Every profile is inside 15-25 hours. The efficient profile cannot skip required
bosses or decisions; it saves time through faster comparison and presentation
speed. Adversarial includes more elite offers and recoverable losses. Returning
includes recap/segmentation overhead, not forced repetition.
Mastery targets 60+ cumulative hours. At least 70% of post-ending time must use
a previously unclear Profession, Trait, equipment behavior, Artifact conflict,
enemy/boss fixture, or Oath law. An unchanged clear may contribute at most 20%
of any mastery milestone.
### 13. Exact Twenty-Minute Pacing Traces
All timestamps are simulation-independent wall time spent actively playing a
representative 1,200-second session. `M` lists Market openings. `B` lists battle
start-end intervals. `D` lists meaningful decisions; confirmation, inspection,
speed changes, and reward animation do not count.
| Profile | `M` Market opens (s) | `B` battle intervals (s) | `D` decision timestamps (s) | Maximum decision gap |
|---|---|---|---|---:|
| Normal | `[0,300,600,900]` | `[(180,270),(480,570),(780,870),(1080,1170)]` | `[25,90,175,280,325,390,475,580,625,690,775,880,925,990,1075,1180]` | 105 s |
| Active | `[0,330,660,990]` | `[(210,300),(540,630),(870,960),(1140,1200)]` | `[20,75,135,205,310,350,410,475,535,640,680,740,805,865,970,1015,1070,1135]` | 105 s |
| Efficient | `[0,260,520,780,1040]` | `[(130,230),(390,490),(650,750),(910,1010),(1170,1200)]` | `[35,125,240,295,385,500,555,645,760,815,905,1020,1075,1165]` | 115 s |
| Adversarial | `[0,290,610,940]` | `[(150,260),(440,580),(760,910),(1100,1200)]` | `[30,80,145,270,320,375,435,590,640,700,755,920,970,1030,1095]` | 165 s |
| Returning | `[0,270,590,930]` | `[(140,240),(420,550),(760,890),(1090,1180)]` | `[25,85,135,250,300,355,415,560,620,680,755,900,950,1015,1085,1190]` | 145 s |
The gap includes time zero to the first decision and the last decision to 1,200.
Every battle that completes before the trace boundary is followed by its next
decision within 15 seconds. Decision kinds across each trace must include
Recruit/equipment valuation, order or deployment,
battle-risk commitment, reward/recovery, and next-Market budget. Adversarial's
165-second gap is a long elite battle and remains below the product veto because
the battle itself is capped at 150 simulation seconds and the result decision
appears immediately afterward.
### 14. Standard Solvency Trace
A lowest-reward run takes only steady offers. In Region band `B`, ordinary
battle income is:
```text
sum(battle_coin(B,R,0), R=1..12)
= sum(18 + 2*R + 2*B)
= 372 + 24*B Coin
```
With starting Coin, total pre-boss resources are `424 + 24*B Coin`. The
baseline plan spends:
| Required purchase | Cost |
|---|---:|
| Eight base hires needed to grow from two provisional Recruits to capacity ten | 156 |
| Two tier-0/1/2/3 items in Region band `B` | `168 + 24*B` |
| Two refreshes in one no-lock Market | 15 |
| Three locks | 6 |
| Total | `345 + 24*B` |
The Region-band income and equipment-price terms cancel exactly, so the run
reaches boss Muster with `79 Coin` before optional sales, risky-offer
bonuses, reward Coin cards, or Credit. The deterministic boss-counter objective
costs no more than one omitted baseline equipment purchase. Standard solvency
passes when every Region seed can follow an equivalent plan with at least 40
Coin or one complete three-casualty recovery set entering boss Muster.
### 15. Anti-Dominance And Balance Gates
#### 15.1 Required Strategy Tests
| Candidate policy | Why it might dominate | Rejection fixture |
|---|---|---|
| Always refresh twice | More chances at desired synergies | Costs at least 15 Coin per Market before locks; 12-Market use costs 180, exceeds the 79-Coin baseline reserve and must lose one fixed low-variance seed |
| Never refresh | Preserves all Coin | Must lose value in at least two of five seeds where one refresh exposes a counter tag worth more than 6 Coin |
| Buy every Recruit | Reserve breadth and death insurance | Reserve earns no XP/Coin; dismissal returns at most 25% paid Coin; fixed plan must lose equipment/refresh flexibility and fail one boss comparison |
| Dismiss after every battle | Attempt to cycle Traits/offers | No Credit, zero rebate before two survivals, maximum 10 later; cycle is strictly Coin-negative and loses history/level |
| Never dismiss | Avoid sunk-cost loss | Must lose one fixture where releasing an obsolete low-level Reserve funds a reachable counter without weakening deployed footprint |
| Never replace | Preserve Coin after death | Must lose one post-casualty fixture because lower footprint changes reach/protection and leaves Coin unused |
| Always take steady | Minimize death | Must trail pressured/elite in at least one build-breadth objective and one Artifact timing fixture |
| Always take elite | Maximize rewards | Expected casualties and missed steady recovery must reduce clear rate or terminal Coin in at least two profiles |
No policy needs to be bad everywhere. A policy is dominant if it weakly beats
all alternatives in every fixed state and strictly beats one; any such result
blocks the design.
#### 15.2 Executable Fixed-Seed Comparator
The Gate 6 build-policy proof is
[ANTI_DOMINANCE_FIXED_SEEDS.json](product/fixtures/ANTI_DOMINANCE_FIXED_SEEDS.json),
recalculated by `python3 tools/simulate_anti_dominance.py`. It is a prebattle
feasibility comparator, not a second combat model: the fixture declares the
locked route's ordered enemy inputs, required counter tags, minimum deployed
footprint, and casualty counts. The script replays the Market transactions,
tests that the resulting deployed build covers those route requirements, and
then applies the Section 6 battle-Coin formula only on a clear. It introduces
no accuracy, evasion, random draw, hidden generator state, or formula override.
Every seed declares all of the following rather than asking a generator for
them at test time:
| Fixture field | Required declared input or recalculation |
|---|---|
| State | Round, cleared Rounds, Region band, difficulty, Coin, Credit before casualties, prior refresh count, paid locks, and expected capacity/offer-level/refresh-cost values |
| Company | Every living Recruit ID, deployed/Reserve assignment, counter tags, Coin paid, and survived-battle count |
| Recovery | Every casualty's original list price, Coin paid, expected Credit grant, post-casualty Credit, and the protected offer ID or explicit null |
| Boards | All three Recruit, four equipment, and three battle offers on both the opening and first-refresh board, including exact price inputs and stored prices |
| Route | The paid-locked route preserved through refresh, ordered enemy inputs, risk, required tags, deployed footprint, outcome casualties, and stored battle Coin |
| Oracle | Exact action trace and outcome metrics for every policy plus the expected winner |
Each fixture's opening Coin is the post-lock balance; the declared paid lock is
already sunk. Its count still enters the first-refresh formula, and the locked
route is byte-identical on the refresh board so every policy faces the same
combat contract.
The script rejects a missing row or field, a sixth or duplicate seed, a changed
locked route or protected offer, a noncanonical price/reward/Credit value, an
illegal deployment, or an expected-result mismatch. A casualty state must have
exactly one protected Recruit on both boards. The protected offer remains
unchanged through refresh. Dismissal checks the canonical living count before
target selection; with one living Recruit it records `FINAL_LIVE_RECRUIT`, pays
no rebate, and may still perform a separate legal equipment purchase. Casualty
Credit and protected recovery are inputs to later hire transactions, never a
dismissal side effect.
The four fixed policy families are intentionally narrow and deterministic:
| Policy | Exact algorithm |
|---|---|
| `refresh_first` | Pay exactly the declared first refresh when affordable, then buy at most one affordable refreshed equipment offer that maximizes missing route-tag coverage; ties use lower price then offer ID |
| `hire_first` | Buy at most one affordable opening-board Recruit, preferring the protected offer, then missing route-tag coverage, lower Coin cost after optional Credit, lower list price, and offer ID; deploy it only when capacity has a vacancy |
| `dismiss_first` | If legal, dismiss one Reserve with least route relevance, then highest rebate and offer ID tie-breaks; otherwise record the rejection; buy at most one affordable opening-board equipment counter |
| `no_replace` | Never refresh, hire, or dismiss; buy at most one affordable opening-board equipment counter using the same coverage/price/ID ordering |
The comparison is lexicographic and all higher values are better:
`route_cleared`, ending Coin, ending Credit, ending living Recruit count, then
counter-tag coverage. The JSON also records deployed/required footprint,
battle Coin, Market Coin spent, dismissal rebate, Credit used, Line capacity,
and exact actions. This ordering does not convert Credit into Coin and does not
claim that the fixture policies are exhaustive play. It makes one declared
prebattle choice comparable without hiding a utility weight.
For two policies `A` and `B`, `A` weakly dominates `B` only when `A`'s complete
comparison tuple is at least `B`'s on all five seeds and strictly greater on at
least one. The run fails if either policy has no win, has no loss, or any
ordered policy pair meets that definition. `--self-test` first accepts the
canonical matrix, then creates an in-memory matrix in which `refresh_first`
beats every peer on every seed and proves that the dominance check rejects it.
| Stable seed | Fixed state counterfactual | Expected winner |
|---|---|---|
| `AD-FS-001-REFRESH-COUNTER` | First refresh exposes the locked pressured route's only affordable wardbreak counter | `refresh_first` |
| `AD-FS-002-PROTECTED-HIRE` | One casualty leaves both a protection and fourth-deployed-Recruit gap | `hire_first` |
| `AD-FS-003-DISMISSAL-REBATE` | A legal four-Coin obsolete-Reserve rebate makes the current reach counter affordable | `dismiss_first` |
| `AD-FS-004-PRESERVE-COIN` | The current line already clears; extra transactions lose Coin or living roster value | `no_replace` |
| `AD-FS-005-CREDIT-CROSSOVER` | Final-Recruit dismissal is illegal and a zero-Coin protected hire beats two equipment paths | `hire_first` |
Canonical verification is:
```sh
python3 tools/simulate_anti_dominance.py --self-test
python3 tools/simulate_anti_dominance.py
python3 tools/simulate_anti_dominance.py | shasum -a 256
```
Two default runs must be byte-identical. The summary prints every policy's
clear, ending Coin, ending Credit, ending living count, and counter coverage
for every seed, the win counts, a canonical result SHA-256, and
`ABG_ANTI_DOMINANCE_OK no_weak_dominance=1`.
#### 15.3 Complete Gates
1. **Solvency:** all eight Region bands have a steady Standard plan reaching
boss Muster with 40 Coin or the full declared recovery set.
2. **Recovery:** the Section 7.1 three-death state fields three legal base
replacements in one Market and does not increase total Coin-equivalent value.
3. **Capacity:** candidate ten is the only candidate passing all profile,
battle-duration, event-rate, and recovery gates.
4. **Profession diversity:** each base branch pair and hidden hybrid has opposed
fixtures; no identity leads both without a different Company/order cost.
5. **Equipment/Artifact diversity:** every row changes two observable events;
no price/tier creates strict catalog-wide superiority.
6. **Market diversity:** across five fixed run seeds and five profiles, at least
three different first purchases, battle risks, and final orders are optimal.
7. **Failure pressure:** one death is consequential; three are recoverable; a
full-company loss can close a run without making next-run Meta power required.
8. **Decision density:** exact traces reproduce their maximum gaps, and no
battle/result handoff exceeds 15 real seconds after resolution at 1x.
9. **Campaign:** all profiles finish within 15-25 hours and mastery has new
decision content through 60+ hours.
### 16. Difficulty Contracts
Difficulty is chosen before run creation and stored for the entire run:
| Difficulty | Enemy HP | Enemy damage/healing | Reward multiplier | Audience |
|---|---:|---:|---:|---|
| Wayfinder | 0.90x | 0.90x | 1.00x | learning and accessibility-forward play |
| Standard | 1.00x | 1.00x | 1.00x | authored first-clear baseline |
| Oathbound | 1.10x | 1.12x | 1.15x | prepared high-risk play |
Multipliers apply once after enemy derived stats and once to battle Coin where
declared. They do not change offer access, Recruit attribute budgets, XP,
Renown, target rules, telegraph length, Replacement Credit, Line capacity, or
save behavior. Fallen Recruits remain permanent on every difficulty.
Postgame Oath laws are opt-in rule changes, not hidden difficulty multipliers.
Each declares Market/formation/enemy/reward changes and its Record. Laws may not
alter transaction integrity or reintroduce permanent Meta stats.
### 17. Fixed Economy Test Vectors
| Vector | Inputs | Expected result |
|---|---|---|
| Capacity | cleared Rounds 0,2,4,6,8,10,12 | `4,5,6,7,8,9,10` |
| Base hire | L=3, P=0 | 22 Coin list price |
| Advanced hire | L=3, P=1 | 32 Coin list price |
| Credit hire | list 22, Credit 19, use=true | 3 Coin, Credit becomes zero |
| Item price | B=4, T=2 | `12+12+12=36` Coin |
| Item sale | 36 Coin paid | 10 Coin |
| Refresh | N=0/L=0; N=1/L=0; N=7/L=0 | 6, 9, 24 Coin |
| Steady income | B=0, R=5, Standard | 28 Coin |
| Region steady income | B=7, R=5, Standard | 42 Coin |
| Elite income | B=0, R=5, Oathbound | `floor((18+10+14)*1.15)=48` Coin |
| Level XP | L=4 | 60 XP to level 5 |
| Battle XP | R=5, Q=2 | 28 XP to living deployed winner |
| Death Credit | original list 22, paid 18 | 19 Credit |
| Dismissal | paid 32, survived 3 | 8 Coin; no Credit |
| Final dismissal | one living Recruit, paid 32, survived 3, Coin 3 | reject `FINAL_LIVE_RECRUIT`; Coin remains 3, no rebate/offer/roster change |
| Solvency | any B; start plus 12 steady wins minus baseline | 79 Coin |
| Decision gaps | five Section 13 traces | 105, 105, 115, 165, 145 seconds |
| Capacity candidate | C=10 | 5/5 prep, 106 s p95, 3.60 events/s, one-Market recovery |
### 18. Acceptance Checklist
- [ ] Market board, offer levels, prices, refresh, lock, purchase, sale, and
reward formulas are complete.
- [ ] Coin, Credit, XP, Renown, and progression facts have explicit persistence,
sources, sinks, caps, and non-power boundaries.
- [ ] Guild Charter preview/claim/retry uses existing Renown, the declared cost
ranges, exactly-once receipts, and no permanent raw power.
- [ ] Death recovery is affordable without making dismissal/death profitable.
- [ ] The dismissal rebate is unreachable when the target is the final living
Recruit; abandonment and casualty recovery remain separate.
- [ ] Capacity ten follows a declared five-profile analytical selection.
- [ ] Five exact Market/battle/decision traces and campaign budgets meet 15-25
first-clear and 60+ mastery targets.
- [ ] Solvency and anti-dominance gates cover refresh, hire, dismiss, replace,
and risk policies.
- [ ] All fixed content counts and no-filler requirements match the other
authorities.
- [ ] Both final-campaign choices settle identical earned economy receipts,
archive the Company, and differ only in destination/postgame eligibility.
<!-- Historical section source: SAVE_AND_FAILURE_CONTRACT; retained in Git history. -->
## 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-r3 / 2026-08-11
>
> [01_GAME_DESIGN.md](01_GAME_DESIGN.md) owns the player promise.
> [02_GAMEPLAY_AND_BALANCE.md](02_GAMEPLAY_AND_BALANCE.md) owns battle state and casualty
> predicates. [02_GAMEPLAY_AND_BALANCE.md](02_GAMEPLAY_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 respawns in the same battle and never returns to the
same run through Reserve substitution, reload, backup choice, rehearsal,
migration, reward, promotion, difficulty, or clock manipulation.
6. Death removes the Recruit but returns exactly the Recruit's optional
`equipped_item_id` to run inventory. Neither half of that result can commit
alone; an empty slot returns nothing.
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.
11. A Run seed is reserved in a durable `run_creation_draft` before any opening
Recruit is generated. At most one draft or `current_run` exists, and a
draft can only become the `current_run` that carries that same seed.
### 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.
```text
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, next_run_ordinal
Region/Contract progress, Renown, unlocks, Records, discoveries
run_creation_draft or null
current_run or null
operation_journal, compact_applied_ids
closure_receipt_head, run_creation_receipt_head
payload_sha256, envelope_crc32
run_creation_draft
draft_id, run_id, Contract, difficulty, status
seed_ordinal, run_seed, source_commit_id
ruleset_version, content_revision, opening_role_weight_snapshot
opening_offers[0..3] or empty, opening_offer_set_hash or null
selected_offer_ids[2] or empty, selection_revision
draft_hash, run_creation_receipt_head
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.
`run_creation_draft` and `current_run` are mutually exclusive. A draft with
status `seed_reserved` has an empty offer array and selection. `offers_ready`
has exactly four complete offers and no accepted pair; `selection_ready` has the
same four offers and exactly two distinct selected IDs. No state may serialize a
partial offer array, one selected ID, or a draft whose seed ordinal is not less
than `next_run_ordinal`.
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 run-creation draft, battle, reward draft, Market board, or outcome already
committed stays on its stored ruleset/content snapshot. A new ruleset becomes
active only before seed reservation or a new Market is generated, after all
prior result operations are finalized. It cannot rematerialize an existing
opening four.
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:
```text
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:
```text
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 |
|---|---|---|
| Begin run creation | Contract, difficulty, expected `next_run_ordinal`, derived draft/run IDs and seed, version/role-weight snapshot | one `seed_reserved` draft and ordinal increment; no offers or `current_run` |
| Materialize opening offers | draft ID/hash, run seed hash, versions, expected empty offer set | all four indexed offer records/IDs/output hashes and one offer-set hash |
| Set opening selection | draft ID/hash, offer-set hash, expected selection revision, exactly two distinct offer IDs | same four offers, canonical selected pair, revision increment, receipt |
| Commit run creation | draft ID/hash, offer-set hash, selection revision, exact pair and output hashes | draft removed; same run ID/seed and selected Recruit records in one new `current_run`; opening Coin/capacity and run-start receipt |
| 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, candidate item, expected prior item or null, and/or order IDs | one-slot replacement receipt, complete legal inventory and Party-line state |
| Promote | Recruit, old/new Profession, eligibility proof | one Profession replacement and derived-state refresh |
| Dismiss | Recruit, optional equipped item ID, rebate preview, expected `live_recruit_count` | Recruit removal, optional item 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/optional-item/Credit set | all Recruit removals, all present 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. Run Creation, Market, And Round Save Points
#### 7.1 Seed-First Run Creation
`Begin run creation` derives values without an ambient RNG or wall clock:
```text
seed_ordinal = next_run_ordinal
run_seed = low128(sha256(generation_root_seed || lineage_id ||
"run-seed" || u64(seed_ordinal)))
draft_id = low128(sha256(run_seed || "run-creation-draft"))
run_id = low128(sha256(run_seed || "run-id"))
```
Its pending intent fixes the chosen Contract/difficulty and generation snapshot
but generates no Recruit. Its applied child creates the `seed_reserved` draft,
increments `next_run_ordinal` once, mirrors a seed-reservation receipt into both
closure ledgers, and is flushed, pointed to, and read back before the opening
generator may run. A retry from the unchanged parent derives the same ordinal
and seed. Once a draft exists, a new begin request returns
`RUN_CREATION_DRAFT_ACTIVE` and that draft; it cannot consume another ordinal.
If `current_run` exists, begin returns `RUN_ALREADY_ACTIVE` and applies nothing.
`Materialize opening offers` uses only the read-back draft and the content
authority's fixed mappings for indices `0..3`. One applied child stores all four
complete Recruit records, offer IDs, output hashes, and their canonical
offer-set hash. A missing index, duplicate ID, output-hash mismatch, changed
generation snapshot, or any fifth offer rejects with zero deltas and leaves the
same reserved seed. The materialization receipt is mirrored redundantly and is
sufficient to regenerate and verify the same four records.
The UI may hold a zero- or one-card highlight transiently, but the durable
`Set opening selection` operation accepts only a canonical unordered pair of two
distinct IDs from the saved four. A different legal pair replaces the prior
pair and increments `selection_revision`; repeating the same operation ID/hash
returns its original receipt. A new operation requesting the already accepted
pair returns `SELECTION_UNCHANGED` and that receipt without a commit. The player
may revise the pair until Run commit without regenerating an offer.
Back or Cancel exits the view only; it does not discard the draft, change its
Contract/difficulty, consume an ordinal, or expose a refresh action. Save and
Quit, suspension, process restart, locale/timezone change, and wall-clock
rollback reopen the same four offers and latest durable pair. No player-facing
action allocates a replacement draft while one exists.
`Commit run creation` revalidates the draft ID/hash, offer-set hash, exact
selection revision, pair, and both selected output hashes. Its applied child is
one atomic state: it removes the draft, creates `current_run` with the same
`run_id` and `run_seed`, transfers the two unchanged Recruit records, establishes
52 Coin and Line capacity four, and writes the run-start checkpoint and receipt.
There is no intermediate state with both objects or neither object. A request
for a stale draft/revision returns `STALE_RUN_CREATION_DRAFT` plus the current
draft summary and applies nothing. A retry naming an already promoted draft and
matching its commit receipt returns `RUN_CREATION_ALREADY_APPLIED` plus the
original receipt; a conflicting pair/hash is rejected. Neither path generates
offers or creates a second Run.
The Run-creation commit receipt is indexed by both `operation_id` and
`draft_id`. Therefore a duplicate confirm with the original ID and a recovery
retry that acquired a new operation ID both resolve to the one applied receipt
when their draft/pair/hash intent matches. A reused `draft_id` with different
intent returns `RUN_CREATION_COMMIT_CONFLICT` and cannot reach the generic
new-ID path.
##### Run-Creation Crash And Retry Matrix
| Durable boundary | Reachable state after recovery | Exact retry result |
|---|---|---|
| Before begin-pending pointer | parent, no draft, unchanged ordinal | derive the same ordinal/seed and retry |
| After begin-pending pointer | fixed seed intent, zero deltas | finalize the same `seed_reserved` draft once |
| After seed-reservation pointer/read-back | `seed_reserved`, no offers | materialize indices `0..3` from that seed |
| Before/during materialization pointer | seed-only or one complete four-offer child | validate pointer; regenerate same four or return receipt |
| After materialization pointer | `offers_ready`, complete saved four | reopen exact four; no generator call on normal load |
| Before/during selection pointer | prior pair or one complete revised pair | validate pointer; finalize pair or return its receipt |
| After selection pointer | `selection_ready` with exact pair/revision | reopen same pair; later revision remains legal |
| Before/during Run-commit pointer | complete draft or complete `current_run` | finalize from pending intent or return original commit receipt |
| After Run-commit pointer, before replica rotation | `current_run`, no draft | repair replicas; never recreate draft or transfer twice |
Every row uses the generic pending-to-applied procedure in Section 5. A corrupt
candidate is never partially merged with its parent.
#### 7.2 Market, Round, And Run Save Points
Safe commits occur after:
- run-seed reservation, complete opening-offer materialization, each accepted
two-Recruit selection revision, and atomic Run creation;
- 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:
```text
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:
```text
recruit_id, final Profession/level/XP/Trait/history hash
equipped_item_id or null
original list price, Coin paid, computed Credit grant
death tick and causal event ID
```
Applied settlement must satisfy:
```text
all casualty Recruit IDs absent from living Company and Reserve
every non-null casualty equipped_item_id 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:
```text
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:
```text
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:
```text
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_creation`: latest verified seed/offers/selection draft boundary until
atomic Run commit;
- `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-creation, run-start, Market, or
pre-migration copy. Seed reservation, offer materialization, selection revision,
and Run commit receipts are mirrored into both closure ledgers under
`run_creation_receipt_head`. Older selection receipts may compact only after an
exact-ID segment and the latest complete pair/revision snapshot are retained.
#### 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. run-creation seed ordinal, draft/current-run exclusivity, indexed opening
offer hashes, and selection revision;
10. Market seed ancestry, offers, locks, and counters;
11. encounter snapshot, event journal, draw counters, and outcome;
12. 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. Before Run commit, reconstruct a draft only from a valid seed-reservation
receipt plus a complete matching receipt chain. Regenerate the four offers
and verify their saved hashes, then restore the latest complete pair/revision.
5. After a Run-commit receipt exists, never reconstruct its former draft. Recover
the matching `current_run`, or seal it if the full transferred state cannot
be verified.
6. Replay later closure receipts only when parent/result hashes form a complete
chain and each result validates.
7. If the chain reconstructs the newer state, write a recovered child and
continue normally.
8. If a missing/corrupt gap crosses a Run commit, 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`.
9. If no active, backup, draft checkpoint, or redundant receipt proves the
reserved seed, mark the slot `creation_recovery_blocked`; do not advance the
ordinal, allocate another seed, or offer a playable branch. Export and the
existing explicit Guild-slot delete/reset recovery remain available.
10. 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:
```text
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.
```text
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 |
|---|---|---|
| Seed reservation crash | crash after begin-pending or applied pointer | same ordinal/seed; zero offers before applied draft read-back; one draft after retry |
| Offer materialization crash | crash around four-offer pointer | seed-only draft or complete indices `0..3`; retry yields identical IDs/output hashes |
| Selection revision crash | revise accepted pair and crash around pointer | prior complete pair or new complete pair with one revision/receipt; never one selected ID |
| Run creation duplicate | repeat original ID or use a new ID naming the applied draft/pair | same original receipt, same seed/two Recruit records, no draft and no second Run |
| Run creation stale draft | submit old draft hash or selection revision | `STALE_RUN_CREATION_DRAFT`, current summary returned, zero mutation |
| Run creation cancel/restart | Cancel, Save and Quit, or process restart before commit | same draft, four offer IDs/hashes and latest durable pair; no new ordinal/seed |
| Run draft clock rollback | wall clock moves back 24 hours before Run commit | diagnostic only; same draft/offers/pair and zero gameplay delta |
| Run draft active corruption | invalid active checksum, intact draft checkpoint/backup | recover newest valid draft boundary and exact four/pair; no replacement seed |
| Run draft replica corruption | active/backups invalid, redundant creation receipts intact | rebuild same seed/four/pair, or same committed Run if commit receipt exists |
| Run draft total corruption | no valid primary, backup, checkpoint, or creation receipt | `creation_recovery_blocked`; quarantine/export, no new draft or free reroll |
| 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, zero-or-one item return, 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 the non-null 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.
- [ ] Run creation durably reserves one seed before offer generation, stores an
all-or-nothing indexed four, receipts revisable exact-pair selection, and
promotes that same seed and pair into `current_run` exactly once.
- [ ] 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.