Ordering & resolution

Tactics tie

tactics-tie · an adoption of Event resolution, version 2

Tests included

Use in your package

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

What is in the zip file

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

The contract that this adoption uses

This contract covers actions and events that happen at the same game moment. It decides their order, ties, interruptions, chain reactions, and refused actions. It also settles missing sources or targets, two removals reaching one target, simultaneous endings, and presentation delays. This contract does not cover individual effects, input buffering, clocks and pausing, displayed facts, or the simulation stages of each game update. The Input forgiveness contract covers input buffering. The Fact list contract covers displayed facts.

Read the full Event resolution contract →

Questions

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

An action cannot happen exactly as proposed. What happens by default?

Choices for An action cannot happen exactly as proposed. What happens by default?
The refused attempt changes nothing. A tactics unit blocked by a closed gate keeps its stamina and its turn.

The refusal changes no game state, spends no cost, and advances no clock. Time already advancing independently may continue.

The refused attempt spends its cost and changes nothing else. A card refused after commitment still spends its mana.

The refusal applies the action's complete declared cost, produces none of the action's effects, and does not advance a clock because of the refusal.

Supplied answer The refused attempt moves time forward and spends no cost. A roguelike hero who walks into a wall still uses a turn.

The refusal advances one clock once, applies no cost, and produces none of the action's effects. Every case using this result cites the clock rule in its rejection-cases row.

The refused attempt spends its cost and moves time forward. A dungeon wand that fails uses one charge and one turn.

The refusal applies the complete declared cost and advances one clock once, but produces none of the action's effects. Every case using this result cites the clock rule in its rejection-cases row.

The action happens with a smaller result instead of failing. A factory machine on low power makes parts more slowly.

The proposal is accepted, not refused. The scale rule cited by its rejection-cases row scales the complete effect and cost, and the event continues through this lifecycle.

Every proposed action that this adoption covers is accepted. An arcade blaster always fires because ammunition and cooldown never refuse a shot.

No action governed by this adoption has a refusal branch.

Why this is asked

The attempt may fail, spend a resource, use time, or continue at a smaller scale. This answer is the default answer. A rejection-cases row can choose another result.

Two events of the same class are ready at the same moment. What decides which one goes first?

Choices for Two events of the same class are ready at the same moment. What decides which one goes first?
Supplied answer A named number that does not change decides the order. Strategy effects use priority first, and they use spawn number to decide a tie.

Every class with a visible tie has a row naming an integer the event holds when it enters the waiting set. The number may be the event's own or inherited from its source object, such as a relic's acquisition number or a minion's board position. Lower numbers resolve first. An optional second named integer decides between equal first numbers. When no second integer is named, equal first numbers cannot occur, or their order cannot change the result.

The event that entered the waiting set first goes first. Card effects triggered together resolve in the order they were added.

Earlier admission to the waiting set resolves first. Admission order is stable for the complete instant.

Grid position decides the order. Tied tactics units resolve row by row, starting from the map's first row. Inside one row, they resolve starting from the first column.

The grid cited by the class or rank-tie row defines what row and column mean. Its own first row resolves first; within that row, its own first column resolves first.

Positions resolve clockwise around a fixed center. Four boss hazards start from the direction the arena marks first.

The grid cited by the class or rank-tie row defines the center and the first direction. Clockwise angle continues the order, and the row's named stable index decides between events at an equal angle and distance.

No order can change the game result. Racing-game confetti bursts may update in either order because neither affects the race.

No event in the class observes another event in the same class during the instant, and every permitted execution order produces the same game state and player-facing result. This includes the case where two such events cannot arise together.

A declared random draw decides the order. Two creatures with equal speed roll to see which acts first.

Each affected class has a within-class-rules row citing the random rule and its complete draw procedure. The result may vary between runs; the cited rule, not container order, selects it.

Why this is asked

A class is one kind of event in your event-classes list. The rank orders classes. It does not order two events of one class. Their order can change targets, rewards, and later chains.

A win and a loss happen at the same moment. Which ending does the game record?

Asked when
the terminal collisions list has at least one row.
If not asked
No declared ending pair can produce a win and a loss at the same instant.
Choices for A win and a loss happen at the same moment. Which ending does the game record?
Only the loss is recorded. An arena hero who defeats the boss while reaching zero health loses the run.

The lifecycle records only the loss, regardless of event rank or the order in which the two ending conditions were detected.

Only the win is recorded. A racing driver who crosses the finish line while crashing still wins.

The lifecycle records only the win, regardless of event rank or the order in which the two ending conditions were detected.

Both endings are recorded. A card duel in which both heroes reach zero health records a shared defeat and a completed victory condition.

The lifecycle records both the win and the loss. The game's presentation rule decides whether that appears as a draw, one combined result, or two named results.

Why this is asked

The two ending conditions may be detected by different events. This answer decides the recorded result after both conditions are known.

Can an animation or sound delay an event, and can another event pass it?

Asked when
the presentation driven events list has at least one row.
If not asked
No animation or sound delays an event or changes the order of events.
Choices for Can an animation or sound delay an event, and can another event pass it?
Presentation never changes game timing or order. An attack resolves at game time even when its animation runs early or late.

No named event waits for presentation. The rows record that the game considered these events and declared that they do not wait. An adoption with no rows makes no such declaration. This question is then not asked, and no animation or sound delays an event or changes the order of events.

The listed events may wait for their animation or sound. Every later event waits until they finish. A card attack resolves after its animation, and later effects keep their order.

Only events named by presentation-driven-events wait for a cue of their own. Every event behind a held event in the order waits with it, so the delay cannot let a later event resolve first.

The listed events may wait for their animation or sound. Later events do not wait for them. A fighting-game cinematic can finish after a later poison pulse has already resolved.

Only events named by presentation-driven-events may wait. A later eligible event may resolve before a delayed event, and the delayed event keeps its original payload.

Why this is asked

A visual delay and a rules delay are different decisions. The answer decides whether only time changes or order changes too.

Two events would each remove the same target at this moment. What happens to the second one?

Asked when
the same target cases list has at least one row, or the same target exceptions list has at least one row.
If not asked
No two events in this adoption can each remove the same target at one instant.
Choices for Two events would each remove the same target at this moment. What happens to the second one?
Supplied answer Only the first event affects the target. Two tactics arrows reach the last enemy, and the later arrow finds no target.

The first ordered event applies to the target and removes it. Every later event in the collision applies nothing to that target; its cost and carrier follow their own game rules. Every class that can supply two events to such a collision has a visible order. This means that its within-class-rules row, its rank-ties row, or the default answer to within-class-order gives an order other than no-visible-order.

Every event applies, but the target is removed once. Two arena hits both count before one enemy defeat is recorded.

All events in the collision apply to the target in order. Removal is deferred until the collision group ends and is recorded once.

Every event applies, and each event records its own removal result. Two arcade lasers can each award a break bonus from one target.

Every event in the collision applies in order and records its own declared removal consequence, even after an earlier event recorded one.

Why this is asked

The second event may vanish, still count, or create another ending. This answer is the default answer. A same-target-exceptions row can choose another result for its target kind.

An event happening now causes another event due at this moment. When does the new event resolve?

Asked when
the caused event kinds list has at least one row.
If not asked
No non-interrupt event in this adoption causes another event due at the same instant.
Choices for An event happening now causes another event due at this moment. When does the new event resolve?
The new event resolves inside the event that caused it. A card's defeat effect draws a replacement before the next waiting card effect.

The caused event finishes inside the event that caused it, before the next waiting event. Every caused-event row cites a finite chain bound.

The new event joins its normal place in the current order. An arena enemy activated early can attack later in the same moment.

The caused event resolves in its declared class's place, this instant or the next. Every caused-event row cites a finite chain bound.

The new event always waits for the next game moment. A factory alarm raised now closes the conveyor gate on the next update.

Every caused event is admitted to the next instant and none of its effects resolve during the instant that created it. Every caused-event row cites a finite chain bound.

Why this is asked

A chain can continue immediately, join the normal order, or wait for the next game moment.

An interrupt can stop or replace an event that is already happening. When does the interrupt take effect?

Asked when
the interrupts list has at least one row.
If not asked
No event in this adoption stops or replaces another event once it has been selected to resolve, at any point including before its first effect.
Choices for An interrupt can stop or replace an event that is already happening. When does the interrupt take effect?
The interrupt takes effect before the next effect of the current event. A card secret counters a spell before its damage applies.

At each declared effect boundary, a valid interrupt resolves before the next effect of the active event. The interrupt row then stops or replaces the active event.

Supplied answer The current effect finishes before the interrupt takes effect. A parry accepts the sword's contact, prevents the remaining damage, and replaces it with stagger.

The active effect commits first. A valid interrupt then resolves at the following declared boundary and stops or replaces every remaining effect of the active event.

The interrupt joins the normal event order. A factory emergency stop waits for the safety rank, then cancels later machine work.

The interrupt joins this instant when its class is current or has not passed, and otherwise joins the next instant. When it joins the next instant, the active event finishes unchanged in this one and the interrupt stops or replaces only events of its active class that are still waiting or active when it resolves. It remains an interrupt creation path, so the caused-events answer does not govern it.

Why this is asked

An interrupt needs a fixed entry point before it can stop or replace the event already in progress.

An event loses the source or target it needs. What happens by default?

Asked when
the orphan cases list has at least one row.
If not asked
No event in this adoption can lose its required source or target while waiting or resolving.
Choices for An event loses the source or target it needs. What happens by default?
The event still resolves with the state that remains. A card's queued effect continues after its minion leaves play.

The event keeps its captured payload. A missing source or target is unavailable, effects that require it are skipped, and every independent effect still resolves. This is the default answer unless the orphan-cases row chooses another result.

Supplied answer The event is canceled. A homing shot disappears when its only target leaves play before impact.

The event produces no further effect after the required source or target becomes unavailable. Costs already committed are not restored by this contract. This is the default answer unless the orphan-cases row chooses another result.

Why this is asked

A queued card effect or tactics shot can still be waiting after its source or target is gone. The result must not depend on which object the game removed first.

Numbersno numbers

This contract has no numbers to set.

Rules1 rule

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

  • The answer only-first-resolves needs a visible order between events of one class. Add a within-class-rules row that decides which event reaches the target first.

    Forbidden when Within class order is No visible order, and Two events one target is Only first resolves. (a warning, not an error)

Lists11 lists

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

Event classes

event-classes

IdRankLabelDeclared in
reactions0Reaction movestactics.reactions
movement1Movementtactics.movement
light-attacks2Light attackstactics.light-attacks
heavy-attacks2Heavy attackstactics.heavy-attacks
removals3Removalstactics.removals

Within class rules

within-class-rules

This adoption declares no within class rules.

Rank ties

rank-ties

IdRankClass idsTie breakIndex nameSecond index nameGrid declared inRule declared in
attack-initiative2light-attacks, heavy-attacksmap-reading-order——tactics.grid—

Rejection cases

rejection-cases

IdResultDeclared inScale rule declared inClock rule declared in
blocked-movementclock-advancestactics.blocked-movement—tactics.turn-clock

Terminal collisions

terminal-collisions

This adoption declares no terminal collisions.

Same target cases

same-target-cases

IdTarget kindDeclared in
unit-under-two-arrowsunittactics.arrow-collisions

Same target exceptions

same-target-exceptions

This adoption declares no same target exceptions.

Presentation driven events

presentation-driven-events

This adoption declares no presentation driven events.

Caused event kinds

caused-event-kinds

This adoption declares no caused event kinds.

Interrupts

interrupts

IdActive classInterrupt classBoundary granularityResultDeclared inReplacement declared in
sword-parryheavy-attacksreactionsafter-each-hitreplace-remaining-effectstactics.parry-windowtactics.parry-stagger

Orphan cases

orphan-cases

IdEvent classMissingResolutionDeclared in
homing-shot-without-targetlight-attackstargetis-canceledtactics.homing-shot
Test inputsscope and seeds

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

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

Acceptance tests55 tests apply

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

instant-and-turn-boundary

an instant is one simultaneous moment

scenarioonce

Applies to tactics-tie.

An instant is one moment the game treats as simultaneous. A turn boundary the adoption can construct is an instant like any other. Where there is no turn boundary, this test checks nothing about turn boundaries.

Test steps and diagnostics
Given

events at one moment tactics-tie treats as simultaneous, including a turn boundary where the adoption has one

When
  • the moment is resolved
Then
  • all events belong to one instant and follow one lifecycle
  • a constructed turn boundary is handled as an instant like any other. Where no turn boundary exists, this test checks nothing about turn boundaries.
Diagnostics
  • tactics-tie-instant-trace

waiting-active-and-first-boundary

waiting and active events are distinguishable

scenarioonce

Applies to tactics-tie.

A waiting event is accepted but has not begun. An active event is applying ordered effects. Every active event visits an effect boundary before its first effect.

Test steps and diagnostics
Given

an accepted event before and during resolution

When
  • the event enters the waiting set and is selected
Then
  • before selection it is recorded as a waiting event that has not begun
  • during ordered effect application it is recorded as the active event
  • the active event visits an effect boundary before its first effect
Diagnostics
  • tactics-tie-waiting-set
  • tactics-tie-event-trace

ending-event-records-nothing

ending events record no outcome while resolving

scenarioonce

Applies to tactics-tie.

Each ending event the adoption can construct follows the ordinary event lifecycle but records no outcome when it resolves. Only step 15 records an outcome, after the waiting set is empty. Where no ending event exists, this test checks nothing.

Test steps and diagnostics
Given

each ending event the adoption can construct whose effects make an ending condition hold

When
  • the ending event resolves and the instant later reaches settlement
Then
  • resolving the ending event records no match outcome
  • an outcome is recorded only after the waiting set is empty at step 15
  • the ending record distinguishes event resolution from outcome recording
  • where no ending event exists, this test checks nothing
Diagnostics
  • tactics-tie-event-trace
  • tactics-tie-ending-record

file-order-never-decides-resolution

container order never decides event resolution

scenarioonce

Applies to tactics-tie.

Changing container order or document order without changing declarations never changes event resolution. Rank, a rank tie, and within-class order decide the sequence; document order never does. Step 5 says container order in the adoption file never selects an event.

Test steps and diagnostics
Given

two equivalent tactics-tie adoption documents whose container order or document order differs, with the same recorded random inputs where a declared random order is used

When
  • the same instant is resolved from the same pre-instant state under each document
Then
  • the two runs select and resolve the same events in the same declared order
  • no event order is taken from container order or document order
Diagnostics
  • tactics-tie-order-trace
  • tactics-tie-settlement-report

rejection-case-nothing-changes

Row.id refuses the proposal with nothing-changes

scenarioper rejection-cases row

Does not apply to tactics-tie: no row in rejection-cases matches result nothing changes.

rejection-case-cost-taken

Row.id refuses the proposal with cost-taken

scenarioper rejection-cases row

Does not apply to tactics-tie: no row in rejection-cases matches result cost taken.

rejection-case-clock-advances · blocked-movement

blocked-movement refuses the proposal and advances its cited clock

scenarioper rejection-cases row

Applies to the blocked-movement row.

blocked-movement refuses the proposal, spends no cost, produces no action effect, and advances once the clock selected by tactics.turn-clock. The test demands that observable consequence, names the address, and restates nothing from it or tactics.blocked-movement.

Test steps and diagnostics
Given

a proposal that meets the refusal case at tactics.blocked-movement and a distinguishable clock state governed at tactics.turn-clock

When
  • the proposal is checked
Then
  • the proposal ends without creating an event
  • the clock selected by tactics.turn-clock advances once, no cost is applied, and no action effect is produced
  • the proposal log distinguishes this advance, which the refusal causes, from time that advances independently
Diagnostics
  • tactics-tie-proposal-log
  • tactics-tie-clock-trace

rejection-case-cost-and-clock

Row.id refuses the proposal with its cost and cited clock advance

scenarioper rejection-cases row

Does not apply to tactics-tie: no row in rejection-cases matches result cost and clock.

rejection-case-derates-and-enters-lifecycle

Row.id accepts a scaled action

scenarioper rejection-cases row

Does not apply to tactics-tie: no row in rejection-cases matches result derate.

rejection-default-applies-without-row

the default answer to rejected-action-effect governs cases without rows

scenarioonce

Does not apply to tactics-tie: rejected action effect is clock advances, not nothing changes or cost taken or not applicable.

rejection-is-repeatable-from-identical-state

a refused proposal is repeatable from identical state

scenarioonce

Applies to tactics-tie.

The same proposal refused from an identical pre-attempt state produces the same result. The comparison resets the whole pre-attempt state; it does not repeat from state changed by an earlier attempt.

Test steps and diagnostics
Given

two executions of each refused proposal the adoption can construct from identical recorded pre-attempt states

When
  • the proposal is checked in each execution
Then
  • both executions produce the same refusal result and the same recorded cost and clock consequence
  • the comparison starts from identical pre-attempt state rather than the state left by the first execution
  • where no refused proposal exists, this test checks nothing
Diagnostics
  • tactics-tie-proposal-log
  • tactics-tie-state-before-after

accepted-proposal-creates-one-event

an accepted proposal commits once and creates one event

scenarioonce

Applies to tactics-tie.

An accepted proposal creates one payload once and enters its declared event class at its due instant. Where it has an ordinary cost, that cost commits once at the point its rule names. Otherwise this test checks nothing about the cost. The payload captures every value that must stay available after a later loss of the source. Where no such loss can occur, this test checks nothing about those values.

Test steps and diagnostics
Given

a valid proposal with distinguishable payload values, event class, due instant, and an ordinary cost where its Fixed rule declares one

When
  • the proposal is accepted and its event is admitted
  • where the adoption can construct later source loss, the source becomes unavailable before a captured value is used
Then
  • where an ordinary cost is declared, it is committed once at the point its Fixed game rule names. Otherwise this test checks nothing about the cost.
  • one event payload is created once and enters its declared event class at its due instant
  • the payload contains every value that must stay available after a later loss of the source, while uncaptured state is read only when an effect resolves
  • where later source loss cannot be constructed, this test checks nothing about the values that must stay available after source loss
Diagnostics
  • tactics-tie-proposal-log
  • tactics-tie-cost-log
  • tactics-tie-waiting-set
  • tactics-tie-event-trace

new-event-uses-one-admission-path

a new event uses the interrupt path or caused-event path

scenarioonce

Applies to tactics-tie.

After an instant opens, a valid declared interrupt enters only through the interrupt path. Every other newly created event due now follows caused-events. For a branch that the adoption cannot construct, this test checks nothing. No event uses both paths.

Test steps and diagnostics
Given

events created after an instant opens, including valid declared interrupts and other new events where the adoption can construct them

When
  • each new event is admitted
Then
  • an event backed by a valid interrupts row enters only through the interrupt path and is governed by interrupt-entry
  • every other new event due now follows caused-events
  • For a branch that the adoption cannot construct, this test checks nothing. No event uses both paths.
Diagnostics
  • tactics-tie-proposal-log
  • tactics-tie-interrupt-report
  • tactics-tie-caused-event-report

lowest-eligible-rank-resolves-first

the lowest eligible rank resolves first

scenarioonce

Applies to tactics-tie.

At every selection, the lowest eligible rank resolves first without changing the selected event's payload. Where no instant has two eligible ranks, this test checks nothing about the order of ranks. Step 4 selects the lowest eligible rank; step 5 selects within the class without changing the payload.

Test steps and diagnostics
Given

each instant the adoption can construct with eligible waiting events from at least two declared ranks

When
  • the next waiting event is selected repeatedly
Then
  • each selection comes from the lowest rank that is eligible at that selection
  • selection does not change the selected event's payload
  • where no instant has two eligible ranks, this test checks nothing about the order of ranks
Diagnostics
  • tactics-tie-waiting-set
  • tactics-tie-order-trace

event-class-never-passes-lower-rank · reactions

reactions at rank 0 never passes a lower rank

scenarioper event-classes row

Applies to the reactions row.

An event of reactions at rank 0 never resolves before an eligible lower-rank event in the same instant. Where the adoption cannot construct that pairing, this test checks nothing.

Test steps and diagnostics
Given

an eligible reactions event and an eligible event of a lower declared rank in the same instant

When
  • the waiting set is selected
Then
  • the lower-rank event is selected before the reactions event
  • where no lower-rank event is eligible with reactions, this test checks nothing
Diagnostics
  • tactics-tie-waiting-set
  • tactics-tie-order-trace

event-class-never-passes-lower-rank · movement

movement at rank 1 never passes a lower rank

scenarioper event-classes row

Applies to the movement row.

An event of movement at rank 1 never resolves before an eligible lower-rank event in the same instant. Where the adoption cannot construct that pairing, this test checks nothing.

Test steps and diagnostics
Given

an eligible movement event and an eligible event of a lower declared rank in the same instant

When
  • the waiting set is selected
Then
  • the lower-rank event is selected before the movement event
  • where no lower-rank event is eligible with movement, this test checks nothing
Diagnostics
  • tactics-tie-waiting-set
  • tactics-tie-order-trace

event-class-never-passes-lower-rank · light-attacks

light-attacks at rank 2 never passes a lower rank

scenarioper event-classes row

Applies to the light-attacks row.

An event of light-attacks at rank 2 never resolves before an eligible lower-rank event in the same instant. Where the adoption cannot construct that pairing, this test checks nothing.

Test steps and diagnostics
Given

an eligible light-attacks event and an eligible event of a lower declared rank in the same instant

When
  • the waiting set is selected
Then
  • the lower-rank event is selected before the light-attacks event
  • where no lower-rank event is eligible with light-attacks, this test checks nothing
Diagnostics
  • tactics-tie-waiting-set
  • tactics-tie-order-trace

event-class-never-passes-lower-rank · heavy-attacks

heavy-attacks at rank 2 never passes a lower rank

scenarioper event-classes row

Applies to the heavy-attacks row.

An event of heavy-attacks at rank 2 never resolves before an eligible lower-rank event in the same instant. Where the adoption cannot construct that pairing, this test checks nothing.

Test steps and diagnostics
Given

an eligible heavy-attacks event and an eligible event of a lower declared rank in the same instant

When
  • the waiting set is selected
Then
  • the lower-rank event is selected before the heavy-attacks event
  • where no lower-rank event is eligible with heavy-attacks, this test checks nothing
Diagnostics
  • tactics-tie-waiting-set
  • tactics-tie-order-trace

event-class-never-passes-lower-rank · removals

removals at rank 3 never passes a lower rank

scenarioper event-classes row

Applies to the removals row.

An event of removals at rank 3 never resolves before an eligible lower-rank event in the same instant. Where the adoption cannot construct that pairing, this test checks nothing.

Test steps and diagnostics
Given

an eligible removals event and an eligible event of a lower declared rank in the same instant

When
  • the waiting set is selected
Then
  • the lower-rank event is selected before the removals event
  • where no lower-rank event is eligible with removals, this test checks nothing
Diagnostics
  • tactics-tie-waiting-set
  • tactics-tie-order-trace

within-class-stable-index

Row.class id uses stable index order

scenarioper within-class-rules row

Does not apply to tactics-tie: within-class-rules has no rows.

within-class-arrival-order

Row.class id uses stable arrival order

scenarioper within-class-rules row

Does not apply to tactics-tie: within-class-rules has no rows.

within-class-map-reading-order

Row.class id follows its cited map reading order

scenarioper within-class-rules row

Does not apply to tactics-tie: within-class-rules has no rows.

within-class-clockwise-order

Row.class id follows its cited clockwise order

scenarioper within-class-rules row

Does not apply to tactics-tie: within-class-rules has no rows.

within-class-no-visible-order

Row.class id exposes no within-class order

scenarioper within-class-rules row

Does not apply to tactics-tie: within-class-rules has no rows.

within-class-declared-random-order

Row.class id uses its cited random order

scenarioper within-class-rules row

Does not apply to tactics-tie: within-class-rules has no rows.

within-class-game-rule-order

Row.class id uses its cited game rule order

scenarioper within-class-rules row

Does not apply to tactics-tie: within-class-rules has no rows.

rank-tie-stable-index

Row.id orders its tied classes by stable index

scenarioper rank-ties row

Does not apply to tactics-tie: no row in rank-ties matches tie-break stable index.

rank-tie-arrival-order

Row.id orders its tied classes by stable arrival

scenarioper rank-ties row

Does not apply to tactics-tie: no row in rank-ties matches tie-break arrival order.

rank-tie-map-reading-order · attack-initiative

attack-initiative follows its cited map reading order

scenarioper rank-ties row

Applies to the attack-initiative row.

For attack-initiative, the grid at tactics.grid supplies its own first row and first column and produces one total order over light-attacks, heavy-attacks. The test names the address and restates nothing from it.

Test steps and diagnostics
Given

eligible events of light-attacks, heavy-attacks in different rows and columns of the grid at tactics.grid

When
  • the shared-rank order is selected
Then
  • one total order over all events of all listed classes follows the grid's own first row and then its own first column
  • the order trace distinguishes the rank tie from container order
Diagnostics
  • tactics-tie-order-trace

rank-tie-clockwise-order

Row.id follows its cited clockwise order

scenarioper rank-ties row

Does not apply to tactics-tie: no row in rank-ties matches tie-break clockwise.

rank-tie-no-visible-order

Row.id exposes no shared-rank order

scenarioper rank-ties row

Does not apply to tactics-tie: no row in rank-ties matches tie-break no visible order.

rank-tie-declared-random-order

Row.id uses its cited random order

scenarioper rank-ties row

Does not apply to tactics-tie: no row in rank-ties matches tie-break declared random.

rank-tie-game-rule-order

Row.id uses its cited game rule order

scenarioper rank-ties row

Does not apply to tactics-tie: no row in rank-ties matches tie-break game rule.

general-within-class-order

the default answer to within-class-order covers classes without another order

scenarioonce

Applies to tactics-tie.

For every class without a class row and without a shared rank, the default answer has no named data to compare for such a class, so two ready events cannot arise or cannot change the result. Where no such class or pair exists, this test checks nothing.

Test steps and diagnostics
Given

each event class with no within-class-rules row and no shared rank

When
  • two events of that class are ready together where the adoption can construct them
Then
  • the default answer has no named data to compare for such a class, so two ready events cannot arise or cannot change the result
  • where no such class or pair exists, this test checks nothing
Diagnostics
  • tactics-tie-order-trace
  • tactics-tie-settlement-report

presentation-never-holds-event

Row.event class never waits for Row.presentation cue

scenarioper presentation-driven-events row

Does not apply to tactics-tie: presentation timing is not asked for tactics-tie.

presentation-delay-holds-following-events

Row.event class waits for Row.presentation cue without reordering

scenarioper presentation-driven-events row

Does not apply to tactics-tie: presentation timing is not asked for tactics-tie.

presentation-delay-skips-only-held-event

Row.event class may be passed while Row.presentation cue is unfinished

scenarioper presentation-driven-events row

Does not apply to tactics-tie: presentation timing is not asked for tactics-tie.

unnamed-event-has-no-presentation-wait

events with no presentation row wait for no cue of their own

scenarioonce

Applies to tactics-tie.

A selected event named by no presentation-driven-events row never waits for a cue of its own; where the adoption answers may-delay and it is behind a held event in the order, it waits with that event; otherwise it is admitted immediately. Where every selected class has a row, this test checks nothing. This follows the presentation-timing option semantics and step 6.

Test steps and diagnostics
Given

each selected event class not named by a presentation-driven-events row

When
  • presentation waiting is checked
Then
  • the event never waits for a cue of its own; where the adoption answers may-delay and it is behind a held event in the order, it waits with that event; otherwise it is admitted immediately
  • where every selected class is named by a presentation row, this test checks nothing
Diagnostics
  • tactics-tie-presentation-trace
  • tactics-tie-order-trace

terminal-collision-rows-cover-ending-pairs

declaration check: terminal-collision rows cover every ending pair

scenarioonce

Applies to tactics-tie.

This declaration check reads the adoption and its Fixed ending rules, not the running game. A terminal-collisions row must exist for every pair of a win and a loss that can become true at one instant. The when-empty sentence of the row set states what holds when no row exists. When a pair that can be constructed has no row, the adoption is invalid, and this test fails. Where no win and loss can become true at one instant, this test checks nothing.

Test steps and diagnostics
Given

the tactics-tie adoption and the ending pairs its Fixed rules can construct, not the running game

When
  • a reviewer compares every pair with terminal-collisions
Then
  • a terminal-collisions row exists for every pair that can produce a win and a loss at the same instant
  • the terminal-collisions when-empty sentence states what holds when no row exists
  • When a pair that can be constructed has no row, the adoption is invalid, and this test fails. Where no win and loss can become true at one instant, this test checks nothing.
Diagnostics
  • tactics-tie-declaration-record
  • tactics-tie-ending-record

same-target-rows-cover-removal-collisions

declaration check: same-target rows cover every removal collision

scenarioonce

Applies to tactics-tie.

This declaration check reads the adoption and its Fixed removal rules, not the running game. A same-target-cases or same-target-exceptions row must exist for every target kind with a same-instant removal collision. The when-empty sentences of the two row sets state what holds when no row exists. When a target kind whose collision can be constructed has no row, the adoption is invalid, and this test fails. Where no such collision can occur, this test checks nothing. This test says nothing about a shared target with no removal.

Test steps and diagnostics
Given

the tactics-tie adoption and the target-removal cases its Fixed rules can construct, not the running game

When
  • a reviewer compares every target kind with same-target-cases and same-target-exceptions
Then
  • a same-target-cases or same-target-exceptions row exists for every target kind for which two events can each remove one target at one instant
  • the when-empty sentences of the two row sets state what holds when no row exists
  • When a target kind whose collision can be constructed has no row, the adoption is invalid, and this test fails. Where no such collision can occur, this test checks nothing.
Diagnostics
  • tactics-tie-declaration-record
  • tactics-tie-collision-report

caused-event-rows-cover-due-now-causes

declaration check: caused-event rows cover every cause of an event that is due now

scenarioonce

Applies to tactics-tie.

This declaration check reads the adoption and its Fixed cause rules, not the running game. A caused-event-kinds row must exist for every non-interrupt event that is caused and is due in the same instant. The when-empty sentence of the row set states what holds when no row exists. When a caused event that is due now and can be constructed has no row, the adoption is invalid, and this test fails. Where no such event exists, this test checks nothing.

Test steps and diagnostics
Given

the tactics-tie adoption and the causes of non-interrupt events that its Fixed rules can construct, not the running game

When
  • a reviewer compares every kind of newly caused event that is due now with caused-event-kinds
Then
  • a caused-event-kinds row exists for every non-interrupt event that is caused and is due in the same instant
  • the caused-event-kinds when-empty sentence states what holds when no row exists
  • When a caused event that is due now and can be constructed has no row, the adoption is invalid, and this test fails. Where no such event exists, this test checks nothing.
Diagnostics
  • tactics-tie-declaration-record
  • tactics-tie-caused-event-report

interrupt-rows-cover-stops-and-replacements

declaration check: interrupt rows cover every stop and replacement

scenarioonce

Applies to tactics-tie.

This declaration check reads the adoption and its Fixed interaction rules, not the running game. An interrupts row must exist for every event that stops or replaces another after selection, including before its first effect. The when-empty sentence of the row set states what holds when no row exists. When a stopping or replacement interaction that can be constructed has no row, the adoption is invalid, and this test fails. Where no such interaction exists, this test checks nothing.

Test steps and diagnostics
Given

the tactics-tie adoption and the event interactions its Fixed rules can construct after resolution starts, not the running game

When
  • a reviewer compares every stopping or replacement interaction with interrupts
Then
  • an interrupts row exists for every event that stops or replaces another after selection, including before its first effect
  • the interrupts when-empty sentence states what holds when no row exists
  • When a stopping or replacement interaction that can be constructed has no row, the adoption is invalid, and this test fails. Where no such interaction exists, this test checks nothing.
Diagnostics
  • tactics-tie-declaration-record
  • tactics-tie-interrupt-report

same-target-general-resolution · unit-under-two-arrows

unit uses the default answer to two-events-one-target

scenarioper same-target-cases row

Applies to the unit-under-two-arrows row.

For unit, the first ordered event applies to the target and removes it; every later group member applies nothing to that target. tactics.arrow-collisions identifies the collision case; the test names the address and restates nothing from it.

Test steps and diagnostics
Given

two events in one instant that can each remove one unit under tactics.arrow-collisions

When
  • their collision group resolves
Then
  • the first ordered event applies to the target and removes it; every later group member applies nothing to that target
  • the collision report distinguishes this result from an orphan result or an ordinary event with no effect
Diagnostics
  • tactics-tie-collision-report
  • tactics-tie-event-trace

same-target-exception-resolution

Row.target kind uses its collision exception

scenarioper same-target-exceptions row

Does not apply to tactics-tie: same-target-exceptions has no rows.

collision-precedes-orphan-check

same-target grouping precedes the orphan check

scenarioonce

Applies to tactics-tie.

For each same-target collision group the adoption can construct, grouping settles before the orphan check and removal inside the group never makes another member an orphan. Under all-resolve-one-ending, the target stays live for members and for a non-member that resolves before the group ends. For a case that the adoption cannot construct, this test checks nothing.

Test steps and diagnostics
Given

each same-target collision group the adoption can construct, plus each non-member event that can resolve before deferred removal

When
  • the group is settled and source and target availability are checked
Then
  • the same-target decision is settled before the general orphan check
  • removal inside the group never turns another group member into an orphan
  • under all-resolve-one-ending, the target remains live for group members and any non-member that resolves before the group ends
  • where no such group or no such non-member exists, this test checks nothing about that case
Diagnostics
  • tactics-tie-collision-report
  • tactics-tie-orphan-report
  • tactics-tie-event-trace

orphan-override-resolves-what-remains

Row.id resolves what remains

scenarioper orphan-cases row

Does not apply to tactics-tie: no row in orphan-cases matches resolution resolves what remains.

orphan-override-is-canceled · homing-shot-without-target

homing-shot-without-target is canceled

scenarioper orphan-cases row

Applies to the homing-shot-without-target row.

For homing-shot-without-target, losing the required target removes a waiting event or discards an active event's remaining effects. Committed costs and effects remain. tactics.homing-shot identifies the case; the test names the address and restates nothing from it.

Test steps and diagnostics
Given

waiting and active light-attacks events whose required target becomes unavailable under tactics.homing-shot

When
  • availability is checked before the first effect and at every later effect boundary
Then
  • a waiting event is removed and an active event discards every remaining effect
  • no committed cost or effect is restored
  • the orphan report distinguishes cancellation from resolving what remains and from an event that independently produced no effect
Diagnostics
  • tactics-tie-orphan-report
  • tactics-tie-event-trace
  • tactics-tie-cost-log

orphan-default-row

Row.id uses the default answer to orphaned-events

scenarioper orphan-cases row

Does not apply to tactics-tie: no row in orphan-cases matches resolution default.

undeclared-orphan-is-invalid

declaration check: an undeclared missing object has no implied result

scenarioonce

Applies to tactics-tie.

This declaration check reads the adoption, not the running game. Every event that can lose a required source or target has an orphan-cases row. When an event outside those rows can lose a required object, no result is implied. The adoption is then invalid, and this test fails. Where no event can become an orphan, this test checks nothing.

Test steps and diagnostics
Given

each event that can lose a required source or target while waiting or resolving

When
  • a reviewer compares that case with orphan-cases before the build is audited
Then
  • every such case has a matching orphan-cases row
  • a missing object outside those rows is an invalid adoption and receives no implied cancellation or remaining-effects result
  • When a case is not declared, the adoption is invalid, and this test fails. Where no event can lose a required object, this test checks nothing.
Diagnostics
  • tactics-tie-orphan-report
  • tactics-tie-declaration-record

active-event-commits-effects-in-order

an active event commits each effect before the next boundary

scenarioonce

Applies to tactics-tie.

An active event visits the boundary before its first effect, applies effects in its Fixed rule's order, and commits each effect before the next boundary. If no event with two or more effects exists, this test checks nothing about the boundary between effects.

Test steps and diagnostics
Given

an eligible event with two or more ordered effects where the adoption can construct one

When
  • the event becomes active and applies its effects
Then
  • the event visits the boundary before its first effect
  • effects are applied in the order stated by the event's Fixed game rule
  • each effect commits before the next declared boundary is visited
  • where no event with two or more effects exists, this test checks nothing about the boundary between effects
Diagnostics
  • tactics-tie-event-trace
  • tactics-tie-interrupt-report

interrupt-stops-original

Row.id enters and stops the original event

scenarioper interrupts row

Does not apply to tactics-tie: no row in interrupts matches result stop original.

interrupt-replaces-remaining-effects · sword-parry

sword-parry enters and replaces the remaining effects

scenarioper interrupts row

Applies to the sword-parry row.

For an active heavy-attacks event whose after-each-hit boundaries are named at tactics.parry-window, sword-parry creates one valid reactions interrupt offered at a named boundary and enters after the just-finished effect has committed. Where the interrupt enters at a boundary of the active event, event order selects one valid interrupt, the active event advances only to permitted boundaries while it waits, and the interrupt passes through steps 6 to 11 before changing that event; where it joins the next instant instead, the active event runs to completion in this one. Then after it resolves, every remaining original effect is discarded and the complete replacement at tactics.parry-stagger is installed in its stated order while committed effects and costs remain. A presentation skip returns to the interrupted boundary. Where no skip is possible, this test checks nothing about a skip. The test names both addresses and restates nothing from them.

Test steps and diagnostics
Given

an active heavy-attacks event whose after-each-hit boundaries are named at tactics.parry-window, and a valid reactions interrupt offered at one of them

When
  • the valid interrupt is discovered, selected, and resolved
Then
  • one interrupt event is created through the interrupt path and enters after the just-finished effect has committed
  • where the interrupt enters at a boundary of the active event: when several interrupts are valid, event rank, the shared-rank order, and then within-class order select one; the active event advances only to boundaries tactics.parry-window permits while it waits; and the interrupt passes through presentation waiting, collision grouping, orphan checks, and its own boundaries before changing that event
  • where the interrupt joins the next instant instead, the active event runs to completion in this one
  • after it resolves, every remaining original effect is discarded and the complete replacement at tactics.parry-stagger is installed in its stated order while committed effects and costs remain
  • only one selected interrupt resolves at a time, and the report distinguishes replacement from an invalid offer
  • where presentation skips the interrupt, control returns to the interrupted event's boundary rather than step 4. Where no such skip is possible, this test checks nothing about a skip.
Diagnostics
  • tactics-tie-interrupt-report
  • tactics-tie-event-trace
  • tactics-tie-cost-log

invalid-interrupt-does-not-enter · sword-parry

sword-parry refuses an invalid interrupt offer

scenarioper interrupts row

Applies to the sword-parry row.

An interrupt of reactions offered outside the validity window named at tactics.parry-window, or at a point that is not one of the after-each-hit boundaries, creates no interrupt event. The active heavy-attacks event advances unchanged, and the interrupt report distinguishes the invalid offer from a valid interrupt that was not selected. Where the adoption cannot construct an invalid offer, this test checks nothing.

Test steps and diagnostics
Given

an interrupt of reactions offered outside the validity window named at tactics.parry-window, or at a point that is not one of the after-each-hit boundaries

When
  • the interrupt offer is checked
Then
  • no interrupt event is created
  • the active heavy-attacks event advances unchanged
  • the interrupt report distinguishes an invalid offer from a valid interrupt that was not selected
  • where the adoption cannot construct an invalid offer, this test checks nothing
Diagnostics
  • tactics-tie-interrupt-report
  • tactics-tie-event-trace

caused-event-follows-declared-route

Row.id creates one caused event on its declared route

scenarioper caused-event-kinds row

Does not apply to tactics-tie: caused events is not asked for tactics-tie.

active-event-finishes-and-loop-continues

a finished event returns resolution to the waiting set

scenarioonce

Applies to tactics-tie.

Finishing an active event applies any due deferred removal, records each selected consequence once, removes the event, and returns to step 4. The instant continues until the waiting set is empty and waits for any held cue. For a branch that the adoption cannot construct, this test checks nothing.

Test steps and diagnostics
Given

each active event, including a collision-group member where the adoption can construct one

When
  • the active event finishes
Then
  • any deferred target removal due at that point is applied and each selected consequence is recorded once
  • the event is removed from the waiting set and selection returns to step 4
  • the instant continues until the waiting set is empty, waiting for a cited cue when only presentation-held events remain
  • where no deferred removal or no held event exists, this test checks nothing about that case
Diagnostics
  • tactics-tie-event-trace
  • tactics-tie-waiting-set
  • tactics-tie-collision-report

single-ending-records-after-waiting-set-empties

a single ending records from final settled state

scenarioonce

Applies to tactics-tie.

After the waiting set is empty, a single ending that holds in final settled state is recorded once through the game's cited ending rule. Earlier ending-event resolution and class rank do not record or override it. If no single ending can hold, this test checks nothing.

Test steps and diagnostics
Given

each instant the adoption can construct whose final settled state makes one ending hold outside every declared terminal collision

When
  • the waiting set becomes empty and step 15 runs
Then
  • the game's cited ending rule records that ending once from final settled state
  • no earlier ending-event resolution or class rank records or overrides it
  • where no such single ending can hold, this test checks nothing
Diagnostics
  • tactics-tie-waiting-set
  • tactics-tie-ending-record

terminal-collision-follows-precedence

Row.id records its selected ending result

scenarioper terminal-collisions row

Does not apply to tactics-tie: terminal precedence is not asked for tactics-tie.

instant-commits-state-and-next-waiting-set-together

the instant commits state and next-instant work together

scenarioonce

Applies to tactics-tie.

Step 16 commits final state and the next-instant waiting set together. No event from a closed instant can re-enter it. Where there is no next-instant event, this test checks nothing about the members of the next-instant waiting set.

Test steps and diagnostics
Given

an instant with distinguishable final state and next-instant waiting events where the adoption can construct them

When
  • step 16 closes the instant
Then
  • the final state and next-instant waiting set commit together
  • no event from the closed instant later re-enters it
  • where no next-instant event exists, this test checks nothing about the members of the next-instant waiting set
Diagnostics
  • tactics-tie-instant-trace
  • tactics-tie-waiting-set

settlement-report-carries-lifecycle-fields

each settlement report carries the lifecycle record

scenarioonce

Applies to tactics-tie.

Every settlement report carries the instant, event id, class, rank, tie-break when used, within-class order, source and target availability, applied effects, interrupt and replacement when present, caused-event destination, removal result, and terminal result when present. For an optional circumstance that the adoption cannot construct, this test checks nothing.

Test steps and diagnostics
Given

each event settled by tactics-tie, with optional tie, interrupt, replacement, caused event, removal, and terminal result where present

When
  • its settlement report is read
Then
  • the report carries instant, event id, class, rank, tie-break when used, within-class order, source and target availability, effects applied, interrupt and replacement when present, caused-event destination, removal result, and terminal result when present
  • for an optional circumstance that the adoption cannot construct, this test checks nothing
Diagnostics
  • tactics-tie-settlement-report

within-class-row-class-join

declaration check: Row.class id names an existing event class

scenarioper within-class-rules row

Does not apply to tactics-tie: within-class-rules has no rows.

presentation-row-class-join

declaration check: Row.event class names an existing event class

scenarioper presentation-driven-events row

Does not apply to tactics-tie: presentation-driven-events has no rows.

caused-row-class-join

declaration check: Row.event class names an existing event class

scenarioper caused-event-kinds row

Does not apply to tactics-tie: caused-event-kinds has no rows.

orphan-row-class-join · homing-shot-without-target

declaration check: light-attacks names an existing event class

scenarioper orphan-cases row

Applies to the homing-shot-without-target row.

This declaration check reads the adoption, not the running game. light-attacks must name exactly one event class in this adoption. In any other case, the adoption is invalid, and this test fails.

Test steps and diagnostics
Given

the tactics-tie adoption document, not the running game

When
  • a reviewer compares light-attacks with event-classes ids
Then
  • light-attacks names exactly one event-classes row in this adoption. In any other case, the adoption is invalid, and this test fails.
Diagnostics
  • tactics-tie-declaration-record

interrupt-active-class-join · sword-parry

declaration check: heavy-attacks names an existing event class

scenarioper interrupts row

Applies to the sword-parry row.

This declaration check reads the adoption, not the running game. heavy-attacks must name exactly one event class in this adoption. In any other case, the adoption is invalid, and this test fails.

Test steps and diagnostics
Given

the tactics-tie adoption document, not the running game

When
  • a reviewer compares heavy-attacks with event-classes ids
Then
  • heavy-attacks names exactly one event-classes row in this adoption. In any other case, the adoption is invalid, and this test fails.
Diagnostics
  • tactics-tie-declaration-record

interrupt-class-join · sword-parry

declaration check: reactions names an existing event class

scenarioper interrupts row

Applies to the sword-parry row.

This declaration check reads the adoption, not the running game. reactions must name exactly one event class in this adoption. In any other case, the adoption is invalid, and this test fails.

Test steps and diagnostics
Given

the tactics-tie adoption document, not the running game

When
  • a reviewer compares reactions with event-classes ids
Then
  • reactions names exactly one event-classes row in this adoption. In any other case, the adoption is invalid, and this test fails.
Diagnostics
  • tactics-tie-declaration-record

rank-tie-rows-match-shared-ranks

declaration check: shared ranks and rank-tie rows match exactly

scenarioonce

Applies to tactics-tie.

This declaration check reads the adoption, not the running game. Shared ranks and rank-ties rows must match exactly: one row for each shared rank, and one shared rank for each row. Each comma-separated class-ids string lists every event class at that rank and no other class; the class-ids description requires each id to be listed once. In any other case, the adoption is invalid, and this test fails. With no shared rank and no row, this test checks nothing.

Test steps and diagnostics
Given

the tactics-tie adoption document, not the running game

When
  • a reviewer groups event-classes rows by rank and compares the groups with rank-ties rows
Then
  • every rank shared by two or more event classes has exactly one rank-ties row, and every rank-ties row names a rank shared by at least two event classes
  • each rank-ties class-ids string lists every event-classes id at that rank and no other id; the class-ids description requires each id to be listed once
  • In any other case, the adoption is invalid, and this test fails. When no rank is shared and no rank-ties row exists, this test checks nothing.
Diagnostics
  • tactics-tie-declaration-record

tied-classes-have-no-within-class-row

declaration check: tied classes have no class-specific order row

scenarioonce

Applies to tactics-tie.

This declaration check reads the adoption, not the running game. A class already ordered by a rank-ties row must have no within-class-rules row. When such a class has one, that row has no effect. The adoption is then invalid, and this test fails. With no rank tie, this test checks nothing.

Test steps and diagnostics
Given

the tactics-tie adoption document, not the running game

When
  • a reviewer compares every class named by rank-ties with within-class-rules class-id fields
Then
  • no event class named by a rank-ties row has a within-class-rules row
  • When a tied class has a within-class-rules row, that row has no effect. The adoption is then invalid, and this test fails. With no rank tie, this test checks nothing.
Diagnostics
  • tactics-tie-declaration-record

same-target-kinds-do-not-overlap

declaration check: collision cases and exceptions do not overlap

scenarioonce

Applies to tactics-tie.

This declaration check reads the adoption, not the running game. same-target-cases and same-target-exceptions must not name the same target kind. When a target kind is named in both row sets, the adoption is invalid, and this test fails. With either set empty, this test checks nothing.

Test steps and diagnostics
Given

the tactics-tie adoption document, not the running game

When
  • a reviewer compares target-kind across same-target-cases and same-target-exceptions
Then
  • no target kind is named in both row sets
  • When a target kind is named in both row sets, the adoption is invalid, and this test fails. When either set is empty, this test checks nothing.
Diagnostics
  • tactics-tie-declaration-record

only-first-needs-visible-order

declaration check: only-first collisions have a visible order

scenarioonce

Applies to tactics-tie.

This declaration check reads the adoption, not the running game. Every event class that can supply two events to a collision resolved by only-first-resolves, through the default answer to two-events-one-target or a same-target-exceptions row, must have a visible order from its class row, its rank tie, or the default answer to within-class-order. When such a class has no visible order, the adoption is invalid, and this test fails. Where no target kind uses only-first-resolves, this test checks nothing. The only-first-resolves semantics states this duty.

Test steps and diagnostics
Given

the tactics-tie adoption document, not the running game

When
  • a reviewer finds every event class that can supply two events to a collision resolved by only-first-resolves through the default answer to two-events-one-target or a same-target-exceptions row
Then
  • every such class has a visible order: a within-class-rules row whose order is not no-visible-order, a rank-ties row whose tie-break is not no-visible-order, or a default answer to within-class-order other than no-visible-order
  • In any other case, the adoption is invalid, and this test fails. Where no target kind is resolved by only-first-resolves, this test checks nothing.
Diagnostics
  • tactics-tie-declaration-record

same-target-exception-differs-from-general

declaration check: each same-target exception differs from the default answer

scenarioonce

Applies to tactics-tie.

This declaration check reads the adoption, not the running game. No same-target-exceptions row may repeat the default answer to two-events-one-target. When a row repeats it, that row has no effect. The adoption is then invalid, and this test fails. With no exception row, this test checks nothing. The same-target-exceptions description states this duty.

Test steps and diagnostics
Given

the tactics-tie adoption document, not the running game

When
  • a reviewer compares every same-target-exceptions resolution with the default answer to two-events-one-target
Then
  • no same-target-exceptions row names the same resolution as the default answer
  • When an exception row repeats the default answer, that row has no effect. The adoption is then invalid, and this test fails. With no exception row, this test checks nothing.
Diagnostics
  • tactics-tie-declaration-record

stable-index-second-index-duty

declaration check: stable indexes settle equal first numbers

scenarioonce

Applies to tactics-tie.

This declaration check reads the adoption and its Fixed event rules, not the running game. Every stable-index class row or rank tie must name a second index or belong to a class or tied group that cannot produce equal first numbers that change the result. In any other case, the adoption is invalid, and this test fails. With no stable-index row, this test checks nothing.

Test steps and diagnostics
Given

the tactics-tie adoption document and its Fixed event rules, not the running game

When
  • a reviewer checks every within-class-rules row and every rank-ties row using stable-index
Then
  • every such row either names a second index or belongs to a class or tied group that cannot produce equal first numbers that change the result
  • In any other case, the adoption is invalid, and this test fails. With no stable-index row, this test checks nothing.
Diagnostics
  • tactics-tie-declaration-record

visible-tie-needs-class-row

declaration check: visible ties have class rows when named data is needed

scenarioonce

Applies to tactics-tie.

This declaration check reads the adoption and its Fixed event rules, not the running game. A class that can have a visible tie outside a rank tie must have a within-class-rules row when the default answer is stable-index, map-reading-order, clockwise, or declared-random. In any other case, the adoption is invalid, and this test fails. For arrival-order or no-visible-order, or with no such class, this test checks nothing.

Test steps and diagnostics
Given

the tactics-tie adoption document and the event classes its Fixed rules can tie visibly, not the running game

When
  • a reviewer compares each such class outside a rank tie with within-class-rules and the default answer to within-class-order
Then
  • every class that can have a visible tie and is not in a rank tie has a within-class-rules row when the default answer needs named data: stable-index, map-reading-order, clockwise, or declared-random
  • In any other case, the adoption is invalid, and this test fails. Where the default answer is arrival-order or no-visible-order, or no such class exists, this test checks nothing.
Diagnostics
  • tactics-tie-declaration-record

rejection-default-citations-have-rows

declaration check: a default answer that needs a citation has a row for every case

scenarioonce

Applies to tactics-tie.

This declaration check reads the adoption, not the running game. Every case that can take this default answer must have a rejection-cases row carrying a clock-rule-declared-in citation. When a row or its citation is missing, the adoption is invalid, and this test fails. If no case can take the default answer, this test checks nothing.

Test steps and diagnostics
Given

the tactics-tie adoption document and every proposal case that can take the default answer, not the running game

When
  • a reviewer compares those cases with rejection-cases rows
Then
  • every case that can take the default answer has a rejection-cases row carrying a clock-rule-declared-in citation
  • When a row or its citation is missing, the adoption is invalid, and this test fails. Where no case can take the default answer, this test checks nothing.
Diagnostics
  • tactics-tie-declaration-record

event-class-list-is-complete

declaration check: event-classes covers every event due at an instant

scenarioonce

Applies to tactics-tie.

This declaration check reads the adoption and its Fixed event rules, not the running game. event-classes must cover every event due at an instant exactly once, must name event classes rather than simulation stages, and must invert authored numbers once when the game's source convention is higher-first. When a class is omitted, when simulation stages are presented as classes, or when the numbers are reversed at runtime, the adoption is invalid, and this test fails.

Test steps and diagnostics
Given

the tactics-tie adoption and its Fixed event rules, not the running game

When
  • a reviewer compares every event due at an instant with event-classes
Then
  • every event due at an instant belongs to exactly one event-classes row
  • a game whose source convention is higher-first has inverted its authored numbers once in event-classes rather than reversing them at runtime
  • When an event class is omitted, or a list of simulation stages is presented as classes, the adoption is invalid, and this test fails.
Diagnostics
  • tactics-tie-declaration-record

event-class-citation-states-membership · reactions

declaration check: the citation for reactions names its class and members

scenarioper event-classes row

Applies to the reactions row.

This declaration check reads the adoption and tactics.reactions, not the running game. The citation must name reactions, labeled Reaction moves in the game's own words, and the events belonging to it. When the citation means something else, the adoption is invalid, and this test fails. The test names the address and restates nothing from it.

Test steps and diagnostics
Given

the tactics-tie adoption and the prose at tactics.reactions, not the running game

When
  • a reviewer checks the citation's meaning
Then
  • tactics.reactions names reactions, labeled Reaction moves in the game's own words, and the events belonging to it
  • When the citation means something else, the adoption is invalid, and this test fails.
Diagnostics
  • tactics-tie-declaration-record

event-class-citation-states-membership · movement

declaration check: the citation for movement names its class and members

scenarioper event-classes row

Applies to the movement row.

This declaration check reads the adoption and tactics.movement, not the running game. The citation must name movement, labeled Movement in the game's own words, and the events belonging to it. When the citation means something else, the adoption is invalid, and this test fails. The test names the address and restates nothing from it.

Test steps and diagnostics
Given

the tactics-tie adoption and the prose at tactics.movement, not the running game

When
  • a reviewer checks the citation's meaning
Then
  • tactics.movement names movement, labeled Movement in the game's own words, and the events belonging to it
  • When the citation means something else, the adoption is invalid, and this test fails.
Diagnostics
  • tactics-tie-declaration-record

event-class-citation-states-membership · light-attacks

declaration check: the citation for light-attacks names its class and members

scenarioper event-classes row

Applies to the light-attacks row.

This declaration check reads the adoption and tactics.light-attacks, not the running game. The citation must name light-attacks, labeled Light attacks in the game's own words, and the events belonging to it. When the citation means something else, the adoption is invalid, and this test fails. The test names the address and restates nothing from it.

Test steps and diagnostics
Given

the tactics-tie adoption and the prose at tactics.light-attacks, not the running game

When
  • a reviewer checks the citation's meaning
Then
  • tactics.light-attacks names light-attacks, labeled Light attacks in the game's own words, and the events belonging to it
  • When the citation means something else, the adoption is invalid, and this test fails.
Diagnostics
  • tactics-tie-declaration-record

event-class-citation-states-membership · heavy-attacks

declaration check: the citation for heavy-attacks names its class and members

scenarioper event-classes row

Applies to the heavy-attacks row.

This declaration check reads the adoption and tactics.heavy-attacks, not the running game. The citation must name heavy-attacks, labeled Heavy attacks in the game's own words, and the events belonging to it. When the citation means something else, the adoption is invalid, and this test fails. The test names the address and restates nothing from it.

Test steps and diagnostics
Given

the tactics-tie adoption and the prose at tactics.heavy-attacks, not the running game

When
  • a reviewer checks the citation's meaning
Then
  • tactics.heavy-attacks names heavy-attacks, labeled Heavy attacks in the game's own words, and the events belonging to it
  • When the citation means something else, the adoption is invalid, and this test fails.
Diagnostics
  • tactics-tie-declaration-record

event-class-citation-states-membership · removals

declaration check: the citation for removals names its class and members

scenarioper event-classes row

Applies to the removals row.

This declaration check reads the adoption and tactics.removals, not the running game. The citation must name removals, labeled Removals in the game's own words, and the events belonging to it. When the citation means something else, the adoption is invalid, and this test fails. The test names the address and restates nothing from it.

Test steps and diagnostics
Given

the tactics-tie adoption and the prose at tactics.removals, not the running game

When
  • a reviewer checks the citation's meaning
Then
  • tactics.removals names removals, labeled Removals in the game's own words, and the events belonging to it
  • When the citation means something else, the adoption is invalid, and this test fails.
Diagnostics
  • tactics-tie-declaration-record

within-class-grid-citation-duty

declaration check: the grid citation for Row.id supplies what its order needs from the grid

scenarioper within-class-rules row

Does not apply to tactics-tie: within-class-rules has no rows.

within-class-rule-citation-duty

declaration check: the rule citation for Row.id supplies one total order

scenarioper within-class-rules row

Does not apply to tactics-tie: within-class-rules has no rows.

rank-tie-grid-citation-duty · attack-initiative

declaration check: the grid citation for attack-initiative supplies what its order needs from the grid

scenarioper rank-ties row

Applies to the attack-initiative row.

This declaration check reads the adoption and tactics.grid, not the running game. The citation must supply what the order selected by attack-initiative needs from the grid: the first row and first column, or the center, first direction, and clockwise sense. When the citation does not meet this requirement, the adoption is invalid, and this test fails. The test names the address and restates nothing from it.

Test steps and diagnostics
Given

the tactics-tie adoption and the prose at tactics.grid, not the running game

When
  • a reviewer checks the cited Grid and direction rule
Then
  • the citation supplies the row's required first row and first column, or center, first direction, and clockwise sense
  • When the citation does not meet this requirement, the adoption is invalid, and this test fails.
Diagnostics
  • tactics-tie-declaration-record

rank-tie-rule-citation-duty

declaration check: the rule citation for Row.id supplies one total order

scenarioper rank-ties row

Does not apply to tactics-tie: no row in rank-ties matches tie-break declared random or game rule.

rejection-scale-citation-duty

declaration check: the scale citation for Row.id covers effect and cost

scenarioper rejection-cases row

Does not apply to tactics-tie: no row in rejection-cases matches result derate.

rejection-clock-citation-duty · blocked-movement

declaration check: the clock citation for blocked-movement names one advance

scenarioper rejection-cases row

Applies to the blocked-movement row.

This declaration check reads the adoption and tactics.turn-clock, not the running game. The citation must name exactly one advance caused by this refusal. When the citation does not meet this requirement, the adoption is invalid, and this test fails. The test names the address and restates nothing from it.

Test steps and diagnostics
Given

the tactics-tie adoption and the prose at tactics.turn-clock, not the running game

When
  • a reviewer checks the cited clock or turn rule
Then
  • the citation names exactly one clock advance caused by this refusal
  • When the citation does not meet this requirement, the adoption is invalid, and this test fails.
Diagnostics
  • tactics-tie-declaration-record

presentation-cue-citation-duty

declaration check: the presentation citation for Row.id supplies a finite cue

scenarioper presentation-driven-events row

Does not apply to tactics-tie: presentation-driven-events has no rows.

caused-event-citation-duty

declaration check: the caused-event citation for Row.id supplies its bound

scenarioper caused-event-kinds row

Does not apply to tactics-tie: caused-event-kinds has no rows.

interrupt-citation-duty · sword-parry

declaration check: the interrupt citation for sword-parry supplies its entry rule

scenarioper interrupts row

Applies to the sword-parry row.

This declaration check reads the adoption and tactics.parry-window, not the running game. The citation must name the validity window, the after-each-hit effect boundaries, and the stop result. When the citation does not meet this requirement, the adoption is invalid, and this test fails. The test names the address and restates nothing from it.

Test steps and diagnostics
Given

the tactics-tie adoption and the prose at tactics.parry-window, not the running game

When
  • a reviewer checks the cited interrupt rule
Then
  • tactics.parry-window names the validity window, the after-each-hit effect boundaries, and the stop result
  • When the citation does not meet this requirement, the adoption is invalid, and this test fails.
Diagnostics
  • tactics-tie-declaration-record

interrupt-replacement-citation-duty · sword-parry

declaration check: the replacement citation for sword-parry supplies every effect in order

scenarioper interrupts row

Applies to the sword-parry row.

This declaration check reads the adoption and tactics.parry-stagger, not the running game. The citation must state every replacement effect and its order. When the citation does not meet this requirement, the adoption is invalid, and this test fails. The test names the address and restates nothing from it.

Test steps and diagnostics
Given

the tactics-tie adoption and the prose at tactics.parry-stagger, not the running game

When
  • a reviewer checks the cited replacement rule
Then
  • tactics.parry-stagger states every replacement effect and its order
  • When the citation does not meet this requirement, the adoption is invalid, and this test fails.
Diagnostics
  • tactics-tie-declaration-record

orphan-case-citation-duty · homing-shot-without-target

declaration check: the orphan citation for homing-shot-without-target supplies its case

scenarioper orphan-cases row

Applies to the homing-shot-without-target row.

This declaration check reads the adoption and tactics.homing-shot, not the running game. The citation must identify the case, when it applies, the captured payload, and the source or target requirement. When the citation does not meet this requirement, the adoption is invalid, and this test fails. The test names the address and restates nothing from it.

Test steps and diagnostics
Given

the tactics-tie adoption and the prose at tactics.homing-shot, not the running game

When
  • a reviewer checks the cited orphan rule
Then
  • tactics.homing-shot identifies the case, when it applies, the captured payload, and the source or target requirement
  • When the citation does not meet this requirement, the adoption is invalid, and this test fails.
Diagnostics
  • tactics-tie-declaration-record

resolution-holds

event resolution holds for the whole run

generalonce

Applies to tactics-tie.

Across every instant of the audited run that this adoption owns in full, lower eligible ranks never resolve late, each tie and within-class order holds, each proposal follows its case or the default answer, each accepted proposal creates one payload, each valid interrupt takes effect at its entry point, each caused event follows its route, each orphan follows its row, and each ending records once at step 15. For an instant shared with another adoption, this test checks nothing. For a case that the adoption cannot construct, this test checks nothing.

Test steps and diagnostics
Holds

for every instant of the audited run that this adoption alone governs, every event resolved at or after every eligible lower-rank event; every rank tie and within-class selection followed its declared order; every refusal followed its matching case or the default answer; every accepted proposal created one payload once; every valid interrupt entered at its declared point and took effect; every caused event was created once and followed its route; every orphan followed its row; every ending was recorded once at step 15; for an instant shared with another adoption, this test checks nothing; for a case the adoption cannot construct, this test checks nothing

Seeds

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

Scope

every instant of the audited run that this adoption owns in full

Diagnostics
  • tactics-tie-instant-trace
  • tactics-tie-proposal-log
  • tactics-tie-order-trace
  • tactics-tie-collision-report
  • tactics-tie-orphan-report
  • tactics-tie-interrupt-report
  • tactics-tie-ending-record
  • tactics-tie-settlement-report
  • tactics-tie-first-ordering-violation
JSONthe adoption as one file

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

