Skip to content

Migrate BattleRuntime move resolver to MoveRuntimeExecutionContext atomically #573

Open

Description

@Teffa14

Highest-leverage parity blocker after merged #574, #575, and #577. The failed/closed #572 and #576 experiments established that compatibility projections must be removed only after all resolver consumers migrate.

Current main: 5e514e6308e0de11b1322fbbb9e5f6625949502d
Pinned Python oracle: Teffa14/AutoPTU@16d228efa63aabecb67fa788959a359aac7f8f03

Current repository evidence

BattleRuntime.applyAuthoritativeMoveInternal still carries three independent legacy decisions:

  • spendOrdinaryMoveResources
  • runPreDamageReactions
  • declaredChoiceAlreadyValidated

The first boolean currently controls both action-budget mutation and moveFrequencyUsage().recordUse(move). That prevents the resolver from representing action-spend ownership and move-frequency ownership as independent contract dimensions.

#577 is now merged. CommittedReactionRuntimeExecutionPlan.spendOrdinaryMoveResources() derives its transitional projection from executionContext().ownsActionSpend() rather than the deprecated combined context projection. This removes one real consumer before the central migration and preserves frequency ownership as an independent dimension.

Live ingress mapping at current main

  • ordinary public/dependency-aware paths: legacy tuple true, true, false -> MoveRuntimeExecutionContext.ordinary()
  • pre-resolution target replacement: true, true, true -> MoveRuntimeExecutionContext.preResolutionResolved()
  • area target execution: false, true, false -> MoveRuntimeExecutionContext.areaResolved()
  • delayed hit execution: false, false, false -> MoveRuntimeExecutionContext.delayed()
  • committed reaction remains deferred until this migration is green -> MoveRuntimeExecutionContext.committedReaction()

Required bounded migration

  1. Replace the three resolver booleans with one MoveRuntimeExecutionContext in the same production change.
  2. Every ingress constructs its named identity explicitly: ORDINARY, PRE_RESOLUTION_RESOLVED, AREA_RESOLVED, DELAYED, later COMMITTED_REACTION.
  3. Call executionContext.requireValidDeclaration(...) exactly once before RNG consumption.
  4. Gate PRE hooks only with executionContext.runPreDamageReactions().
  5. Pass only executionContext.ownsActionSpend() to the action-budget mutation point.
  6. Record move frequency only when executionContext.ownsMoveFrequency() is true.
  7. Remove the deprecated combined context projection only after every Java consumer is migrated in the same PR. Do not repeat Remove combined move resource ownership projection #572/Remove combined execution-context resource projection #576 cleanup-first ordering.
  8. Preserve RNG draw order, accuracy/evasion, move-special ordering, type multiplier handling, post-damage hooks, HP/injury history, forced movement, semantic event ordering and final state.

Acceptance gates

  • Java Core Parity
  • Python Action Economy
  • Kairos Action Economy
  • Runtime Ordinary Damage Ingress
  • Battle RNG Ownership
  • Pre Resolution Target
  • Multi-Target Move Execution
  • Delayed Hit Execution + Delayed Hit Resource
  • Move Special Registry + Move Special Effect Roll
  • Status Move Execution + StatusController Phase Order
  • Forced Movement Instruction
  • Intercept Orchestration
  • Initiative Rebuild
  • Round Temporary Effect Expiry
  • Field Round Progression
  • relevant Trainer Feature / Held Item / history / temporary-HP gates

Do not wire COMMITTED_REACTION until this migration is green. The next slice after this issue is the first end-to-end AoO trace against the pinned Python oracle.

