File size: 4,772 Bytes
463f868
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
# Structured Runtime Migration Plan

Goal: simplify the Python to Rust ability pipeline without breaking current behavior.

This plan is intentionally staged. We keep the current bytecode path working until each
new boundary has parity tests and a rollback point.

## Guiding rule

Do not remove an old path until the new path:

1. Produces the same behavior on a targeted test set.
2. Has a clear Rust-side consumer.
3. Has a rollback path that is one commit away.

## Phase 1: Introduce structured data as the source of truth

Scope:

- `engine/models/ability.py`
- `engine/models/structured_instruction_ir.py`
- `compiler/parser_v2.py` (legacy entry point; parser logic is now split across `parser_*` modules)
- `compiler/main.py`

Work:

1. Keep `Ability.compile()` behavior unchanged for now.
2. Make sure every `Effect`, `Condition`, and `Cost` carries the runtime fields we need later:
   - `runtime_opcode`
   - `runtime_value`
   - `runtime_attr`
   - `runtime_slot`
   - `is_optional`
   - `target`
   - `params`
3. Treat the structured IR export as the new contract for inspection and debugging.
4. Add tests that snapshot the structured representation for a few representative abilities.

Pass criteria:

- Existing card compilation still works.
- Structured output is stable and human-readable.
- No runtime behavior changes yet.

Rollback:

- Remove the new structured export usage and keep the old compile path only.

## Phase 2: Move one runtime behavior at a time

Scope:

- `engine_rust_src/src/core/logic/interpreter/handlers/flow.rs`
- `engine_rust_src/src/core/logic/interpreter/mod.rs`
- `engine_rust_src/src/core/logic/models.rs`

Work:

1. Start with `ALL_PLAYERS` handling.
2. Let Rust interpret the target semantics directly for one opcode family.
3. Keep Python emitting the legacy expansion until Rust parity is proven.
4. Add regression tests for:
   - self/opponent alternation
   - multiple consecutive `ALL_PLAYERS` effects
   - target reset behavior after mixed target sequences

Pass criteria:

- Rust can execute the structured target behavior without relying on Python expansion.
- Legacy compiled bytecode still passes old tests.

Rollback:

- Re-enable the Python expansion path only.

## Phase 3: Move optional flow control into Rust

Scope:

- `engine/models/ability.py`
- `engine_rust_src/src/core/logic/interpreter/mod.rs`
- `engine_rust_src/src/core/logic/interpreter/handlers/flow.rs`

Work:

1. Keep Python jump generation in place until Rust handles optional flow correctly.
2. Add a Rust-side optional execution path that can:
   - ask for player choice
   - skip an instruction cleanly when declined
   - continue execution at the next instruction
3. Test:
   - optional cost accepted
   - optional cost declined
   - nested optional blocks

Pass criteria:

- Optional behavior matches current output.
- No `JUMP_IF_FALSE` dependency is needed for the new path.

Rollback:

- Restore the Python jump emission and keep Rust as a passive executor.

## Phase 4: Remove the semantic shim

Scope:

- `engine/models/ability_ir.py`
- `engine/models/ability.py`
- `compiler/parser_v2.py` (legacy parser surface at the time of writing)

Work:

1. Replace intermediate semantic representations with direct `Effect` / `Condition` / `Cost` construction.
2. Keep any compatibility wrappers needed by tests or exporters.
3. Remove only the parts that no longer have consumers.

Pass criteria:

- Parser output feeds the final dataclasses directly.
- No hidden mapping layer remains in the hot path.

Rollback:

- Reintroduce the shim as a thin adapter if needed.

## Phase 5: Replace packed runtime_attr with named params



Scope:



- `engine/models/generated_packer.py`
- `engine/models/ability.py`
- `engine_rust_src/src/core/logic/models.rs`
- `engine_rust_src/src/core/logic/interpreter/handlers/*`

Work:

1. Add named param access on the Rust side.
2. Keep packed fields available until every consumer has moved.
3. Remove bit-packing only after the last Rust caller stops using it.

Pass criteria:

- Rust reads named params directly.
- Bit-packing is no longer required for runtime correctness.

Rollback:

- Restore packed-field reads from the existing compatibility fields.

## Recommended first commit

If we want the safest possible start, the first commit should only do this:

1. Add or tighten structured IR export coverage.
2. Add golden tests for:
   - optional costs
   - `ALL_PLAYERS`
   - constant-trigger condition handling
3. Leave `Ability.compile()` behavior unchanged.

That gives us a foundation we can trust before we touch control flow or target expansion.