diff --git a/LUMEN_TASK_HSR_RUNTIME_ARCH_044.md b/LUMEN_TASK_HSR_RUNTIME_ARCH_044.md new file mode 100644 index 0000000..5e20761 --- /dev/null +++ b/LUMEN_TASK_HSR_RUNTIME_ARCH_044.md @@ -0,0 +1,159 @@ +# HSR-RUNTIME-ARCH-044 — Reviewed Static GrantExtraTurn Golden Fixture + +## Execution routing +- ChatGPT model: **GPT-5.6 Terra** +- Codex reasoning: **High** + +## Current confirmed state +- Canonical accepted `main`: `b3e9f7385f08dd13e53caa00a2b921dd1b500471`. +- HSR-RUNTIME-ARCH-043 is accepted. +- Post-merge main CI run #264 passed: + - pytest: `1713 passed in 10.07s` + - legacy regression: `20/20` + - trace evidence: `2/2` + - runtime action-session Golden regression: `9/9` +- Accepted `GrantExtraTurn` simulator behavior appends target IDs to `extra_turn_stack`, emits one post-append `extra_turn_queued` event per resolved target, and preserves existing LIFO resolution. +- `RuntimeExtraTurnQueuedObservation` and the legacy adapter mapping are already accepted. +- Real HSR hidden extra-turn scheduling priority/FIFO/LIFO remains outside this task and unresolved. + +## Objective +Add one independently authored, manually reviewed, compact canonical runtime Golden expectation for a deterministic one-target `GrantExtraTurn` production action and prove accepted ARCH-016 end-to-end validation matches it. + +This task adds evidence only. It must not change simulator semantics or promote the new fixture into the runtime regression manifest. + +## Required implementation +1. Add exactly one static fixture: + - `hsr_axis_sim/data/runtime_golden_fixtures/arch_044_reviewed_grant_extra_turn_expected.json` + - compact canonical UTF-8 JSON + - no trailing newline + - metadata: + - `construction: manual-reviewed` + - `fixture_id: arch-044-reviewed-static-grant-extra-turn` + - `purpose: grant-extra-turn-end-to-end-golden` + - trace ID: `arch-044-reviewed-static-expected` + - exactly three records, sequence `0,1,2`: + 1. `ACTION_START` + 2. `EXTRA_TURN_QUEUED` + 3. `ACTION_END` + - adapter stream ID: `arch-044-reviewed-axis` + - action ID: `reviewed-grant-extra-turn` + - actor ID: `extra-turn-actor` + - target ID: `extra-turn-target` + - queued observation must be exactly: + - `target_id: extra-turn-target` + - `stack_depth_before: 0` + - `stack_depth_after: 1` + - record-level `numeric_values` remains empty. +2. Add one focused test module: + - `hsr_axis_sim/tests/test_runtime_arch_044_static_grant_extra_turn_golden_fixture.py` +3. Build production actual only through the accepted ARCH-016 action-session path using one production `Action` with `GrantExtraTurn(target_ids=["extra-turn-target"])`, `ends_turn=False`. +4. Pin the static fixture's exact byte length and SHA-256 in the focused test after the file is finalized. +5. Prove the exact static fixture schema/event IDs/actor/action/target/payloads and adapter binding. +6. Prove production validation matches the static fixture and leaves the queued target on `extra_turn_stack` without changing target AV. +7. Add one controlled mismatch by preloading one valid queued unit ID in `BattleState.extra_turn_stack` before the same action. The accepted comparator sorts mapping keys, so the exact first divergence must be: + - record index: `1` + - path: `/event/payload/extra_turn_queue/stack_depth_after` + - expected: `1` + - actual: `2` + Also prove the corresponding `stack_depth_before` values are expected `0`, actual `1`. +8. Add an AST/source guard proving this ARCH-044 test contains no runtime expected-generation or fixture-write path. The static expectation must remain independently authored. +9. Prove the ARCH-044 fixture is absent from both the legacy regression manifest and the runtime action-session regression manifest. +10. Pin all nine previously accepted static fixture identities exactly and prove the existing runtime regression lane remains exactly `9/9` in its accepted case order. +11. Prove legacy regression remains `20/20`, trace evidence remains `2/2`, and production extra-turn LIFO compatibility is unchanged. +12. Update `hsr_axis_sim/LUMEN_RESULT.md` only after real CI results exist. Report actual commands and actual results only. + +## Acceptance criteria +- New fixture is independent static reviewed bytes, not generated from simulator/runtime export code. +- Static fixture has exactly 3 records with `ACTION_START -> EXTRA_TURN_QUEUED -> ACTION_END`. +- `EXTRA_TURN_QUEUED` contains exact typed `extra_turn_queue` and exact preserved `legacy_data`. +- ARCH-016 production action-session validation matches the static fixture. +- Controlled preloaded-stack mismatch reports the exact first structured divergence at `/event/payload/extra_turn_queue/stack_depth_after`, record 1, expected 1 vs actual 2. +- Existing production LIFO behavior remains unchanged. +- Existing nine reviewed fixture byte identities remain unchanged. +- New fixture is not promoted into either regression manifest. +- Full pytest passes. +- Legacy regression remains 20/20. +- Trace evidence remains 2/2. +- Runtime action-session Golden regression remains exactly 9/9. +- No locked/unauthorized files are modified. +- `LUMEN_RESULT.md` contains the final evidence and exclusions. + +## Required tests +At minimum, the focused test must cover: +- exact fixture byte count and SHA-256; +- no trailing newline; +- strict compact loader + required digest match; +- exact metadata/schema/sequence/event types/event IDs/action/actor/target; +- exact `extra_turn_queue` payload; +- exact raw `legacy_data` payload; +- exact adapter legacy event type/mechanic/mapping status; +- production ARCH-016 match; +- state postcondition: stack gains target and AV is unchanged; +- controlled preloaded-stack mismatch and exact first-divergence path/value; +- no expected-generation/write path in focused test source; +- fixture absent from both manifests; +- all nine prior fixture identities exact; +- runtime lane exactly 9/9 in existing order; +- legacy 20/20 and trace 2/2; +- LIFO compatibility unchanged. + +## Files/areas that must remain unchanged +Do not modify: +- `hsr_axis_sim/sim/**` +- `hsr_axis_sim/runtime_contracts/**` +- `hsr_axis_sim/runtime_adapters/**` +- `hsr_axis_sim/runtime_comparators/**` +- `hsr_axis_sim/runtime_divergence/**` +- `hsr_axis_sim/runtime_loaders/**` +- `hsr_axis_sim/runtime_exports/**` +- `hsr_axis_sim/runtime_action_sessions/**` +- `hsr_axis_sim/runtime_action_session_validation/**` +- `hsr_axis_sim/runtime_action_session_regression/**` +- `hsr_axis_sim/data/runtime_action_session_regression_manifest.json` +- `hsr_axis_sim/data/regression_manifest.json` +- every pre-ARCH-044 Golden fixture +- trace schemas and research/reference artifacts. + +Only these task files are expected before the final report update: +- `LUMEN_TASK_HSR_RUNTIME_ARCH_044.md` +- `hsr_axis_sim/data/runtime_golden_fixtures/arch_044_reviewed_grant_extra_turn_expected.json` +- `hsr_axis_sim/tests/test_runtime_arch_044_static_grant_extra_turn_golden_fixture.py` + +Then update only: +- `hsr_axis_sim/LUMEN_RESULT.md` + +## Explicit exclusions +- No production behavior changes. +- No Timeline changes. +- No extra-turn ordering changes. +- No new event types or adapter mappings. +- No schema/comparator/divergence changes. +- No regression-manifest version bump or fixture promotion. +- No claim about real HSR FIFO/LIFO, priority, interrupts, or hidden scheduling values. +- No damage/character database/video extraction/UI work. +- No unrelated refactors or documentation cleanup. + +## Commands to run +```bash +python -m compileall -q hsr_axis_sim +python -m pytest -q +python -m hsr_axis_sim.regression.runner --manifest hsr_axis_sim/data/regression_manifest.json --format text +python -m hsr_axis_sim.regression.runner --manifest hsr_axis_sim/data/regression_manifest.json --only trace_evidence --format text +python -m hsr_axis_sim.runtime_action_session_regression.runner --manifest hsr_axis_sim/data/runtime_action_session_regression_manifest.json --format text +``` + +## Final report format +Update `hsr_axis_sim/LUMEN_RESULT.md` with: +- task ID; +- implementation summary; +- files added/modified; +- tests added; +- exact commands executed; +- exact pass/fail results; +- exact fixture byte size + SHA-256; +- controlled mismatch first-divergence evidence; +- unresolved issues; +- confirmation that exclusions and locked areas were respected; +- suggested next smallest milestone. + +Do not report PASS from code inspection alone. Real GitHub Actions evidence is required before merge, then post-merge `main` CI must pass before the task is accepted. \ No newline at end of file diff --git a/hsr_axis_sim/LUMEN_RESULT.md b/hsr_axis_sim/LUMEN_RESULT.md index d010937..ad771ee 100644 --- a/hsr_axis_sim/LUMEN_RESULT.md +++ b/hsr_axis_sim/LUMEN_RESULT.md @@ -1,4 +1,4 @@ -# HSR-RUNTIME-ARCH-043 — GrantExtraTurn Runtime Observation Contract +# HSR-RUNTIME-ARCH-044 — Reviewed Static GrantExtraTurn Golden Fixture ## Status @@ -6,142 +6,158 @@ PASS — proceed ## Task ID -`HSR-RUNTIME-ARCH-043` +`HSR-RUNTIME-ARCH-044` ## Current confirmed base -- Accepted `main` before this task: `23be13799facfe63152d727459f345f03e87fac6`. -- Baseline post-merge validation before this task, GitHub Actions run #257: - - pytest: **1687 passed in 10.16s**; +- Accepted `main` before this task: `b3e9f7385f08dd13e53caa00a2b921dd1b500471`. +- That commit is the accepted merge of HSR-RUNTIME-ARCH-043. +- Baseline post-merge validation before this task, GitHub Actions run #264: + - pytest: **1713 passed in 10.07s**; - legacy regression: **20/20**; - trace evidence: **2/2**; - standalone runtime action-session Golden regression: **9/9**. ## Objective completed -Made the already-existing production `GrantExtraTurn` extra-turn-stack append observable through the runtime trace pipeline without changing target resolution, append order, LIFO resolution, Timeline selection, action-value behavior, or the nine accepted reviewed Golden fixtures. +Added one independently authored, manually reviewed, compact canonical runtime Golden expectation for a deterministic production `GrantExtraTurn` action and proved that the accepted ARCH-016 end-to-end action-session validation path matches it exactly at the runtime-record level. -This task observes the simulator's deterministic queue mutation only. It does **not** claim or infer real Honkai: Star Rail priority values, interrupt windows, ultimate priority, extra-action/follow-up behavior, or any undocumented scheduling rule. +This task adds evidence only. It does not change `GrantExtraTurn`, Timeline, the extra-turn stack algorithm, runtime contracts, adapters, comparator behavior, trace schema, or either regression manifest. -## Accepted simulator semantics preserved +The fixture observes only the simulator's already accepted deterministic queue mutation. It does **not** claim that real Honkai: Star Rail uses the simulator's LIFO implementation or expose any hidden game priority/interrupt rule. -For each target resolved by the existing `UnitEffect.target_units()` path, `GrantExtraTurn.apply()` now records the queue depth around the existing append while preserving the append itself: +## Reviewed static fixture -```python -stack_depth_before = len(state.extra_turn_stack) -state.extra_turn_stack.append(unit.id) -stack_depth_after = len(state.extra_turn_stack) -``` +Added: + +`hsr_axis_sim/data/runtime_golden_fixtures/arch_044_reviewed_grant_extra_turn_expected.json` + +Pinned identity: + +- fixture ID: `arch-044-reviewed-static-grant-extra-turn`; +- trace ID: `arch-044-reviewed-static-expected`; +- adapter stream ID: `arch-044-reviewed-axis`; +- exact size: **2658 bytes**; +- SHA-256: `57eefb521cb5cf1840e49c36e5c9c85a08281a7014c23ece0e3d5df1e6dfefdd`; +- compact canonical UTF-8 JSON; +- no trailing newline; +- metadata `construction` is exactly `manual-reviewed`. + +The fixture was written as independent static reviewed bytes. The ARCH-044 test source contains no runtime expected-generation, adapter-generation, stitch-generation, serialization-generation, or fixture-write path. -It then emits one post-mutation legacy event: +## Exact reviewed trace -`extra_turn_queued` +The static expected trace has exactly three contiguous records: -with exactly: +1. `ACTION_START`; +2. `EXTRA_TURN_QUEUED`; +3. `ACTION_END`. -- `actor_id`; -- `action_id`; -- `target_id`; -- `stack_depth_before`; -- `stack_depth_after`. +Identifiers: -The event is emitted after the append, so triggers observe the already-mutated stack. Target order is unchanged. `Timeline.next_turn()` remains unchanged and still resolves the accepted simulator stack by `pop()` from the end, so targets appended `[first, second]` resolve as `second`, then `first`. +- action ID: `reviewed-grant-extra-turn`; +- actor ID: `extra-turn-actor`; +- target ID: `extra-turn-target` on the queue record; +- event IDs: + - `legacy:arch-044-reviewed-axis:0`; + - `legacy:arch-044-reviewed-axis:1`; + - `legacy:arch-044-reviewed-axis:2`. -Extra-turn selection still does not advance global AV or mutate the normal timeline AV snapshot merely by selecting the queued turn. +The queue record's typed observation is exactly: -## Runtime contract added +```text +{ + "target_id": "extra-turn-target", + "stack_depth_before": 0, + "stack_depth_after": 1 +} +``` + +Its preserved legacy data is exactly the same observation plus: + +- `actor_id: extra-turn-actor`; +- `action_id: reviewed-grant-extra-turn`. -Added frozen `RuntimeExtraTurnQueuedObservation` in: +Adapter provenance remains the accepted ARCH-043 binding: -`hsr_axis_sim/runtime_contracts/turn_order_observations.py` +- legacy event type: `extra_turn_queued`; +- runtime event type: `EXTRA_TURN_QUEUED`; +- mechanic ID: `LEGACY_EVENT.EXTRA_TURN_QUEUED`; +- mapping status: `BOUND`. -Exact fields: +All three records retain empty record-level `numeric_values`. -- `target_id: str`; -- `stack_depth_before: int`; -- `stack_depth_after: int`. +## Production match proof -Validation requires: +The focused test constructs one normal production action: + +```python +Action( + "reviewed-grant-extra-turn", + "reviewed-grant-extra-turn", + "extra-turn-actor", + effects=[GrantExtraTurn(target_ids=["extra-turn-target"])], + ends_turn=False, +) +``` -- non-empty string target ID; -- exact integers for both depth fields, rejecting booleans and floats; -- nonnegative depths; -- `stack_depth_after == stack_depth_before + 1`. +That action is executed only through the accepted ARCH-016 action-session validation orchestration. The static fixture is supplied as expected bytes; production output is used only as actual trace data. -`to_payload()` returns exactly those three fields. The contract is exported from `hsr_axis_sim/runtime_contracts/__init__.py`. +The production validation matches the static fixture. Postconditions remain: -## Runtime adapter binding +- `extra_turn_stack == ["extra-turn-target"]`; +- target AV remains `80`; +- production pending-event order remains `action_started -> extra_turn_queued -> action_finished`; +- final capture cursor is `(3, 3)`; +- Golden comparison matches; +- first-divergence result reports a match with no divergence. -The existing runtime vocabulary value `RuntimeEventType.EXTRA_TURN_QUEUED` is reused; `runtime_contracts/enums.py` is unchanged. +## Controlled first-divergence proof -Added deterministic legacy mapping: +A controlled actual state preloads one valid queued unit: -`extra_turn_queued -> EXTRA_TURN_QUEUED` +`prequeued-extra-turn` -Normalized IDs: +before executing the exact same `GrantExtraTurn` action. -- `action_id <- action_id`; -- `actor_id <- actor_id`; -- `target_id <- target_id`. +The actual queue observation therefore becomes: -For valid events the adapter preserves: +- `stack_depth_before: 1`; +- `stack_depth_after: 2`. -- exact raw input in `payload["legacy_data"]`; -- validated structured queue observation in `payload["extra_turn_queue"]`; -- existing adapter provenance. +The accepted comparator traverses mapping keys deterministically in sorted order. The completed Golden mismatch therefore reports the exact first structured divergence: -Malformed structured queue events raise `LegacyEventSchemaError`; they do not degrade to `CONTENT_DEFINED` and are not silently repaired. +- record index: **1**; +- path: `/event/payload/extra_turn_queue/stack_depth_after`; +- expected value: **1**; +- actual value: **2**. -The current mapping registry is 14 entries with 13 bound mappings and the pre-existing unresolved `unit_defeated` lifecycle mapping. The historical ARCH-002 mapping document remains its exact nine-entry historical projection and does not backfill `extra_turn_queued`. +The test also proves the corresponding `stack_depth_before` values are expected **0** versus actual **1**, while target identity remains unchanged. This demonstrates that the ARCH-043 queue-depth observation is inspectable through the existing Golden validator without altering comparator semantics. ## Files added -- `LUMEN_TASK_HSR_RUNTIME_ARCH_043.md` -- `hsr_axis_sim/runtime_contracts/turn_order_observations.py` -- `hsr_axis_sim/tests/test_runtime_arch_043_extra_turn_observation.py` +- `LUMEN_TASK_HSR_RUNTIME_ARCH_044.md` +- `hsr_axis_sim/data/runtime_golden_fixtures/arch_044_reviewed_grant_extra_turn_expected.json` +- `hsr_axis_sim/tests/test_runtime_arch_044_static_grant_extra_turn_golden_fixture.py` -## Files modified +## File modified - `hsr_axis_sim/LUMEN_RESULT.md` -- `hsr_axis_sim/runtime_contracts/__init__.py` -- `hsr_axis_sim/runtime_adapters/legacy_events.py` -- `hsr_axis_sim/sim/effects.py` -- `hsr_axis_sim/tests/test_runtime_legacy_event_mapping.py` -- `hsr_axis_sim/tests/test_runtime_arch_031_advance_action_observation.py` -- `hsr_axis_sim/tests/test_runtime_arch_034_delay_action_observation.py` -- `hsr_axis_sim/tests/test_runtime_arch_037_change_speed_observation.py` -- `hsr_axis_sim/tests/test_runtime_arch_040_immediate_action_observation.py` - -## Focused tests added / updated - -ARCH-043 coverage proves: - -1. `RuntimeExtraTurnQueuedObservation` is frozen and serializes the exact three-field payload; -2. empty/non-string target IDs are rejected; -3. bool/float/string/negative queue-depth values are rejected; -4. depth transitions other than exact `before + 1` are rejected; -5. `extra_turn_queued` maps to `RuntimeEventType.EXTRA_TURN_QUEUED`; -6. normalized action/actor/target IDs are exact; -7. raw `legacy_data` is preserved exactly; -8. typed `extra_turn_queue` payload is exact; -9. malformed structured legacy events raise `LegacyEventSchemaError`; -10. one-target production event order is exactly `action_started -> extra_turn_queued -> action_finished`; -11. the event data contains exactly the five specified legacy fields; -12. an `extra_turn_queued` trigger sees the already-appended target and post-append depth; -13. self-target actor/target identity and depth are exact; -14. multi-target events preserve declared append order; -15. multi-target depth transitions are `0 -> 1` then `1 -> 2`; -16. subsequent `Timeline.next_turn()` calls resolve the accepted stack LIFO in reverse append order; -17. extra-turn selection leaves global AV and normal unit AV snapshots unchanged; -18. ARCH-012 capture produces exactly `ACTION_START -> EXTRA_TURN_QUEUED -> ACTION_END`; -19. `GrantExtraTurn` remains distinct from Advance/Delay/ChangeSpeed/ImmediateAction observation families; -20. all nine reviewed Golden fixture byte identities remain exact; -21. legacy regression remains `20/20`; -22. trace evidence remains `2/2`; -23. standalone runtime action-session Golden regression remains `9/9`; -24. pre-existing production LIFO compatibility remains explicit and passing. - -Historical ARCH-031/034/037/040 source guards were updated narrowly to permit the later-authorized `extra_turn_queued` observation while continuing to assert that `GrantExtraTurn` does not emit their action-axis event types. + +No production/runtime/manifest file is modified by ARCH-044. + +## Focused tests added + +`hsr_axis_sim/tests/test_runtime_arch_044_static_grant_extra_turn_golden_fixture.py` adds eight focused tests proving: + +1. exact static fixture byte count, digest, compact canonical loader acceptance, metadata, schema, sequences, event types, IDs, targets, payloads, adapter binding, and empty numeric values; +2. accepted ARCH-016 production `GrantExtraTurn` matches the independent static fixture and preserves target AV plus expected stack/event postconditions; +3. a valid preloaded extra-turn stack creates the exact accepted first divergence at `/event/payload/extra_turn_queue/stack_depth_after`, record 1, expected 1 versus actual 2; +4. the new fixture is absent from both legacy and runtime action-session regression manifests; +5. the focused test source contains no runtime expected-generation or fixture-write path; +6. all nine previously accepted static fixture byte identities remain exact and the runtime lane remains exactly 9/9 in accepted order; +7. legacy regression remains 20/20 and trace evidence remains 2/2; +8. pre-existing production extra-turn LIFO compatibility remains explicit and passing. ## Exact commands executed by CI @@ -157,91 +173,83 @@ python -m hsr_axis_sim.runtime_action_session_regression.runner --manifest hsr_a Authoritative green PR validation before this completion-report update: -GitHub Actions `HSR Axis Sim Validation`, PR #48, run **#262** (`33027644428`), job **`98372622604`**, branch head `4851c6e551313082dc38c2b3ae8a763357e8624d`: +GitHub Actions `HSR Axis Sim Validation`, PR #49, run **#265** (`33028261812`), job **`98374578250`**, branch head `6bd5d4a7368101c96036d4fd3ced246118bd9527`: - compile: **PASS**; -- pytest: **1713 passed in 9.85s**; +- pytest: **1721 passed in 8.22s**; - legacy locked regression: **20/20**; - trace evidence: **2/2**; - standalone runtime action-session Golden regression: **9/9**. -The nine runtime action-session reviewed cases and their accepted record counts remain: - -1. `arch-017-reviewed-static-action-session` — 4; -2. `arch-021-reviewed-static-clamped-energy` — 3; -3. `arch-023-reviewed-static-clamped-skill-point` — 3; -4. `arch-025-reviewed-static-energy-consume` — 3; -5. `arch-027-reviewed-static-skill-point-consume` — 3; -6. `arch-032-reviewed-static-action-advance` — 3; -7. `arch-035-reviewed-static-action-delay` — 3; -8. `arch-038-reviewed-static-change-speed` — 3; -9. `arch-041-reviewed-static-immediate-action` — 3. +The runtime regression cases remain exactly: -No tenth runtime regression case was added. +1. `arch-017-reviewed-static-action-session`; +2. `arch-021-reviewed-static-clamped-energy`; +3. `arch-023-reviewed-static-clamped-skill-point`; +4. `arch-025-reviewed-static-energy-consume`; +5. `arch-027-reviewed-static-skill-point-consume`; +6. `arch-032-reviewed-static-action-advance`; +7. `arch-035-reviewed-static-action-delay`; +8. `arch-038-reviewed-static-change-speed`; +9. `arch-041-reviewed-static-immediate-action`. -## Validation history / resolved failures +The new ARCH-044 fixture is deliberately not a tenth regression case in this milestone. -Initial PR validation run **#258** (`33027318210`), job **`98371603615`**, branch head `bd44f11ee3b34502aac525fd94748e867a83fba7`: +## Previous reviewed fixture identities preserved -- compile: **PASS**; -- pytest: **4 failed, 1709 passed in 10.76s**; -- downstream regression lanes were skipped because the pytest gate failed. +ARCH-044 pins and rechecks all nine prior fixtures: -All four failures were stale historical source guards that still required `GrantExtraTurn` to emit no event at all: - -1. ARCH-031 action-advance observation scope guard; -2. ARCH-034 action-delay observation scope guard; -3. ARCH-037 speed-change observation scope guard; -4. ARCH-040 ImmediateAction observation scope guard. - -No new ARCH-043 contract, adapter, production queue mutation, trigger, capture, self-target, multi-target, LIFO, or regression-identity test failed in that run. - -The four historical guards were then updated narrowly so they continue to reject cross-mechanic action-axis events but explicitly allow the newly authorized `extra_turn_queued` observation. Run #262 then passed the complete workflow. +1. ARCH-017 — 3013 bytes — `f672ffaac9ef9296e4982a6fb61f4d0257b5c0506412bcf54eb1768334118c66`; +2. ARCH-021 — 2759 bytes — `4fe2c8b3c9c22821a49ff380c9635828e36f3c4a3bbbc13d2cc077dd4d97e605`; +3. ARCH-023 — 2744 bytes — `fc359b367c2922ed39e059f89ad16845ba215508292cfa43ca2ab6031c4c9ba9`; +4. ARCH-025 — 2750 bytes — `7d61528687a5a2f499249e0f914f6f2f50975c7c153165eddd5e116f3ed19a75`; +5. ARCH-027 — 2796 bytes — `d0dcf128f3a28f691324f4e9295b7bcd66460598186f6059d4619f55e8ae39ec`; +6. ARCH-032 — 2818 bytes — `ab73c224d06690b379d398a5bc2c4b38a1ed654dfd86866d564417432c29d3ce`; +7. ARCH-035 — 2728 bytes — `9efbb65defb5eacc12150d31d0530d9a94b43a42e2303ebca643911f98094c4d`; +8. ARCH-038 — 2604 bytes — `c23b34e0afffdfe4bee53d028e5ff21d946623300b169ba57e5ddfb69478df2a`; +9. ARCH-041 — 2620 bytes — `7fd1594362b5bf9a95eec6f6472b2f17afa9dcfe10196d81ec6c970eab86eea1`. ## Locked areas confirmed unchanged -- `hsr_axis_sim/runtime_contracts/enums.py` unchanged; the pre-existing `EXTRA_TURN_QUEUED` value is reused. -- `hsr_axis_sim/sim/timeline.py` unchanged. -- `hsr_axis_sim/sim/action.py` unchanged. -- `hsr_axis_sim/sim/state.py` unchanged. -- `hsr_axis_sim/sim/unit.py` unchanged. -- runtime resource and action-axis observation semantics unchanged. -- runtime action-session regression manifest, grammar, and runner unchanged. -- `hsr_axis_sim/data/runtime_action_session_regression_manifest.json` unchanged at v1.8 / 9 cases. +- `hsr_axis_sim/sim/**` unchanged. +- `hsr_axis_sim/runtime_contracts/**` unchanged. +- `hsr_axis_sim/runtime_adapters/**` unchanged. +- `hsr_axis_sim/runtime_comparators/**` unchanged. +- `hsr_axis_sim/runtime_divergence/**` unchanged. +- `hsr_axis_sim/runtime_loaders/**` unchanged. +- `hsr_axis_sim/runtime_exports/**` unchanged. +- action-session capture/stitch/validation packages unchanged. +- runtime action-session regression package unchanged. +- `hsr_axis_sim/data/runtime_action_session_regression_manifest.json` unchanged at the accepted nine cases. - `hsr_axis_sim/data/regression_manifest.json` unchanged. -- every reviewed Golden fixture under `hsr_axis_sim/data/runtime_golden_fixtures/` unchanged. -- Golden validator/comparator/first-divergence implementation unchanged. +- all nine previous reviewed Golden fixtures unchanged. +- Timeline and accepted production LIFO behavior unchanged. - trace schema/version unchanged. -- ownership/SP/energy semantics unchanged. -- Advance/Delay/ChangeSpeed/ImmediateAction production semantics unchanged. - -The only production simulator modification is observation emission around the pre-existing append inside `GrantExtraTurn.apply()`. +- ownership/SP/energy and all action-axis observation semantics unchanged. ## Warnings / errors -- No compile, pytest, legacy-regression, trace-evidence, or runtime-action-session-regression failure remains in run #262. +- No compile, pytest, legacy-regression, trace-evidence, or runtime-action-session-regression failure occurred in run #265. - Existing nonblocking GitHub Actions warning remains: `actions/checkout@v4` and `actions/setup-python@v5` target Node 20 and are forced onto Node 24. - Existing upstream Node `punycode` and `url.parse()` deprecation notices remain unrelated to simulator correctness. ## Unresolved issues -None blocking HSR-RUNTIME-ARCH-043 acceptance. +None blocking HSR-RUNTIME-ARCH-044 implementation validation. -Real HSR extra-turn scheduling/priority semantics remain intentionally unresolved. This task only exposes the already accepted simulator LIFO stack mutation and must not be used as evidence that the game implements the same hidden scheduler. +Real HSR extra-turn scheduling/priority semantics remain intentionally unresolved. The new Golden records the accepted simulator queue observation only and is not evidence of the game's hidden scheduling algorithm. ## Exclusions confirmation -Respected: no real-HSR priority inference, no interrupt windows, no ultimate-priority rules, no extra-action/follow-up semantics, no FIFO alternative, no generalized scheduler/priority queue, no Timeline change, no queue deduplication/replacement, no automatic action selection, no static GrantExtraTurn Golden, no runtime regression promotion, no manifest v1.9, no generic effect DSL, no enum change, no trace schema bump, no comparator/divergence change, no video parsing/scraping, no character database expansion, no damage formula work, no AI optimization, and no unrelated UI/refactor work. +Respected: no production behavior change, no Timeline change, no extra-turn ordering change, no event vocabulary or adapter change, no schema/comparator/divergence change, no regression-manifest promotion/version bump, no dynamic expected generation, no real-HSR scheduling inference, no generic effect DSL, no video parsing/scraping, no character database expansion, no damage formula work, no AI optimization, and no unrelated UI/refactor/documentation cleanup. ## Suggested next milestone -Do not begin a new milestone until ARCH-043 is accepted on canonical `main` with post-merge CI. +Do not begin a new milestone until ARCH-044 is accepted on canonical `main` with post-merge CI. -After acceptance, the smallest safe continuation is expected to be **HSR-RUNTIME-ARCH-044 — Reviewed Static GrantExtraTurn Golden Fixture**: manually construct and review one deterministic static Golden for the accepted queue observation and existing LIFO simulator behavior, prove production match and controlled first divergence, and do **not** promote it into the runtime regression lane in the same task. +After acceptance, the smallest safe continuation is expected to be **HSR-RUNTIME-ARCH-045 — GrantExtraTurn Static Golden Regression Promotion**: promote the accepted ARCH-044 static fixture into the existing runtime action-session regression lane as a tenth deterministic case, update only the necessary manifest/runner grammar if the accepted grammar cannot already express `GrantExtraTurn`, and preserve all production semantics and static fixture bytes. -Recommended routing for ARCH-044 after ARCH-043 acceptance: +Recommended routing for ARCH-045 after ARCH-044 acceptance: - ChatGPT: **GPT-5.6 Terra**; - Codex reasoning: **High**. - -The production semantics are already locked; ARCH-044 is deterministic fixture construction and validation rather than a new scheduler-semantic change. diff --git a/hsr_axis_sim/data/runtime_golden_fixtures/arch_044_reviewed_grant_extra_turn_expected.json b/hsr_axis_sim/data/runtime_golden_fixtures/arch_044_reviewed_grant_extra_turn_expected.json new file mode 100644 index 0000000..11e49dc --- /dev/null +++ b/hsr_axis_sim/data/runtime_golden_fixtures/arch_044_reviewed_grant_extra_turn_expected.json @@ -0,0 +1 @@ +{"event_type_counts":{"ACTION_END":1,"ACTION_START":1,"EXTRA_TURN_QUEUED":1},"first_sequence":0,"last_sequence":2,"metadata":{"construction":"manual-reviewed","fixture_id":"arch-044-reviewed-static-grant-extra-turn","purpose":"grant-extra-turn-end-to-end-golden"},"record_count":3,"records":[{"action_context":null,"attack_context":null,"event":{"action_id":"reviewed-grant-extra-turn","actor_id":"extra-turn-actor","attack_id":null,"event_id":"legacy:arch-044-reviewed-axis:0","event_type":"ACTION_START","hit_id":null,"payload":{"adapter":{"adapter_name":"legacy_mvp_event_adapter","adapter_version":"1.0","binding_status":"BOUND","legacy_event_type":"action_started","mapping_status":"BOUND","mechanic_id":"LEGACY_EVENT.ACTION_STARTED","semantic_gap_ids":[]},"legacy_data":{"action_id":"reviewed-grant-extra-turn","actor_id":"extra-turn-actor"}},"sequence":0,"source_id":null,"target_id":null},"hit_context":null,"notes":[],"numeric_values":{},"sequence":0},{"action_context":null,"attack_context":null,"event":{"action_id":"reviewed-grant-extra-turn","actor_id":"extra-turn-actor","attack_id":null,"event_id":"legacy:arch-044-reviewed-axis:1","event_type":"EXTRA_TURN_QUEUED","hit_id":null,"payload":{"adapter":{"adapter_name":"legacy_mvp_event_adapter","adapter_version":"1.0","binding_status":"BOUND","legacy_event_type":"extra_turn_queued","mapping_status":"BOUND","mechanic_id":"LEGACY_EVENT.EXTRA_TURN_QUEUED","semantic_gap_ids":[]},"extra_turn_queue":{"stack_depth_after":1,"stack_depth_before":0,"target_id":"extra-turn-target"},"legacy_data":{"action_id":"reviewed-grant-extra-turn","actor_id":"extra-turn-actor","stack_depth_after":1,"stack_depth_before":0,"target_id":"extra-turn-target"}},"sequence":1,"source_id":null,"target_id":"extra-turn-target"},"hit_context":null,"notes":[],"numeric_values":{},"sequence":1},{"action_context":null,"attack_context":null,"event":{"action_id":"reviewed-grant-extra-turn","actor_id":"extra-turn-actor","attack_id":null,"event_id":"legacy:arch-044-reviewed-axis:2","event_type":"ACTION_END","hit_id":null,"payload":{"adapter":{"adapter_name":"legacy_mvp_event_adapter","adapter_version":"1.0","binding_status":"BOUND","legacy_event_type":"action_finished","mapping_status":"BOUND","mechanic_id":"LEGACY_EVENT.ACTION_FINISHED","semantic_gap_ids":[]},"legacy_data":{"action_id":"reviewed-grant-extra-turn","actor_id":"extra-turn-actor"}},"sequence":2,"source_id":null,"target_id":null},"hit_context":null,"notes":[],"numeric_values":{},"sequence":2}],"schema_name":"hsr_runtime_trace","schema_version":"1.0","semantic_gap_ids":[],"sequence_policy":"CONTIGUOUS","trace_id":"arch-044-reviewed-static-expected"} \ No newline at end of file diff --git a/hsr_axis_sim/tests/test_runtime_arch_044_static_grant_extra_turn_golden_fixture.py b/hsr_axis_sim/tests/test_runtime_arch_044_static_grant_extra_turn_golden_fixture.py new file mode 100644 index 0000000..6e35f48 --- /dev/null +++ b/hsr_axis_sim/tests/test_runtime_arch_044_static_grant_extra_turn_golden_fixture.py @@ -0,0 +1,420 @@ +import ast +import hashlib +from pathlib import Path + +from hsr_axis_sim.regression.manifest import load_regression_manifest +from hsr_axis_sim.regression.runner import run_regression +from hsr_axis_sim.runtime_action_session_regression.manifest import ( + load_runtime_action_session_regression_manifest, +) +from hsr_axis_sim.runtime_action_session_regression.runner import ( + run_runtime_action_session_regression, +) +from hsr_axis_sim.runtime_action_session_validation import run_action_session_validation +from hsr_axis_sim.runtime_action_sessions import ( + ExplicitActionCaptureStep, + MultiActionCaptureSessionConfig, +) +from hsr_axis_sim.runtime_adapters import ( + AmbiguousLegacyEventPolicy, + LegacyEventAdapterConfig, + UnknownLegacyEventPolicy, +) +from hsr_axis_sim.runtime_capture_cursors import PendingEventCaptureCursor +from hsr_axis_sim.runtime_contracts import RuntimeEventType +from hsr_axis_sim.runtime_exports import ( + EmptyTracePolicy, + TraceExportConfig, + TraceSequencePolicy, +) +from hsr_axis_sim.runtime_golden_replays import GoldenReplayValidationConfig +from hsr_axis_sim.runtime_loaders import ( + RuntimeTraceLoadConfig, + TraceCanonicalForm, + TraceCanonicalFormPolicy, + TraceDigestPolicy, + TraceDigestStatus, + load_runtime_trace_bytes, +) +from hsr_axis_sim.runtime_trace_stitching import CapturedTraceStitchConfig +from hsr_axis_sim.sim.action import Action +from hsr_axis_sim.sim.effects import GrantExtraTurn +from hsr_axis_sim.sim.state import BattleState +from hsr_axis_sim.sim.timeline import Timeline +from hsr_axis_sim.sim.unit import Unit + + +PACKAGE_ROOT = Path(__file__).parents[1] +FIXTURE_DIR = PACKAGE_ROOT / "data" / "runtime_golden_fixtures" +EXPECTED_PATH = FIXTURE_DIR / "arch_044_reviewed_grant_extra_turn_expected.json" +LEGACY_REGRESSION_MANIFEST = PACKAGE_ROOT / "data" / "regression_manifest.json" +RUNTIME_REGRESSION_MANIFEST = ( + PACKAGE_ROOT / "data" / "runtime_action_session_regression_manifest.json" +) + +EXPECTED_SHA256 = "57eefb521cb5cf1840e49c36e5c9c85a08281a7014c23ece0e3d5df1e6dfefdd" +EXPECTED_SIZE_BYTES = 2658 +FIXTURE_ID = "arch-044-reviewed-static-grant-extra-turn" +STREAM_ID = "arch-044-reviewed-axis" +ACTION_ID = "reviewed-grant-extra-turn" +ACTOR_ID = "extra-turn-actor" +TARGET_ID = "extra-turn-target" +PREQUEUED_ID = "prequeued-extra-turn" + +PRIOR_FIXTURES = ( + ( + FIXTURE_DIR / "arch_017_reviewed_action_session_expected.json", + 3013, + "f672ffaac9ef9296e4982a6fb61f4d0257b5c0506412bcf54eb1768334118c66", + ), + ( + FIXTURE_DIR / "arch_021_reviewed_clamped_energy_expected.json", + 2759, + "4fe2c8b3c9c22821a49ff380c9635828e36f3c4a3bbbc13d2cc077dd4d97e605", + ), + ( + FIXTURE_DIR / "arch_023_reviewed_clamped_skill_point_expected.json", + 2744, + "fc359b367c2922ed39e059f89ad16845ba215508292cfa43ca2ab6031c4c9ba9", + ), + ( + FIXTURE_DIR / "arch_025_reviewed_energy_consume_expected.json", + 2750, + "7d61528687a5a2f499249e0f914f6f2f50975c7c153165eddd5e116f3ed19a75", + ), + ( + FIXTURE_DIR / "arch_027_reviewed_skill_point_consume_expected.json", + 2796, + "d0dcf128f3a28f691324f4e9295b7bcd66460598186f6059d4619f55e8ae39ec", + ), + ( + FIXTURE_DIR / "arch_032_reviewed_action_advance_expected.json", + 2818, + "ab73c224d06690b379d398a5bc2c4b38a1ed654dfd86866d564417432c29d3ce", + ), + ( + FIXTURE_DIR / "arch_035_reviewed_action_delay_expected.json", + 2728, + "9efbb65defb5eacc12150d31d0530d9a94b43a42e2303ebca643911f98094c4d", + ), + ( + FIXTURE_DIR / "arch_038_reviewed_change_speed_expected.json", + 2604, + "c23b34e0afffdfe4bee53d028e5ff21d946623300b169ba57e5ddfb69478df2a", + ), + ( + FIXTURE_DIR / "arch_041_reviewed_immediate_action_expected.json", + 2620, + "7fd1594362b5bf9a95eec6f6472b2f17afa9dcfe10196d81ec6c970eab86eea1", + ), +) + + +def _expected_bytes() -> bytes: + return EXPECTED_PATH.read_bytes() + + +def _state(*, preload_extra_turn: bool = False) -> BattleState: + units = [ + Unit(ACTOR_ID, "Extra Turn Actor", "ally", 100, current_av=20), + Unit(TARGET_ID, "Extra Turn Target", "ally", 100, current_av=80), + ] + stack = [] + if preload_extra_turn: + units.append(Unit(PREQUEUED_ID, "Prequeued Extra Turn", "ally", 100, current_av=70)) + stack.append(PREQUEUED_ID) + return BattleState(units, extra_turn_stack=stack) + + +def _runtime_inputs(): + steps = ( + ExplicitActionCaptureStep( + Action( + ACTION_ID, + ACTION_ID, + ACTOR_ID, + effects=[GrantExtraTurn(target_ids=[TARGET_ID])], + ends_turn=False, + ) + ), + ) + adapter = LegacyEventAdapterConfig( + STREAM_ID, + UnknownLegacyEventPolicy.REJECT, + AmbiguousLegacyEventPolicy.REJECT, + ) + session_config = MultiActionCaptureSessionConfig( + PendingEventCaptureCursor(0, 0), + adapter, + ( + TraceExportConfig( + "arch-044-axis-segment-0", + TraceSequencePolicy.CONTIGUOUS, + EmptyTracePolicy.REJECT, + {"fixture_id": FIXTURE_ID, "segment": 0}, + ), + ), + False, + ) + stitch_config = CapturedTraceStitchConfig( + TraceExportConfig( + "arch-044-axis-runtime-actual", + TraceSequencePolicy.CONTIGUOUS, + EmptyTracePolicy.REJECT, + {"fixture_id": FIXTURE_ID, "source": "production-action-session"}, + ), + False, + ) + golden_config = GoldenReplayValidationConfig( + FIXTURE_ID, + EXPECTED_SHA256, + TraceCanonicalFormPolicy.COMPACT_ONLY, + 100_000, + ) + return steps, session_config, stitch_config, golden_config + + +def test_static_grant_extra_turn_fixture_has_exact_reviewed_bytes_digest_and_schema(): + expected = _expected_bytes() + + assert len(expected) == EXPECTED_SIZE_BYTES + assert hashlib.sha256(expected).hexdigest() == EXPECTED_SHA256 + assert not expected.endswith(b"\n") + + loaded = load_runtime_trace_bytes( + expected, + config=RuntimeTraceLoadConfig( + TraceCanonicalFormPolicy.COMPACT_ONLY, + TraceDigestPolicy.REQUIRE_MATCH, + EXPECTED_SHA256, + 100_000, + ), + ) + + assert loaded.canonical_form is TraceCanonicalForm.COMPACT + assert loaded.digest_status is TraceDigestStatus.MATCHED + document = loaded.artifact.document + assert document.trace_id == "arch-044-reviewed-static-expected" + assert document.schema_name == "hsr_runtime_trace" + assert document.schema_version == "1.0" + assert document.sequence_policy is TraceSequencePolicy.CONTIGUOUS + assert document.record_count == 3 + assert dict(document.metadata) == { + "construction": "manual-reviewed", + "fixture_id": FIXTURE_ID, + "purpose": "grant-extra-turn-end-to-end-golden", + } + assert [record.sequence for record in document.records] == [0, 1, 2] + assert [record.event.event_type for record in document.records] == [ + RuntimeEventType.ACTION_START, + RuntimeEventType.EXTRA_TURN_QUEUED, + RuntimeEventType.ACTION_END, + ] + assert [record.event.event_id for record in document.records] == [ + "legacy:arch-044-reviewed-axis:0", + "legacy:arch-044-reviewed-axis:1", + "legacy:arch-044-reviewed-axis:2", + ] + assert [record.event.action_id for record in document.records] == [ + ACTION_ID, + ACTION_ID, + ACTION_ID, + ] + assert [record.event.actor_id for record in document.records] == [ + ACTOR_ID, + ACTOR_ID, + ACTOR_ID, + ] + assert [record.event.target_id for record in document.records] == [ + None, + TARGET_ID, + None, + ] + assert all(dict(record.numeric_values) == {} for record in document.records) + + queued = document.records[1].event + exact_observation = { + "target_id": TARGET_ID, + "stack_depth_before": 0, + "stack_depth_after": 1, + } + assert dict(queued.payload["extra_turn_queue"]) == exact_observation + assert dict(queued.payload["legacy_data"]) == { + "actor_id": ACTOR_ID, + "action_id": ACTION_ID, + **exact_observation, + } + assert queued.payload["adapter"]["legacy_event_type"] == "extra_turn_queued" + assert queued.payload["adapter"]["mechanic_id"] == "LEGACY_EVENT.EXTRA_TURN_QUEUED" + assert queued.payload["adapter"]["mapping_status"] == "BOUND" + + +def test_arch_016_production_grant_extra_turn_matches_static_fixture(): + expected = _expected_bytes() + steps, session_config, stitch_config, golden_config = _runtime_inputs() + state = _state() + + result = run_action_session_validation( + state, + steps, + session_config=session_config, + stitch_config=stitch_config, + expected_payload_bytes=expected, + golden_config=golden_config, + ) + + assert result.matches is True + target = state.get_unit(TARGET_ID) + assert target.current_av == 80 + assert state.extra_turn_stack == [TARGET_ID] + assert [event.type for event in state.pending_events] == [ + "action_started", + "extra_turn_queued", + "action_finished", + ] + assert result.session_result.final_cursor == PendingEventCaptureCursor(3, 3) + golden = result.validation_result.validation_result.validation_result + assert golden.expected_sha256 == EXPECTED_SHA256 + assert golden.comparison.matches is True + assert golden.first_divergence.matches is True + assert golden.first_divergence.divergence is None + + +def test_preloaded_stack_reports_exact_first_extra_turn_queue_depth_divergence(): + expected = _expected_bytes() + steps, session_config, stitch_config, golden_config = _runtime_inputs() + state = _state(preload_extra_turn=True) + + result = run_action_session_validation( + state, + steps, + session_config=session_config, + stitch_config=stitch_config, + expected_payload_bytes=expected, + golden_config=golden_config, + ) + + assert result.matches is False + assert state.get_unit(TARGET_ID).current_av == 80 + assert state.extra_turn_stack == [PREQUEUED_ID, TARGET_ID] + golden = result.validation_result.validation_result.validation_result + divergence = golden.first_divergence.divergence + assert divergence is not None + assert divergence.record_index == 1 + assert divergence.first_field_difference is not None + assert ( + divergence.first_field_difference.path + == "/event/payload/extra_turn_queue/stack_depth_after" + ) + assert divergence.first_field_difference.expected_value == 1 + assert divergence.first_field_difference.actual_value == 2 + + expected_queue = golden.comparison.records[1].expected_record.event.payload[ + "extra_turn_queue" + ] + actual_queue = golden.comparison.records[1].actual_record.event.payload[ + "extra_turn_queue" + ] + assert expected_queue["target_id"] == actual_queue["target_id"] == TARGET_ID + assert expected_queue["stack_depth_before"] == 0 + assert actual_queue["stack_depth_before"] == 1 + assert expected_queue["stack_depth_after"] == 1 + assert actual_queue["stack_depth_after"] == 2 + + expected_legacy = golden.comparison.records[1].expected_record.event.payload[ + "legacy_data" + ] + actual_legacy = golden.comparison.records[1].actual_record.event.payload[ + "legacy_data" + ] + assert expected_legacy["stack_depth_before"] == 0 + assert actual_legacy["stack_depth_before"] == 1 + assert expected_legacy["stack_depth_after"] == 1 + assert actual_legacy["stack_depth_after"] == 2 + + +def test_arch_044_fixture_is_not_promoted_into_any_regression_manifest(): + legacy = LEGACY_REGRESSION_MANIFEST.read_text(encoding="utf-8") + runtime = RUNTIME_REGRESSION_MANIFEST.read_text(encoding="utf-8") + + assert FIXTURE_ID not in legacy + assert EXPECTED_PATH.name not in legacy + assert FIXTURE_ID not in runtime + assert EXPECTED_PATH.name not in runtime + + +def test_arch_044_test_source_has_no_runtime_expected_generation_path(): + tree = ast.parse(Path(__file__).read_text()) + called_names = set() + for node in ast.walk(tree): + if isinstance(node, ast.Call): + if isinstance(node.func, ast.Name): + called_names.add(node.func.id) + elif isinstance(node.func, ast.Attribute): + called_names.add(node.func.attr) + + forbidden_calls = { + "build_runtime_trace_document", + "build_runtime_trace_artifact", + "canonical_json_bytes", + "canonical_json_dumps", + "adapt_legacy_event", + "adapt_legacy_event_stream", + "run_multi_action_capture_session", + "stitch_successful_action_session", + "validate_successful_session_against_golden", + "write_bytes", + "write_text", + "dumps", + "dump", + } + assert called_names.isdisjoint(forbidden_calls) + + +def test_prior_static_fixture_identities_and_runtime_lane_remain_accepted(): + for path, size, digest in PRIOR_FIXTURES: + payload = path.read_bytes() + assert len(payload) == size + assert hashlib.sha256(payload).hexdigest() == digest + + report = run_runtime_action_session_regression( + load_runtime_action_session_regression_manifest(RUNTIME_REGRESSION_MANIFEST) + ) + assert report.passed is True + assert report.total == 9 + assert report.passed_count == 9 + assert [result.case_id for result in report.results] == [ + "arch-017-reviewed-static-action-session", + "arch-021-reviewed-static-clamped-energy", + "arch-023-reviewed-static-clamped-skill-point", + "arch-025-reviewed-static-energy-consume", + "arch-027-reviewed-static-skill-point-consume", + "arch-032-reviewed-static-action-advance", + "arch-035-reviewed-static-action-delay", + "arch-038-reviewed-static-change-speed", + "arch-041-reviewed-static-immediate-action", + ] + + +def test_legacy_regression_and_trace_evidence_remain_unchanged(): + legacy = load_regression_manifest(LEGACY_REGRESSION_MANIFEST) + complete = run_regression(manifest=legacy) + trace = run_regression(manifest=legacy, only="trace_evidence") + + assert complete.passed is True + assert complete.total == 20 + assert complete.passed_count == 20 + assert trace.passed is True + assert trace.total == 2 + assert trace.passed_count == 2 + + +def test_production_lifo_compatibility_remains_unchanged(): + units = [Unit(name, name, "ally", 100) for name in ("first", "second", "third")] + state = BattleState(units=units, extra_turn_stack=["first", "second", "third"]) + + assert [Timeline.next_turn(state).actor_id for _ in range(3)] == [ + "third", + "second", + "first", + ]