Activity

  1. Teffa14 commented on Sep 23, 2026

    @Teffa14
    OwnerAuthor

    Fresh main audit at e131719 confirms the resolver is still the highest-leverage action-economy blocker. BattleRuntime.applyAuthoritativeMoveInternal still accepts three independent booleans: spendOrdinaryMoveResources, runPreDamageReactions, declaredChoiceAlreadyValidated. The first boolean still controls BOTH applyResolvedMoveOutcomeInternal(... spendAction) and actor.moveFrequencyUsage().recordUse(move), so the resolver cannot yet represent split action/frequency ownership. Current typed MoveRuntimeExecutionContext already exposes the required independent decisions: requireValidDeclaration(...), ownsActionSpend(), ownsMoveFrequency(), runPreDamageReactions().

    Pinned Python oracle re-inspected: Teffa14/AutoPTU@16d228e. Its frequency.py keeps frequency as a separate rules authority with battle/round scopes, reinforcing that frequency accounting must not be inferred from action-spend ownership.

    Atomic migration map for this issue:

    1. Replace the three resolver booleans with one MoveRuntimeExecutionContext parameter in BOTH internal overloads.
    2. Every ingress constructs exactly one identity: ORDINARY, PRE_RESOLUTION_RESOLVED, AREA_RESOLVED, DELAYED, or COMMITTED_REACTION.
    3. Call executionContext.requireValidDeclaration(...) before state lookup/RNG.
    4. Pass executionContext.ownsActionSpend() only to applyResolvedMoveOutcomeInternal.
    5. Gate moveFrequencyUsage().recordUse(move) only with executionContext.ownsMoveFrequency().
    6. Gate PRE hooks only with executionContext.runPreDamageReactions().
    7. Preserve RNG order, move-special ordering, post-damage hooks, HP/history mutation, forced movement, semantic events and final state byte-for-byte for existing identities.
    8. Run Java core + Python-oracle parity, ordinary damage ingress, RNG ownership, multi-target, delayed-hit execution/resource, forced movement, intercept, move-special ordering/effect-roll, status move execution and execution-context ownership gates before merging.

    No partial BattleRuntime edit was pushed in this audit because the resolver change must be atomic; leaving mixed boolean/context ownership would create an invalid intermediate authority boundary.

  2. Teffa14 commented on Sep 24, 2026

    @Teffa14
    OwnerAuthor

    Fresh main audit after #577 merge (5e514e6308e0de11b1322fbbb9e5f6625949502d) confirms the next atomic resolver migration. Live BattleRuntime still passes three booleans into applyAuthoritativeMoveInternal: combined ordinary-resource ownership, PRE-reaction execution, and declaration-already-validated. The combined resource flag still owns both action-budget spending and MoveFrequencyUsage.recordUse, while MoveRuntimeExecutionContext already exposes independent ownsActionSpend(), ownsMoveFrequency(), runPreDamageReactions(), and declaration validation. Pinned Python oracle inspected: Teffa14/AutoPTU@16d228efa63aabecb67fa788959a359aac7f8f03.

    Bounded implementation contract for #573 remains atomic: migrate both internal overloads and every ingress together; validate the context before state/RNG; route action spend only through ownsActionSpend(), frequency accounting only through ownsMoveFrequency(), PRE hooks only through runPreDamageReactions(). Preserve target preparation, RNG order, move-special ordering, damage/HP/history, forced movement, ordered events, and final state. Keep the deprecated combined projection until all consumers in this migration are gone; remove it only as a follow-up after green parity.

    Current ingress mapping from live main: ordinary = ordinary context; pre-resolution target path = declaration already validated context; area target = area-resolved context; delayed hit = delayed context; committed reaction remains disconnected until this seam is green. Acceptance gates: Java Core + pinned Python oracle, Python/Kairos action economy, ordinary damage ingress, RNG ownership, pre-resolution target, multi-target, delayed-hit execution/resource, forced movement, intercept, move-special registry/order/effect-roll, status move/phase order, initiative rebuild, temporary-effect expiry, field progression, Trainer Feature, held-item, history and temp-HP gates.

  3. Teffa14 commented on Sep 24, 2026

    @Teffa14
    OwnerAuthor

    Fresh audit on main@5faa49993822037728731068c066296061b99eb9 after closing red #579. Re-inspected every internal docs file and all three docs/contracts files, then read BattleRuntime across the full authoritative move seam. Oracle remains Teffa14/AutoPTU@16d228e.

    Current evidence pins the highest-leverage blocker to action economy / authoritative move dispatch. BattleRuntime still has three internal overloads carrying spendOrdinaryMoveResources, runPreDamageReactions, and declaredChoiceAlreadyValidated. The terminal overload uses spendOrdinaryMoveResources twice for distinct state mutations: applyResolvedMoveOutcomeInternal(..., spendAction) and actor.moveFrequencyUsage().recordUse(move). It also selects declaration validation from the legacy tuple before the first RNG draw. Ordinary, PRE_RESOLUTION_RESOLVED, AREA_RESOLVED, and DELAYED ingress paths still construct those tuples independently.

    The next safe implementation must therefore be one atomic BattleRuntime migration, not another compatibility-removal precursor:

    • replace all three boolean parameters with one MoveRuntimeExecutionContext in every internal overload;
    • ordinary ingress -> ordinary(); pre-resolution target ingress -> preResolutionResolved(); area target ingress -> areaResolved(); delayed ingress -> delayed();
    • call context.requireValidDeclaration(...) exactly once before state lookup / rng.randIntInclusive;
    • pass context.ownsActionSpend() only to applyResolvedMoveOutcomeInternal;
    • gate moveFrequencyUsage().recordUse(move) only with context.ownsMoveFrequency();
    • gate PRE reaction resolution only with context.runPreDamageReactions();
    • preserve reactionAnchor, move-special order, post-damage order, forced movement, event composition, RNG order, and final state unchanged.

    Do not remove MoveRuntimeExecutionContext.spendOrdinaryMoveResources() in this same first implementation attempt. #579 proved that removing the compatibility projection before the resolver migration causes broad Java/Python gate failures. Remove it only after the migrated resolver and all call sites are green.

    Required regression matrix after the atomic edit: Java Core/Python oracle; Python + Kairos action economy; RNG ownership; ordinary damage ingress; pre-resolution target; multi-target; delayed-hit execution/resource; move-special registry/order/effect-roll; status move execution/phase order; forced movement; intercept; initiative rebuild; temporary-effect expiry; field round progression; related Trainer Feature, held-item, temporary-HP, combat-stage, and history gates. No merge until relevant CI is green.

  4. Teffa14 commented on Sep 24, 2026

    @Teffa14
    OwnerAuthor

    Fresh audit on main@c6e1bcd11b45ab2f609c6781f4fbe7a0c512ce65 and pinned Python oracle Teffa14/AutoPTU@16d228e. All 86 workflows on the current main head are completed with zero failures. Re-read every current docs file and all three runtime contracts before inspecting the live resolver.

    Exact production seam confirmed in BattleRuntime.java: ordinary dependency-aware ingress still passes (true,true,false); pre-resolution-resolved passes (true,true,true); area-resolved passes (false,true,false); delayed passes (false,false,false). The three applyAuthoritativeMoveInternal overloads still transport spendOrdinaryMoveResources/runPreDamageReactions/declaredChoiceAlreadyValidated. The terminal overload still branches declaration validation from that tuple before RNG, passes spendOrdinaryMoveResources to applyResolvedMoveOutcomeInternal for action spending, and reuses the same boolean for moveFrequencyUsage().recordUse(move).

    MoveRuntimeExecutionContext/Policy/DeclarationValidator already provide the replacement contract: requireValidDeclaration(...), ownsActionSpend(), ownsMoveFrequency(), runPreDamageReactions(), plus explicit ORDINARY/PRE_RESOLUTION_RESOLVED/AREA_RESOLVED/DELAYED/COMMITTED_REACTION identities. No additional registry family is needed before the migration.

    Prepared branch codex/573-runtime-context-production-guard from the exact green main head. Do not remove spendOrdinaryMoveResources() first (the closed #579 proved that order breaks broad parity). Next safe production commit must migrate all internal overloads and all four existing ingress identities together, preserve RNG/hook/event ordering, then run the full relevant parity matrix before wiring COMMITTED_REACTION.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions