Balance experiment protocol, 12 September 2026
TASK-107 tests six recommendations separately and in combinations. The study changes no playable rule or catalog value.
Baseline revision e116261 defines the control. The work stays on codex/corporations-launch-platform-weapons.
Ranged Accuracy +2 remains fixed. Current movement, offensive Enraged, and Cooling Tender remain fixed.
Every experimental mechanic uses an optional rule, policy, or cloned catalog. Omitted options must preserve complete baseline events and RNG state.
BUG-017 remains unresolved for the playable default. Experimental Momentum support does not replace the live D3 wording.
Experimental melee mechanics
Rules.meleeOpportunityTarget sets a common base target for offensive opportunity dice. The candidate compiler accepts 10, 11, and 12.
The engine accepts only integers from 1 through 20. An omitted value uses effectiveEvasion.
The experimental target equals the common target plus current Evasion changes from the base stat. This preserves Prone and destroyed Legs penalties.
The target formula is common target + effectiveEvasion(target) - target.stats.evasion. Shield dice keep their current threshold.
Rules.meleePilotingDice controls the current floor(Piloting / 2) pool bonus. false removes only this bonus.
An omitted value preserves the current pool. Chassis class, Handling, weapon, and shield dice remain unchanged.
Rules.meleeMomentum enables the experimental D3 application. The fight caller must also pass momentumEligible: true.
Only fighter index 0 can receive this modifier. That fighter must initiate during its own activation.
The pilot must have active D3 Momentum. The fighter must move at least three hexes during that activation.
The caller supplies eligibility explicitly. The exchange does not infer eligibility from the defender or stored movement alone.
Momentum gives 1[+] to the first actual offensive pool die. It never affects shield dice or later offensive dice.
The first offensive source can have zero dice. In that case, Momentum advances to the next actual offensive die.
The first die resolves its normal modifier pool with one added accuracy die. Normal cancellation applies.
The engine rolls that modifier before the d20. Enraged and command rerolls then reuse the same resolved modifier.
If the source has more dice, the engine resolves its ordinary condition modifier once after the first die. Remaining d20s use it.
Later sources keep their current modifier and d20 order. Defender and shield sources keep their current order.
Optional event metadata identifies the Momentum die. Baseline events contain no new keys.
MeleeExchange accepts final options { rules?: Partial<Rules>; momentumEligible?: boolean }. Existing positional arguments stay compatible.
meleePoolSources accepts an optional rules argument. Existing callers without that argument keep the current result.
Existing fight callers default to ineligible. Proactive TurnController and bot callers supply explicit eligibility.
Charge carries explicit proactive context into the fight. Reactions always supply false.
Complete event and RNG parity is a release gate for absent options. No change enters resolveFire.
Paid forms and prospective melee threat
AiOptions.paidForms enables deterministic weapon form choices. It defaults to false.
GameState.changeWeaponMode(mech, hand, modeId) owns validation, mutation, and the existing weapon_mode event. It returns true on success.
The method returns false without mutation or an event when the action is invalid. Its event payload stays unchanged.
TurnController and AI retain action-budget ownership. Each spends one Quick action only after a successful form change.
A successful form change removes Full eligibility. It cannot enable a Full melee exchange during that activation.
The bot compares the best current-turn attack value with its remaining budget. It uses the existing attack valuation.
That valuation is a legacy single-profile approximation. It does not model common-TN or Piloting-pool experiment toggles.
The complete exchange applies both toggles. Every final report must retain this valuation limit.
The bot adds half the best next-activation melee preparation value at its current or planned endpoint. This is a finite heuristic forecast.
The bot evaluates each alternate form after paying one Quick action. It cannot use a future random result.
The alternate must strictly improve the combined value. A tie keeps the current form.
Further ties use right hand, left hand, back, then catalog mode order. Back forms remain unsupported by the current action.
The bot changes each hand at most once per activation. The shared fired-hand set survives every change.
A switch never enables a second Fire from the same hand. It never restores Full eligibility.
This policy measures paid access under one greedy heuristic. It does not establish optimal form play.
AiOptions.prospectiveMeleeThreat extends position threat. It defaults to false.
For each enemy, enumerate legal next-activation Charge and Move-to-contact endpoints. Use actual movement, obstacles, occupancy, and sight.
With two Quick remaining, a full-melee-capable projection includes a legal adjacent Fight or legal moved Charge.
With one Quick remaining, include Move-to-contact reach only.
One Quick cannot project a Full Charge or Fight. Zero Quick adds no new movement threat.
Keep legacy current and adjacent threat unchanged. Label projections as full-melee-capable or contact-only in diagnostics.
Evaluate occupancy after the acting unit moves to the scored candidate hex. Vacate its old hex and block its new hex.
The estimate uses the enemy's current Speed, parts, forms, and available melee sources. It uses no future random result.
Cache queries by enemy, scored candidate hex, and relevant state. Reuse repeated identical queries during the activation.
Shared terrain data can use one activation cache. Invalidate all prospective-threat caches between activations.
For each enemy, use the larger current threat and reachable melee threat. Never add both values for one enemy.
Apply the existing aggression threat weight. Keep all other position scoring and tie breaks unchanged.
An opening diagnostic runs on the current symmetric maps. It records replays and counterfactual bot choices from identical states.
The diagnostic identifies changed choices and reachable threats. It does not claim optimal AI or human testing.
When both AI options are absent, the default decision branch and complete event stream remain byte-for-byte equal.
Candidate catalog and legal refits
data/experiments/2026-09-12-balance-candidates.json owns every experimental value and condition ID.
A pure engine helper clones the supplied catalog. It never mutates the source catalog.
The control clone must equal the source catalog. Each candidate changes only its declared fields.
hammer2 changes the Hammer meleeDice value from 1 to 2. It changes no weapon damage, Pen, Heat, or Weight.
core-plus2 adds 2 Durability to the Light, Medium, and Heavy chassis. core-plus4 adds 4 to the same chassis.
These candidates change no other chassis field. Their deployed Core maximums and total Hull reflect the clone.
blade-cap5 changes Duelist arm Capacity from 7 to 5. blade-cap4 changes it from 7 to 4.
The compiler verifies default equipment and every active form against the cloned catalog. Matching source profiles remain equal.
Capacity candidates keep every original legal build unchanged. An illegal original remains in the manifest with its errors.
Only an illegal original enters automatic refit selection. The compiler enumerates all 121 ordered arm pairs for that build.
Duplicate arm IDs remain legal when the catalog allows them.
Relevant Handling sums the arms that support occupied weapon hands. Both arms count for a two-handed item.
Both arms count for dual weapons. An empty hand adds no relevant Handling.
Filter complete-build candidates through validateBuild first. Rank them by this tuple:
- Maximize combined relevant Handling.
- Minimize changed-arm count.
- Minimize total Weight.
- Maximize combined arm Durability.
- Sort by right-arm ID, then left-arm ID.
Keep enough tied and frontier candidates to preserve every possible Team Cap solution. Never prune a possible legal roster combination.
Do not prune a row with a dominated on the declared Capacity/Handling/Weight/Durability axes label.
Choose the complete roster after build enumeration. Validate it with validateTeam.
If independent build optima exceed Team Cap, rank legal roster combinations by this tuple:
- Maximize summed relevant Handling.
- Minimize total changed-arm count.
- Minimize total Weight.
- Maximize combined arm Durability.
- Sort by roster build IDs and ordered arm IDs.
Record the chosen substitutions, rejected independent optima, original errors, final errors, and ranking values.
Never silently remove an illegal family. A candidate with no legal refit fails before any simulation.
A standalone catalog frontier covers Light, Medium, and Heavy bodies. It compares every relevant ordered arm pair.
The frontier reports Capacity, Handling, Weight, Durability, Armor, Size, equipment support, and build legality.
Use the label dominated on the declared Capacity/Handling/Weight/Durability axes.
This label excludes Armor and Size. It cannot establish a strictly better arm or build.
This report tests whether lower Duelist Capacity creates a useful sacrifice. It is not a whole-game balance verdict.
Candidate structure
The primary mechanical screen is a four-factor factorial. Its active levels are tn11, hammer2, blade-cap4, and core-plus2.
The factorial contains all 16 combinations, including control. Condition IDs list factors in the declared manifest order.
tn10, tn12, blade-cap5, and core-plus4 are separate dose singles. They do not enter the primary factorial.
pilot-pool-off is a separate melee sensitivity. It does not enter the full mechanical bundle automatically.
The full mechanical bundle is tn11+hammer2+blade-cap4+core-plus2. It has all four primary factors active.
Momentum, paid forms, and prospective melee threat form a separate three-factor policy panel. The panel has eight combinations.
Run those eight combinations on control mechanics and the full mechanical bundle. Reuse identical condition keys instead of rerunning overlaps.
Complete-exchange isolates
Use 21 total mechanical profiles. These are the 16-profile factorial, four dose singles, and pilot-pool-off.
All complete-exchange, repeated Core and disarm, and Momentum Charge fixtures omit launch platforms.
Keep policy-only factors out of these isolates. Their identical mechanical rows reuse the same profile results.
For each profile, rerun the prior 15-package melee round robin. Include all 120 unordered package pairs and both initiators.
Use 256 paired blocks, seeds 101000 through 101255. The package matrix gives 120 × 2 × 256 = 61,440 rows.
Preserve the prior Light Idris against Medium Ruko panel. Use Dual Blades, Great Sword, and Guard Lance.
Cross both initiators on the same blocks. The sensitivity gives 3 × 2 × 256 = 1,536 rows.
Add a matched Ruko class panel for the same three weapons. Use Light, Medium, and Heavy chassis.
Keep Targeting head, two Duelist arms, Sprint legs, and Bare back fixed. Apply candidate refits when needed.
Run six unordered class pairs, including mirrors. Both initiators give 6 × 2 × 256 = 9,216 rows.
The complete-exchange isolates contain 72,192 rows per profile and 1,516,032 rows total.
Record initial usable offensive opportunities and initial usable shield opportunities as separate counts.
A fighter has zero available response only when both counts are zero. Zero offensive opportunities alone do not establish it.
Record actual first spender, declarations, accepted strikes, parries, and mutual survival.
Actual defender participation is true after at least one accepted strike or parry. A shield parry counts.
Also record arm disables, Core loss, body damage, Heat, stress, deaths, exchange length, and available choices after first strike.
Use each package's existing deterministic response policy. Resolve all strikes, parries, damage, criticals, and self Heat.
Delivery and movement remain outside these adjacent isolates. Do not convert survival payoff into a whole-game win rate.
Repeated Core and disarm isolates
Use a clear flat board at elevation 0. Place ranged opponents six hexes apart.
Place melee opponents adjacent at distance one.
The attacker uses Medium chassis and Ruko. It uses Targeting head, two Duelist arms, Sprint legs, and Bare back.
Equip Rifle, Cannon, Heavy Bowgun, Blade, Great Sword, or Hammer. Apply the declared legal refits.
Use stock Courier with Vex, Foreman with Ilse, and Bastion with Sato as targets.
Load their exact original builds from data/kits.json. Start every target intact and record each source build in the manifest.
Every package uses its Standard load with unlimited ammunition. Do not use HE, AP, or alternate forms.
Use Rifle, Cannon, and Heavy Bowgun for ranged attacks. Use ordinary Fire, Aimed Core, and Aimed disarm as separate policies.
Use Blade, Great Sword, and Hammer for melee. Use targeted Core and targeted disarm as separate policies.
Apply each policy through public engine actions and budgets. Do not call a substitute damage helper.
Begin each attacker activation through the normal engine lifecycle and Budget.
When at least one Quick remains, compare current Heat plus the main weapon's printed Heat with Heat Cap.
If that sum exceeds Heat Cap, Vent once and end the activation.
Otherwise, perform exactly one declared attack when it is legal and the Budget permits it.
Fire spends one Quick. Aimed attacks and Fight spend Full.
If the attack is unavailable, pass and end. Record the exact reason, including lost Full, jam, or no usable source.
Do not make a fallback attack. Do not grant free or replacement Budget.
Afterward, begin and end one passive target activation through the normal lifecycle.
The target takes no action. Do not run a bot turn or round scoring.
Preserve Heat, damage, ammunition, part state, and status state between activations.
For disarm, choose the first intact support arm in right-then-left order for a damaging held weapon.
A two-handed weapon remains a disarm target until both support arms are destroyed.
Ignore shields, back equipment, and other non-damaging equipment. Target Core after no qualifying support arm remains.
Reevaluate ranged disarm before each declared attack. Reevaluate melee disarm before each actual strike.
Inside Fight, keep the existing automatic strike and parry policy for both fighters.
The target keeps its existing Core declarations. Only the attacker's declared body location follows its strategy.
Use an optional target-selection callback on MeleeExchange.auto when needed. Omission must preserve its exact default.
Do not copy the automatic melee decision policy into the CLI.
Apply normal exchange self Heat. Do not add a per-strike Heat Cap check.
Stop when either combatant is destroyed or after 16 attacker activations. Preserve the existing censor definition.
For each target event, restricted time uses the attacker activation that contains the observed event.
When that event is absent, restricted time is 16. Early attacker destruction does not shorten this value.
Record attacker destruction at its observed attacker activation.
Use event order to record attacker destruction before target destruction and before weapon disable separately.
Record mutual destruction separately when both combatants are destroyed in the same trial.
Record both combatants alive at the 16-activation horizon as a separate censor state.
Any event-time estimate among observed events is additional. Label it as conditional on the observed target event.
Run all 45 package-target-policy cells for every mechanical profile. Use seeds 101000 through 101255.
The ranged panel gives 27 cells. The melee panel gives 18 cells.
Each profile gives 45 × 256 = 11,520 trials. The complete panel contains 241,920 trials.
Record attacks, activations, action costs, vents, Heat, disables, and destruction.
Report Core destruction and weapon disable as competing outcomes. Keep no-kill and no-disable horizons visible.
Post-destruction activation metrics come only from the whole-game observer. The passive fixture cannot measure them.
Momentum Charge fixtures
Use active D3 Momentum with a fielded qualifying teammate. Compare Charge movement of two and three hexes.
Cross experimental Momentum off and on. Use 256 paired blocks, seeds 111000 through 111255.
The four cells contain 1,024 Charge fixtures. Each fixture resolves one complete exchange.
All isolates contain 1,516,032 + 241,920 + 1,024 = 1,758,976 rows.
Record exact modifier and d20 order, the affected opportunity, cancellations, rerolls, and final RNG state.
The two-hex row is the eligibility control. The disabled-rule row is the experimental-option control.
Pilot panel
The pilot panel begins only after the experimental melee option passes engine review.
Keep the original equipment slots fixed: Rifle and Shield, Cannon, then Blade and Shield.
Keep every earlier pilot assignment fixed:
| Assignment | Rifle and Shield | Cannon | Blade and Shield |
|---|---|---|---|
| Original mixed | Vex Adair | Ilse Brandt | Ruko Han |
| Hauer | Bram Holt | Anja Dorn | Ilse Brandt |
| Yutani | Mika Rell | Tavi Sen | Vex Adair |
| Free Salvage Union | Marrow | Ruko Han | Rivet |
| Dioscuri | Idris Kade | Sato Ren | Dara Voss |
| B&B | Sera Quill | Q | Len Oris |
The original mixed assignment is the fixed opponent. It is not a candidate package.
Create independent control-only copies of Vex, Ilse, and Ruko. Use IDs study-control-vex, study-control-ilse, and study-control-ruko.
Copy each native card without changing its name, Faction, Specialty, stats, Initiative, or ability.
Rewrite only Bond references whose targets also have control aliases. This preserves the semantic control Bond graph.
Record the alias-to-source map and every exact source and copied card in the manifest.
Every control build uses these aliases in every pilot cell. Candidate faction builds keep the original pilot IDs.
For each faction assignment, cross native stats with all Gunnery, Piloting, and Systems values set to 1.
Cross each faction assignment with all abilities on and all abilities off.
Keep pilots, slot assignments, Bonds, and all noncombat card fields fixed. Record the full assignment and transformed cards.
The Yutani assignment also crosses Pace, Momentum, and Scramble independently. Include all eight toggle combinations at both stat levels.
Reuse the Yutani all-on and all-off rows from the common ability cross. This gives 32 unique pilot configurations.
Ability instrumentation counts only explicit observed events. Missing event support produces null, never an inferred zero.
Rules.disabledPilotAbilities lists pilot IDs whose abilities are disabled. Omission preserves current ability access.
All ability checks use one GameState gate. Disabled pilots keep their stats, Faction, Specialty, and Bonds.
Ability-off candidates list all three selected pilot IDs. Ability-on candidates omit the field.
Normalization and disabled ability lists target original pilot IDs only. They never target control aliases.
Normalized-stat clones set only the selected trio's Gunnery, Piloting, and Systems values to 1.
Native-stat clones preserve every pilot card. Both clone types preserve all unselected pilots.
The native alias control must match original derived stats and ability access. Its deterministic combat can differ only in declared identity fields.
Record actual form changes, Momentum dice, Lock On Scramble effects, and every other explicit ability event.
Keep Pace use null while no explicit event identifies it. Do not add a default event only to create a counter.
Do not infer ability use from access, position, eligibility, or a changed outcome.
Package means do not authorize a pilot nerf. No result establishes a faction tier.
Whole-game roster panel
Use the prior six whole-game rosters and one Light Duelist roster. Keep every prior roster definition unchanged.
Every whole-game cell deploys two real targetable Cooling Tenders, one for each side.
Set platformTargets: true for both AI sides. Retain the prior baseline platform positioning, movement, and target selection policy.
This setup applies to training, screen, confirmation, pilot, and opening-diagnostic cells. It is not an experiment factor.
The Light Duelist roster keeps the new-melee pilot slot order. It uses Dual Blades, Great Sword, and Hammer.
Each body uses Light chassis and Targeting head. This makes Hammer2 active in whole-game measurement.
Each body starts with two Duelist arms, Sprint legs, and Bare back. Capacity candidates use the declared legal-refit algorithm.
Use all 28 unordered roster pairs, including seven mirrors. Use Quarry, Causeway, and Foundry.
Cross physical orientation 0 and 1 with explicit First Side A and B. This gives four assignment cells.
Every map, pair, seed, and condition contains all four cells. Mirror rows remain separate physical and First Side diagnostics.
Train the baseline on aggression values 0, 0.25, 0.5, 0.75, and 1 for both sides.
Use two blocks, seeds 120000 and 120001. The grid contains 16,800 games.
Select one policy per map and pair with the existing solver. Preserve pure equilibria, mixed equilibria, and current tie rules.
Extend the same bounded solver and independent Python validator from 3 by 3 to 5 by 5. Keep its selection order unchanged.
The screen transfers these baseline-selected policies to every profile. No candidate retrains before selection.
Use two fresh screen blocks, seeds 130000 and 130001. Each profile contains 672 games.
The screen has 35 unique profiles and 23,520 games. It includes the declared mechanical and policy panels.
The original mixed pilot trio leaves D3 abilities inactive in the seven primary rosters. Momentum is a negative control there.
Evaluate actual Momentum in qualifying Charge fixtures and the pilot panel.
Whole-game part-loss observer
For each mech, find its first part_destroyed event for rightArm, leftArm, or legs.
Start the observation window after that event. Exclude the activation that contains the event.
Report whether the mech survives until its next own activation_start.
Report false when mech_destroyed occurs first. Report censoring when the game ends first while the mech remains alive.
Count every subsequent own activation through game end. Count each activation once in each applicable action category.
Attempted attack includes proactive Fire, platform Fire, Fight, Grapple, or Throw by the active mech.
Actual movement requires a move event by the active mech with at least one hex.
Support includes Lock On, Prepare, Brace, Guard, Smoke, Overwatch, weapon mode change, or platform movement.
Attack, movement, and support categories can overlap within one activation.
Maintenance-only requires Vent or Stabilize and no attempted attack, actual movement, or support action.
Attribute only events from the active mech. Exclude every off-turn reaction from own-activation measures.
Record the no-later-activation censor separately. Do not treat snapshot legality as an action metric.
Opening-choice diagnostic
Use eight policy profiles. These are all Momentum, paid-form, and prospective-threat combinations on control mechanics.
Use all seven roster mirrors. Add new-ranged against mobile and legacy against Light Duelists.
Use all three maps and all four assignment cells. Use seeds 140000 through 140007.
The diagnostic contains 8 × 9 × 3 × 4 × 8 = 6,912 whole games.
Apply the frozen baseline-selected aggression policy for each map and pair. Sample mixed policies before each matched block.
Record both opening rounds. Include activation order, choices, movement, attacks, damage, scoring, and reachable threats.
Retain complete replay commands for every game.
Record the counterfactual bot choice from the same opening state with only the tested policy option changed.
Counterfactual choice evaluation consumes no combat RNG. It does not replace the executed game row.
This panel diagnoses opening policy effects on symmetric maps. It does not claim human testing or optimal play.
Prospective screen selection
Calculate each roster's payoff against its six distinct opponents. Weight opponents and maps equally.
The primary spread is the maximum minus minimum of the seven roster means. Lower is better.
A profile passes the round guard when mean rounds increase by no more than 0.75 against baseline.
It passes the no-death guard when no-death games increase by no more than 10 percentage points.
It passes the activity guard when its proactive attack count is at least 75 percent of baseline.
For each profile, find the largest absolute First Side bias across all mirror and map cells.
It passes the mirror guard when this maximum exceeds the control maximum by no more than 10 percentage points.
Preserve physical-side bias, First Side bias, and every mirror row. Physical-side bias remains a separate fairness audit.
Choose one qualifying standalone with exactly one changed factor. Include all declared dose singles in this comparison.
Choose one qualifying combination with at least two factors. Rank qualifying candidates by lower spread.
Break ties by fewer active factors, then lexical condition ID. Record every guard result before selection.
If no profile qualifies, choose the profile with the fewest failed guards. Then use spread, factor count, and lexical ID.
Label a fallback as a failed-screen exploratory selection. It does not become an accepted candidate.
Melee defender agency, Hammer accepted damage, and Core disable timing remain separate mechanism diagnostics.
Form payments, threat-changed choices, Momentum dice, and pilot ability events also remain separate. Do not collapse them into payoff.
Selected-policy training and confirmation
Retrain only the selected standalone and selected combination. Keep the validated baseline grid for the control.
Use the same 5 by 5 grid and seeds 120000 and 120001. The two new grids contain 33,600 games.
Freeze all 84 map-pair policies for each candidate. Do not inspect confirmation rows before both policy artifacts validate.
Confirmation compares baseline, selected standalone, and selected combination. Each uses its own frozen aggression policy.
Use 16 untouched blocks, seeds 230000 through 230015. The confirmation contains 16,128 games.
Do not stop, extend, replace, or add a candidate from confirmation outcomes. Report every declared cell.
Pilot whole-game panel
Cross 32 unique pilot configurations with experimental Momentum off and on. Cross control and the selected combination's mechanical subset.
The selected mechanical subset can equal control when selection contains only policy factors.
This gives 128 pilot conditions. Each condition plays against the fixed original mixed roster.
Enable paid forms and prospective melee threat in both mechanical arms. Keep every other AI factor equal.
Use aggression 0.5 for both sides. Do not train or transfer a selected policy.
Use Quarry, Causeway, and Foundry. Keep four physical-orientation and explicit-First-Side cells.
Use eight blocks, seeds 310000 through 310007. The panel contains 12,288 games.
Keep the same seeds across matched stats, abilities, Momentum, and mechanics. Preserve the full pilot manifest.
Keep both context labels when their mechanics match. Reuse identical deterministic evidence and label that reuse explicitly.
This fixed-policy panel measures controlled package contrasts. It does not establish equilibrium, human counterplay, or faction rank.
Fixed scope and game counts
Primary whole-game work contains 16,800 baseline training games and 23,520 screen games.
It adds 33,600 selected-policy games and 16,128 confirmation games. The primary total is 90,048.
The pilot panel adds 12,288 games. The opening diagnostic adds 6,912 games.
The complete whole-game maximum is 109,248. The isolate maximum is 1,758,976 rows.
Run a timing feasibility check before production. It can reduce a declared fixed dose panel before outcomes.
Record any reduction in this protocol and rebuild every affected manifest. Never change scope after outcome inspection.
Common execution controls
Complete the protocol, manifest, and analysis contract before reading any new outcome aggregate.
Freeze the complete source closure before each production stage. Include engine, harness, analysis, data, maps, protocol, and lockfile inputs.
Record the full revision and SHA-256 hash for each file. Record the normalized candidate catalog and roster hashes.
Write exact commands into every manifest. Refuse to overwrite an existing artifact.
Store one keyed raw row per exchange or game in gzip JSON Lines. Hash compressed and uncompressed content.
Validate unique factorial keys, declared row counts, seed coverage, assignments, legal builds, and complete cells before analysis.
Construct every trial from a typed Scenario. Assert that every numeric mech and platform state field is finite.
Run the finite-state assertion before execution and after completion. Fail the phase before analysis on any invalid value.
Repeat one declared row per stage. Require identical output hashes, events, and final RNG state.
Use common random seed blocks within each paired comparison. Use disjoint seed ranges across selection stages.
Sample mixed policies with a separate deterministic RNG. Key each uniform variate by phase, map, pair, seed, and assignment.
Reuse each keyed uniform variate across matched candidates. Map it through each candidate's own frozen policy distribution.
Record each uniform variate and the selected aggression actions. Different candidate distributions can select different actions.
Never consume combat RNG to select a policy.
Bootstrap complete seed blocks. Keep maps, orientations, First Side, policies, candidates, and counterfactuals linked inside each block.
Report paired candidate-minus-control intervals. Do not treat executions inside one seed block as independent samples.
For every variable-denominator metric, resample shared complete seed blocks.
Within each resample, pool all numerators and denominators before division. Include blocks with zero eligible observations.
Do not average seed-level rates or remove zero-observation blocks.
For paired ratios, compute both pooled ratios from the same resampled blocks before subtraction.
A paired resample is valid only when both pooled denominators are nonzero.
If either observed pooled denominator is zero, report the comparison as null.
Report each observed denominator, the valid-resample count, and the zero-denominator resample count.
Use the current observer, equilibrium solver, archive writer, seed checks, and hash validation. Extend them when a required field is absent.
Complete melee isolates through MeleeExchange. Whole-game harnesses call public engine actions.
No CLI helper may reproduce attack, movement, form, or melee resolution.
An independent reviewer checks engine behavior before any engine commit. A separate reviewer checks final evidence and claims.
Interpretation and delivery
Report isolated candidates, the mechanical factorial, dose singles, policy combinations, pilot ablations, and legal frontiers separately.
Keep map, matchup, orientation, First Side, and policy results visible. Report mirrors before pooled balance claims.
Report interactions against the matching component-alone controls. Do not attribute a bundle result to one factor.
Record all completed candidates, including unfavorable and inconclusive results. Do not extend sampling after result inspection.
Simulation evidence describes the declared bots and policy grid. It does not establish human counterplay or optimal play.
The final report can recommend adoption, more testing, or no change. This study itself adopts nothing.
If a playable default is later adopted, update the rulebook, GDD, README, cited tuning notes, and register in a separate change.
Keep BUG-017 open unless that separate decision adopts the experimental Momentum behavior as the playable default.