{
  "contract": "event-resolution",
  "version": 2,
  "summary": "This contract covers actions and events that happen at the same game moment. It decides their order, ties, interruptions, chain reactions, and refused actions. It also settles missing sources or targets, two removals reaching one target, simultaneous endings, and presentation delays. This contract does not cover individual effects, input buffering, clocks and pausing, displayed facts, or the simulation stages of each game update. The Input forgiveness contract covers input buffering. The Fact list contract covers displayed facts.",
  "questions": {
    "rejected-action-effect": {
      "asks": "An action cannot happen exactly as proposed. What happens by default?",
      "rationale": "The attempt may fail, spend a resource, use time, or continue at a smaller scale. This answer is the default answer. A rejection-cases row can choose another result.",
      "options": {
        "nothing-changes": {
          "meaning": "The refused attempt changes nothing. A tactics unit blocked by a closed gate keeps its stamina and its turn.",
          "semantics": "The refusal changes no game state, spends no cost, and advances no clock. Time already advancing independently may continue."
        },
        "cost-taken": {
          "meaning": "The refused attempt spends its cost and changes nothing else. A card refused after commitment still spends its mana.",
          "semantics": "The refusal applies the action's complete declared cost, produces none of the action's effects, and does not advance a clock because of the refusal."
        },
        "clock-advances": {
          "meaning": "The refused attempt moves time forward and spends no cost. A roguelike hero who walks into a wall still uses a turn.",
          "semantics": "The refusal advances one clock once, applies no cost, and produces none of the action's effects. Every case using this result cites the clock rule in its rejection-cases row."
        },
        "cost-and-clock": {
          "meaning": "The refused attempt spends its cost and moves time forward. A dungeon wand that fails uses one charge and one turn.",
          "semantics": "The refusal applies the complete declared cost and advances one clock once, but produces none of the action's effects. Every case using this result cites the clock rule in its rejection-cases row."
        },
        "derate": {
          "meaning": "The action happens with a smaller result instead of failing. A factory machine on low power makes parts more slowly.",
          "semantics": "The proposal is accepted, not refused. The scale rule cited by its rejection-cases row scales the complete effect and cost, and the event continues through this lifecycle."
        },
        "not-applicable": {
          "meaning": "Every proposed action that this adoption covers is accepted. An arcade blaster always fires because ammunition and cooldown never refuse a shot.",
          "semantics": "No action governed by this adoption has a refusal branch."
        }
      }
    },
    "within-class-order": {
      "asks": "Two events of the same class are ready at the same moment. What decides which one goes first?",
      "rationale": "A class is one kind of event in your event-classes list. The rank orders classes. It does not order two events of one class. Their order can change targets, rewards, and later chains.",
      "options": {
        "stable-index": {
          "meaning": "A named number that does not change decides the order. Strategy effects use priority first, and they use spawn number to decide a tie.",
          "semantics": "Every class with a visible tie has a row naming an integer the event holds when it enters the waiting set. The number may be the event's own or inherited from its source object, such as a relic's acquisition number or a minion's board position. Lower numbers resolve first. An optional second named integer decides between equal first numbers. When no second integer is named, equal first numbers cannot occur, or their order cannot change the result."
        },
        "arrival-order": {
          "meaning": "The event that entered the waiting set first goes first. Card effects triggered together resolve in the order they were added.",
          "semantics": "Earlier admission to the waiting set resolves first. Admission order is stable for the complete instant."
        },
        "map-reading-order": {
          "meaning": "Grid position decides the order. Tied tactics units resolve row by row, starting from the map's first row. Inside one row, they resolve starting from the first column.",
          "semantics": "The grid cited by the class or rank-tie row defines what row and column mean. Its own first row resolves first; within that row, its own first column resolves first."
        },
        "clockwise": {
          "meaning": "Positions resolve clockwise around a fixed center. Four boss hazards start from the direction the arena marks first.",
          "semantics": "The grid cited by the class or rank-tie row defines the center and the first direction. Clockwise angle continues the order, and the row's named stable index decides between events at an equal angle and distance."
        },
        "no-visible-order": {
          "meaning": "No order can change the game result. Racing-game confetti bursts may update in either order because neither affects the race.",
          "semantics": "No event in the class observes another event in the same class during the instant, and every permitted execution order produces the same game state and player-facing result. This includes the case where two such events cannot arise together."
        },
        "declared-random": {
          "meaning": "A declared random draw decides the order. Two creatures with equal speed roll to see which acts first.",
          "semantics": "Each affected class has a within-class-rules row citing the random rule and its complete draw procedure. The result may vary between runs; the cited rule, not container order, selects it."
        }
      }
    },
    "terminal-precedence": {
      "asks": "A win and a loss happen at the same moment. Which ending does the game record?",
      "rationale": "The two ending conditions may be detected by different events. This answer decides the recorded result after both conditions are known.",
      "when": {
        "row-count": {
          "terminal-collisions": "non-empty"
        }
      },
      "otherwise": "No declared ending pair can produce a win and a loss at the same instant.",
      "options": {
        "loss-only": {
          "meaning": "Only the loss is recorded. An arena hero who defeats the boss while reaching zero health loses the run.",
          "semantics": "The lifecycle records only the loss, regardless of event rank or the order in which the two ending conditions were detected."
        },
        "win-only": {
          "meaning": "Only the win is recorded. A racing driver who crosses the finish line while crashing still wins.",
          "semantics": "The lifecycle records only the win, regardless of event rank or the order in which the two ending conditions were detected."
        },
        "both-recorded": {
          "meaning": "Both endings are recorded. A card duel in which both heroes reach zero health records a shared defeat and a completed victory condition.",
          "semantics": "The lifecycle records both the win and the loss. The game's presentation rule decides whether that appears as a draw, one combined result, or two named results."
        }
      }
    },
    "presentation-timing": {
      "asks": "Can an animation or sound delay an event, and can another event pass it?",
      "rationale": "A visual delay and a rules delay are different decisions. The answer decides whether only time changes or order changes too.",
      "when": {
        "row-count": {
          "presentation-driven-events": "non-empty"
        }
      },
      "otherwise": "No animation or sound delays an event or changes the order of events.",
      "options": {
        "never": {
          "meaning": "Presentation never changes game timing or order. An attack resolves at game time even when its animation runs early or late.",
          "semantics": "No named event waits for presentation. The rows record that the game considered these events and declared that they do not wait. An adoption with no rows makes no such declaration. This question is then not asked, and no animation or sound delays an event or changes the order of events."
        },
        "may-delay": {
          "meaning": "The listed events may wait for their animation or sound. Every later event waits until they finish. A card attack resolves after its animation, and later effects keep their order.",
          "semantics": "Only events named by presentation-driven-events wait for a cue of their own. Every event behind a held event in the order waits with it, so the delay cannot let a later event resolve first."
        },
        "may-delay-and-reorder": {
          "meaning": "The listed events may wait for their animation or sound. Later events do not wait for them. A fighting-game cinematic can finish after a later poison pulse has already resolved.",
          "semantics": "Only events named by presentation-driven-events may wait. A later eligible event may resolve before a delayed event, and the delayed event keeps its original payload."
        }
      }
    },
    "two-events-one-target": {
      "asks": "Two events would each remove the same target at this moment. What happens to the second one?",
      "rationale": "The second event may vanish, still count, or create another ending. This answer is the default answer. A same-target-exceptions row can choose another result for its target kind.",
      "when": {
        "any": [
          {
            "row-count": {
              "same-target-cases": "non-empty"
            }
          },
          {
            "row-count": {
              "same-target-exceptions": "non-empty"
            }
          }
        ]
      },
      "otherwise": "No two events in this adoption can each remove the same target at one instant.",
      "options": {
        "only-first-resolves": {
          "meaning": "Only the first event affects the target. Two tactics arrows reach the last enemy, and the later arrow finds no target.",
          "semantics": "The first ordered event applies to the target and removes it. Every later event in the collision applies nothing to that target; its cost and carrier follow their own game rules. Every class that can supply two events to such a collision has a visible order. This means that its within-class-rules row, its rank-ties row, or the default answer to within-class-order gives an order other than no-visible-order."
        },
        "all-resolve-one-ending": {
          "meaning": "Every event applies, but the target is removed once. Two arena hits both count before one enemy defeat is recorded.",
          "semantics": "All events in the collision apply to the target in order. Removal is deferred until the collision group ends and is recorded once."
        },
        "all-resolve-each-ending": {
          "meaning": "Every event applies, and each event records its own removal result. Two arcade lasers can each award a break bonus from one target.",
          "semantics": "Every event in the collision applies in order and records its own declared removal consequence, even after an earlier event recorded one."
        }
      }
    },
    "caused-events": {
      "asks": "An event happening now causes another event due at this moment. When does the new event resolve?",
      "rationale": "A chain can continue immediately, join the normal order, or wait for the next game moment.",
      "when": {
        "row-count": {
          "caused-event-kinds": "non-empty"
        }
      },
      "otherwise": "No non-interrupt event in this adoption causes another event due at the same instant.",
      "options": {
        "resolves-now": {
          "meaning": "The new event resolves inside the event that caused it. A card's defeat effect draws a replacement before the next waiting card effect.",
          "semantics": "The caused event finishes inside the event that caused it, before the next waiting event. Every caused-event row cites a finite chain bound."
        },
        "waits-for-its-place": {
          "meaning": "The new event joins its normal place in the current order. An arena enemy activated early can attack later in the same moment.",
          "semantics": "The caused event resolves in its declared class's place, this instant or the next. Every caused-event row cites a finite chain bound."
        },
        "next-instant": {
          "meaning": "The new event always waits for the next game moment. A factory alarm raised now closes the conveyor gate on the next update.",
          "semantics": "Every caused event is admitted to the next instant and none of its effects resolve during the instant that created it. Every caused-event row cites a finite chain bound."
        }
      }
    },
    "interrupt-entry": {
      "asks": "An interrupt can stop or replace an event that is already happening. When does the interrupt take effect?",
      "rationale": "An interrupt needs a fixed entry point before it can stop or replace the event already in progress.",
      "when": {
        "row-count": {
          "interrupts": "non-empty"
        }
      },
      "otherwise": "No event in this adoption stops or replaces another event once it has been selected to resolve, at any point including before its first effect.",
      "options": {
        "before-next-effect": {
          "meaning": "The interrupt takes effect before the next effect of the current event. A card secret counters a spell before its damage applies.",
          "semantics": "At each declared effect boundary, a valid interrupt resolves before the next effect of the active event. The interrupt row then stops or replaces the active event."
        },
        "after-current-effect": {
          "meaning": "The current effect finishes before the interrupt takes effect. A parry accepts the sword's contact, prevents the remaining damage, and replaces it with stagger.",
          "semantics": "The active effect commits first. A valid interrupt then resolves at the following declared boundary and stops or replaces every remaining effect of the active event."
        },
        "at-normal-rank": {
          "meaning": "The interrupt joins the normal event order. A factory emergency stop waits for the safety rank, then cancels later machine work.",
          "semantics": "The interrupt joins this instant when its class is current or has not passed, and otherwise joins the next instant. When it joins the next instant, the active event finishes unchanged in this one and the interrupt stops or replaces only events of its active class that are still waiting or active when it resolves. It remains an interrupt creation path, so the caused-events answer does not govern it."
        }
      }
    },
    "orphaned-events": {
      "asks": "An event loses the source or target it needs. What happens by default?",
      "rationale": "A queued card effect or tactics shot can still be waiting after its source or target is gone. The result must not depend on which object the game removed first.",
      "when": {
        "row-count": {
          "orphan-cases": "non-empty"
        }
      },
      "otherwise": "No event in this adoption can lose its required source or target while waiting or resolving.",
      "options": {
        "resolves-what-remains": {
          "meaning": "The event still resolves with the state that remains. A card's queued effect continues after its minion leaves play.",
          "semantics": "The event keeps its captured payload. A missing source or target is unavailable, effects that require it are skipped, and every independent effect still resolves. This is the default answer unless the orphan-cases row chooses another result."
        },
        "is-canceled": {
          "meaning": "The event is canceled. A homing shot disappears when its only target leaves play before impact.",
          "semantics": "The event produces no further effect after the required source or target becomes unavailable. Costs already committed are not restored by this contract. This is the default answer unless the orphan-cases row chooses another result."
        }
      }
    }
  },
  "declares": {
    "values": {},
    "rows": {
      "event-classes": {
        "description": "The classes of events that are ordered at one instant. A lower rank resolves first. If your game resolves the higher number first, reverse the numbers once in this list, so that the class that resolves first has the lowest rank. Two classes may share a rank only when one rank-ties row declares the shared rank. An event that represents a possible ending does not record the ending when it resolves.",
        "when-empty": "An empty event-classes list is never a behavior choice: this adoption requires at least one row, and a reviewer rejects a file without it.",
        "record": {
          "id": {
            "type": "string",
            "required": true,
            "pattern": "kebab-case",
            "unique": true,
            "description": "A short name for the event class in your game's words, such as card-effects or enemy-removals."
          },
          "rank": {
            "type": "integer",
            "required": true,
            "description": "The class position, lowest first. Equal ranks are allowed only with one matching rank-ties row."
          },
          "label": {
            "type": "string",
            "required": true,
            "description": "The event class in your game's words. A card duel might use triggered effects."
          },
          "declared-in": {
            "type": "citation",
            "required": true,
            "description": "Where your game's rules name the class and the events that belong to it."
          }
        }
      },
      "within-class-rules": {
        "description": "Classes that use an order different from the default answer to within-class-order, or that must name the data that the default answer compares. A class that uses a stable number, map order, clockwise order, a declared random draw, or a custom game rule has a row whenever a visible tie can occur.",
        "when-empty": "Every event class uses the default answer to within-class-order.",
        "record": {
          "id": {
            "type": "string",
            "required": true,
            "pattern": "kebab-case",
            "unique": true,
            "description": "A short name for this class-specific order in your game's words, such as trigger-board-order."
          },
          "class-id": {
            "type": "string",
            "required": true,
            "unique": true,
            "description": "The id of the affected event class."
          },
          "order": {
            "type": "string",
            "required": true,
            "options": [
              "stable-index",
              "arrival-order",
              "map-reading-order",
              "clockwise",
              "no-visible-order",
              "declared-random",
              "game-rule"
            ],
            "description": "The class-specific order. A custom game rule is available in this field, not as the default answer."
          },
          "index-name": {
            "type": "string",
            "when": {
              "row": {
                "order": [
                  "stable-index",
                  "clockwise"
                ]
              }
            },
            "description": "For stable-index, the first named number. For clockwise, the stable number that decides between events at an equal angle and distance."
          },
          "second-index-name": {
            "type": "string",
            "required": false,
            "description": "An optional stable second number that decides between equal first numbers under stable-index. Omit it for every other order."
          },
          "grid-declared-in": {
            "type": "citation",
            "when": {
              "row": {
                "order": [
                  "map-reading-order",
                  "clockwise"
                ]
              }
            },
            "description": "Where your game's rules state the grid's own first row and first column, or the center, the first direction, and the clockwise sense. The cited rule is your adopted Grid and direction rule."
          },
          "rule-declared-in": {
            "type": "citation",
            "when": {
              "row": {
                "order": [
                  "declared-random",
                  "game-rule"
                ]
              }
            },
            "description": "Where your game's rules state the order. For declared-random, the rules state the complete draw that produces one total order for the instant. For game-rule, the rules state one total deterministic order. The cited rule must state the order itself. It must not refer back to this row."
          }
        }
      },
      "rank-ties": {
        "description": "Every rank shared by two or more event classes, with one tie-break for those classes.",
        "when-empty": "No two event classes share a rank.",
        "record": {
          "id": {
            "type": "string",
            "required": true,
            "pattern": "kebab-case",
            "unique": true,
            "description": "A short name for the shared-rank group in your game's words, such as attack-initiative."
          },
          "rank": {
            "type": "integer",
            "required": true,
            "unique": true,
            "description": "The rank shared by the named classes."
          },
          "class-ids": {
            "type": "string",
            "required": true,
            "description": "The comma-separated ids of every event class at this rank, each listed once."
          },
          "tie-break": {
            "type": "string",
            "required": true,
            "options": [
              "stable-index",
              "arrival-order",
              "map-reading-order",
              "clockwise",
              "no-visible-order",
              "declared-random",
              "game-rule"
            ],
            "description": "The order used across all events of the tied classes at this rank."
          },
          "index-name": {
            "type": "string",
            "when": {
              "row": {
                "tie-break": [
                  "stable-index",
                  "clockwise"
                ]
              }
            },
            "description": "The stable number compared across tied classes, or the stable number that decides between events at an equal clockwise position."
          },
          "second-index-name": {
            "type": "string",
            "required": false,
            "description": "An optional stable second number that decides between equal first numbers under stable-index. Omit it for every other tie-break."
          },
          "grid-declared-in": {
            "type": "citation",
            "when": {
              "row": {
                "tie-break": [
                  "map-reading-order",
                  "clockwise"
                ]
              }
            },
            "description": "Where your game's rules state the grid's own first row and first column, or the center, the first direction, and the clockwise sense. The cited rule is your adopted Grid and direction rule."
          },
          "rule-declared-in": {
            "type": "citation",
            "when": {
              "row": {
                "tie-break": [
                  "declared-random",
                  "game-rule"
                ]
              }
            },
            "description": "Where your game's rules state the order. For declared-random, the rules state the complete draw that produces one total order for the instant. For game-rule, the rules state one total deterministic order. The cited rule must state the order itself. It must not refer back to this row."
          }
        }
      },
      "rejection-cases": {
        "description": "Named action-proposal cases that override the default answer or supply the scale or clock citation that the default answer requires.",
        "when-empty": "No named case overrides the default answer. A reviewer rejects an empty list when a case with the result derate, clock-advances, or cost-and-clock can occur, because that result requires its citation row.",
        "record": {
          "id": {
            "type": "string",
            "required": true,
            "pattern": "kebab-case",
            "unique": true,
            "description": "A short name for the proposal case in your game's words, such as blocked-movement or low-power-crafting."
          },
          "result": {
            "type": "string",
            "required": true,
            "options": [
              "nothing-changes",
              "cost-taken",
              "clock-advances",
              "cost-and-clock",
              "derate"
            ],
            "description": "The result for this case. It may repeat the default answer only to carry a required scale or clock citation. A case in which the action always happens as proposed has no row."
          },
          "declared-in": {
            "type": "citation",
            "required": true,
            "description": "Where your game's rules identify this case and say when it applies."
          },
          "scale-rule-declared-in": {
            "type": "citation",
            "when": {
              "row": {
                "result": [
                  "derate"
                ]
              }
            },
            "description": "Where your game's rules say how the complete effect and cost are scaled."
          },
          "clock-rule-declared-in": {
            "type": "citation",
            "when": {
              "row": {
                "result": [
                  "clock-advances",
                  "cost-and-clock"
                ]
              }
            },
            "description": "Where your game's rules for the clock or for turns name exactly one advance that this refusal causes."
          }
        }
      },
      "terminal-collisions": {
        "description": "Pairs of win and loss conditions that can become true in one instant.",
        "when-empty": "A win and a loss cannot happen at the same instant in this adoption.",
        "record": {
          "id": {
            "type": "string",
            "required": true,
            "pattern": "kebab-case",
            "unique": true,
            "description": "A short name for this ending collision in your game's words, such as both-heroes-at-zero."
          },
          "win-condition": {
            "type": "string",
            "required": true,
            "description": "The winning condition in your game's words, such as boss defeated."
          },
          "loss-condition": {
            "type": "string",
            "required": true,
            "description": "The losing condition in your game's words, such as hero defeated."
          },
          "declared-in": {
            "type": "citation",
            "required": true,
            "description": "Where your game's rules name both ending conditions and show how both can become true at one instant."
          }
        }
      },
      "same-target-cases": {
        "description": "Target kinds for which two events can each be enough to remove the same target at one instant.",
        "when-empty": "No same-target case that uses the default answer to two-events-one-target is declared. The same-target-exceptions rows, if any, are the only declared collisions.",
        "record": {
          "id": {
            "type": "string",
            "required": true,
            "pattern": "kebab-case",
            "unique": true,
            "description": "A short name for the target case in your game's words, such as unit-under-two-arrows."
          },
          "target-kind": {
            "type": "string",
            "required": true,
            "unique": true,
            "description": "The target kind, such as arena enemies or breakable cover."
          },
          "declared-in": {
            "type": "citation",
            "required": true,
            "description": "Where your game's rules show how two events at the same instant can each remove this target kind."
          }
        }
      },
      "same-target-exceptions": {
        "description": "Target kinds that use a result different from the default answer to two-events-one-target.",
        "when-empty": "Every declared same-target case uses the default answer to two-events-one-target.",
        "record": {
          "id": {
            "type": "string",
            "required": true,
            "pattern": "kebab-case",
            "unique": true,
            "description": "A short name for the exception in your game's words, such as player-contact."
          },
          "target-kind": {
            "type": "string",
            "required": true,
            "unique": true,
            "description": "The non-overlapping target kind covered by this exception."
          },
          "resolution": {
            "type": "string",
            "required": true,
            "options": [
              "only-first-resolves",
              "all-resolve-one-ending",
              "all-resolve-each-ending"
            ],
            "description": "The result used for this target kind instead of the default answer."
          },
          "declared-in": {
            "type": "citation",
            "required": true,
            "description": "Where your game's rules name the target kind and its collision at one instant."
          }
        }
      },
      "presentation-driven-events": {
        "description": "Events whose relation to an animation or sound is explicitly declared, including events declared never to wait.",
        "when-empty": "No presentation timing rule is declared for an event in this adoption, so presentation does not delay an event or change event order.",
        "record": {
          "id": {
            "type": "string",
            "required": true,
            "pattern": "kebab-case",
            "unique": true,
            "description": "A short name for the event and presentation case in your game's words, such as card-attack-animation."
          },
          "event-class": {
            "type": "string",
            "required": true,
            "description": "The id of the event class whose timing is compared with presentation."
          },
          "presentation-cue": {
            "type": "string",
            "required": true,
            "description": "The animation or sound in your game's words, such as a card attack animation."
          },
          "declared-in": {
            "type": "citation",
            "required": true,
            "description": "Where your game's rules relate the event's game timing to its presentation cue and show that the cue finishes in finite game time or real time."
          }
        }
      },
      "caused-event-kinds": {
        "description": "Non-interrupt events that can create another event due during the instant that created it.",
        "when-empty": "No non-interrupt event creates another event due in the same instant.",
        "record": {
          "id": {
            "type": "string",
            "required": true,
            "pattern": "kebab-case",
            "unique": true,
            "description": "A short name for the chain in your game's words, such as enemy-activation-to-attack."
          },
          "event-class": {
            "type": "string",
            "required": true,
            "description": "The class of the newly caused event."
          },
          "declared-in": {
            "type": "citation",
            "required": true,
            "description": "Where your game's rules name the cause, the new event, and the finite bound on repeated caused events."
          }
        }
      },
      "interrupts": {
        "description": "Events created through the interrupt path that can stop or replace another event at a declared effect boundary, including the boundary before its first effect.",
        "when-empty": "No event stops or replaces another event after resolution begins or before its first effect.",
        "record": {
          "id": {
            "type": "string",
            "required": true,
            "pattern": "kebab-case",
            "unique": true,
            "description": "A short name for the interrupt in your game's words, such as counter-secret or sword-parry."
          },
          "active-class": {
            "type": "string",
            "required": true,
            "description": "The event class that can be interrupted."
          },
          "interrupt-class": {
            "type": "string",
            "required": true,
            "description": "The event class of the interrupt."
          },
          "boundary-granularity": {
            "type": "string",
            "required": true,
            "description": "The smallest named boundary the interrupt can use, such as before the first effect, after each hit, or at each crossed tile edge."
          },
          "result": {
            "type": "string",
            "required": true,
            "options": [
              "stop-original",
              "replace-remaining-effects"
            ],
            "description": "Whether the interrupt ends the active event or substitutes a cited set of effects for what remains."
          },
          "declared-in": {
            "type": "citation",
            "required": true,
            "description": "Where your game's rules name the validity window, the effect boundaries, and the stop result."
          },
          "replacement-declared-in": {
            "type": "citation",
            "when": {
              "row": {
                "result": [
                  "replace-remaining-effects"
                ]
              }
            },
            "description": "Where your game's rules state every replacement effect and its order."
          }
        }
      },
      "orphan-cases": {
        "description": "Events that can lose a required source or target while waiting or resolving. Each row selects its own result, so one adoption can hold cases with different results.",
        "when-empty": "No event can lose a required source or target while waiting or resolving.",
        "record": {
          "id": {
            "type": "string",
            "required": true,
            "pattern": "kebab-case",
            "unique": true,
            "description": "A short name for the missing-object case in your game's words, such as trigger-after-source-loss."
          },
          "event-class": {
            "type": "string",
            "required": true,
            "description": "The event class that may lose its required source or target."
          },
          "missing": {
            "type": "string",
            "required": true,
            "options": [
              "source",
              "target",
              "source-or-target"
            ],
            "description": "Which required game object may become unavailable while the event waits or resolves."
          },
          "resolution": {
            "type": "string",
            "required": true,
            "options": [
              "default",
              "resolves-what-remains",
              "is-canceled"
            ],
            "description": "Use the default answer, or choose another answer for this row."
          },
          "declared-in": {
            "type": "citation",
            "required": true,
            "description": "Where your game's rules identify this case. The rules say when the case applies. The rules name the captured payload, which is the set of values that the event keeps from the moment of its creation. The rules state the source or target requirement."
          }
        }
      }
    }
  },
  "rules": {
    "visible-first-needs-order": {
      "forbid": {
        "all": [
          {
            "flag": {
              "within-class-order": [
                "no-visible-order"
              ]
            }
          },
          {
            "flag": {
              "two-events-one-target": [
                "only-first-resolves"
              ]
            }
          }
        ]
      },
      "severity": "warning",
      "message": "The answer only-first-resolves needs a visible order between events of one class. Add a within-class-rules row that decides which event reaches the target first."
    }
  },
  "origin": "https://opengdd.org/contracts/event-resolution-2",
  "mechanism": [
    "This text decides the order of the steps for how a proposed action becomes a resolved event, how waiting events are ordered, where a tie is decided, where an interrupt enters, and what happens to an orphan. The questions and rows supply choices and cited game rules. They do not change the order.",
    "An **instant** is one moment the game treats as simultaneous. A **proposal** is an action requested by an actor. A **waiting event** has been accepted for an instant but has not begun resolving. An **active event** is currently applying its ordered effects. An **effect boundary** is a point named by a game rule at which an interrupt may enter; one exists before the event's first effect. An **orphan** is an event whose required source or target is no longer available. An **ending event** represents a possible match outcome, but its resolution records nothing. Step 15 is the sole outcome-recording step and reads final settled state: a single ending records there through the game's own cited rule, and a declared collision records through `terminal-precedence`.",
    "A move through three tiles can be modeled as one effect per crossed tile edge. An overwatch shot can then name the edge where it enters. Each interrupt row carries that boundary granularity instead of leaving the decomposition implicit.",
    "A turn-based game has three of the cases of this contract under its own names. A tie on initiative is the question of order at one instant. A skipped turn is a refused action. A turn boundary is an instant like any other instant.",
    "### Propose and admit",
    "1. Read the proposed action against its Fixed game rules. A matching `rejection-cases` row supplies its result; otherwise apply the default answer to `rejected-action-effect` once. `nothing-changes`, `cost-taken`, `clock-advances`, and `cost-and-clock` end the proposal without creating an event. `derate` accepts a scaled action and continues. `not-applicable` admits every proposal that this adoption covers. Repeating `nothing-changes` from the same state produces the same result, because the first refusal changed nothing. For every answer, the same proposal refused from an identical pre-attempt state produces the same result. This idempotence is an obligation of the adoption, not a description of every observed game.",
    "2. For an accepted action, commit its ordinary cost at the point named by its game rule. Create its event payload once. The payload captures every value that must stay available after a later loss of the source; uncaptured state is read only when an effect resolves.",
    "3. Assign every event to its declared class and due instant. Events already due when the instant opens enter its waiting set. An event created after the instant opens has one of two admission paths. A valid `interrupts` row creates an interrupt event; the interrupt entry answer governs it. Every other newly created event follows `caused-events`. The caused-event rows and their `otherwise` sentence exclude declared interrupts.",
    "### Order the waiting set",
    "4. Select the lowest rank among eligible waiting events. If several classes share that rank, the matching `rank-ties` row produces one total order over all events in those tied classes. A `no-visible-order` tie may use any order only because the result cannot expose it.",
    "5. Within each class's remaining events at the selected rank, apply that class's `within-class-rules` row or the default answer to `within-class-order`, and select the first event without changing its payload. Container order in the adoption file never selects an event. Where a `rank-ties` row produced one total order over the tied classes' events in step 4, that order already decides each tied class's internal sequence; a tied class under such an order carries no `within-class-rules` row, and a reviewer rejects such a row because it has no effect.",
    "6. Apply presentation waiting to the selected event. `presentation-timing.otherwise` admits it immediately. Under `may-delay`, an unfinished cue holds that event and every event behind it in the order from steps 4–5. Under `may-delay-and-reorder`, temporarily skip only the named event and return to step 4; later eligible events may pass it. Under `never`, presentation does not hold it. A held event becomes eligible again when its cited cue finishes. The instant waits and cannot close while any presentation-held event remains. Carrying it to a later instant would invent a new game-time rule, so this branch waits instead.",
    "7. Form any same-target collision group containing the selected event. Under `only-first-resolves`, only the first event keeps the target. Under `all-resolve-one-ending`, removal waits until the complete group resolves. Under `all-resolve-each-ending`, every event keeps its own removal consequence. A matching exception row replaces the default answer.",
    "### Check the event and resolve it",
    "8. Settle the same-target decision before the general orphan check. Later events suppressed by `only-first-resolves` apply nothing to that target. The other two answers keep the target available to every member of the collision group. A removal inside that group never turns another member into an orphan. Until an `all-resolve-one-ending` group ends, its target is live for non-member rules too; a non-member resolving before deferred removal therefore reads a live target.",
    "9. Check the selected event's required source and target after its collision group is known, before its first effect, and again at every effect boundary. The matching `orphan-cases` row selects its own result or the default answer to `orphaned-events`. `resolves-what-remains` skips only effects that require the missing object. `is-canceled` removes a waiting event or discards an active event's remaining effects. This continuous check covers a unit killed midway through a cast. A missing object outside the declared cases is an invalid adoption, not an implied result.",
    "10. Make the selected event active and visit the boundary before its first effect. If it continues, apply effects in the order stated by its Fixed game rule. After each effect, commit that effect before reaching the next declared boundary. An ending event follows the same ordering and boundary rules, but its resolution records nothing.",
    "11. At each boundary, discover every valid interrupt and create it through the interrupt path in step 3. When several are valid, select lowest rank first, apply the shared-rank event order when needed, then the selected class's within-class order. Under `before-next-effect`, the selected interrupt enters before the next effect, including before the first. Under `after-current-effect`, the just-finished effect remains committed. Under `at-normal-rank`, place it by the same current-or-next-instant rule as `waits-for-its-place`, then return to step 4. The active event advances only to boundaries its interrupt row permits while that interrupt waits.",
    "12. An interrupt entering at the current boundary keeps presentation waiting, same-target collision grouping, continuous orphan checks, and its own boundaries: it passes through steps 6–11 before its result changes the interrupted event. `stop-original` discards every remaining original effect. `replace-remaining-effects` discards them and installs the complete cited replacement. Already committed effects and costs remain. The cited interrupt rule supplies a finite chain bound, and only one selected interrupt resolves at a time. A presentation skip of an interrupt returns to the interrupted event's boundary, not to step 4.",
    "13. When an effect causes another event due now, apply `caused-events`. `resolves-now` resolves it at the current boundary before the active event continues. `waits-for-its-place` adds it to the current waiting set when its declared class is current or has not passed, otherwise to the next instant. `next-instant` always adds it to the next instant. Every route creates the caused event once and obeys the row's finite chain bound. A common design gives all caused events one class with the last rank and resolves that class until it is empty.",
    "14. Finish the active event. Apply any deferred target removal required by its same-target group, record each selected consequence once, and remove the event from the waiting set. Return to step 4. If only presentation-held events remain, wait for a cited cue; a `may-delay-and-reorder` skip ends as soon as that event's cue finishes. Continue until the waiting set is empty.",
    "### Settle the instant",
    "15. After the waiting set is empty, the game's own cited ending rules record any single ending that holds in the final settled state; this contract does not re-derive them. Then read each `terminal-collisions` row's `win-condition` and `loss-condition` from that state. When both hold, record only the loss, only the win, or both according to `terminal-precedence`, overriding the single-ending recording for that pair. This is the only step at which a match outcome is recorded; class rank and ending-event resolution never override it.",
    "16. Commit the instant's final state and the next-instant waiting set together. No event from the closed instant may later re-enter it. That rule never applies to a presentation-held event, because step 14 forbids closure while the event remains held. Presentation may continue after commitment only when its cited rule cannot change gameplay state or order.",
    "Every settlement report carries the instant, event id, class, rank, tie-break when used, within-class order, source and target availability, effects applied, interrupt and replacement when present, caused-event destination, removal result, and terminal result when present."
  ],
  "pack": "sha256:bb1a678a2812b734ff07d2bc2613237bda360ff8489b7f56b0e5d808bf5a6fe0",
  "answers": {
    "rejected-action-effect": "clock-advances",
    "within-class-order": "stable-index",
    "two-events-one-target": "only-first-resolves",
    "interrupt-entry": "after-current-effect",
    "orphaned-events": "is-canceled"
  },
  "values": {},
  "rows": {
    "event-classes": [
      {
        "id": "reactions",
        "rank": 0,
        "label": "Reaction moves",
        "declared-in": "tactics.reactions"
      },
      {
        "id": "movement",
        "rank": 1,
        "label": "Movement",
        "declared-in": "tactics.movement"
      },
      {
        "id": "light-attacks",
        "rank": 2,
        "label": "Light attacks",
        "declared-in": "tactics.light-attacks"
      },
      {
        "id": "heavy-attacks",
        "rank": 2,
        "label": "Heavy attacks",
        "declared-in": "tactics.heavy-attacks"
      },
      {
        "id": "removals",
        "rank": 3,
        "label": "Removals",
        "declared-in": "tactics.removals"
      }
    ],
    "rank-ties": [
      {
        "id": "attack-initiative",
        "rank": 2,
        "class-ids": "light-attacks, heavy-attacks",
        "tie-break": "map-reading-order",
        "grid-declared-in": "tactics.grid"
      }
    ],
    "rejection-cases": [
      {
        "id": "blocked-movement",
        "result": "clock-advances",
        "declared-in": "tactics.blocked-movement",
        "clock-rule-declared-in": "tactics.turn-clock"
      }
    ],
    "same-target-cases": [
      {
        "id": "unit-under-two-arrows",
        "target-kind": "unit",
        "declared-in": "tactics.arrow-collisions"
      }
    ],
    "interrupts": [
      {
        "id": "sword-parry",
        "active-class": "heavy-attacks",
        "interrupt-class": "reactions",
        "boundary-granularity": "after-each-hit",
        "result": "replace-remaining-effects",
        "declared-in": "tactics.parry-window",
        "replacement-declared-in": "tactics.parry-stagger"
      }
    ],
    "orphan-cases": [
      {
        "id": "homing-shot-without-target",
        "event-class": "light-attacks",
        "missing": "target",
        "resolution": "is-canceled",
        "declared-in": "tactics.homing-shot"
      }
    ],
    "within-class-rules": [],
    "terminal-collisions": [],
    "same-target-exceptions": [],
    "presentation-driven-events": [],
    "caused-event-kinds": []
  }
}