nmarshall commited on
Commit
33c79fb
·
1 Parent(s): b9f1c35

gameplay: port mode8 eligibility policy

Browse files
crates/urt-gameplay/src/bin/urt-move-callbacks.rs CHANGED
@@ -70,6 +70,7 @@ fn main() {
70
  }
71
  "self" => println!("{}", self_recovery(&w)),
72
  "cleanup" => println!("{}", cleanup(&w)),
 
73
  _ => panic!("unknown policy"),
74
  }
75
  }
@@ -158,3 +159,53 @@ fn cleanup(w: &[u32]) -> String {
158
  .join(",");
159
  format!(r#"{{"clients":[{}],"proceed":{}}}"#, rows, proceed)
160
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
70
  }
71
  "self" => println!("{}", self_recovery(&w)),
72
  "cleanup" => println!("{}", cleanup(&w)),
73
+ "eligibility" => println!("{}", eligibility(&w)),
74
  _ => panic!("unknown policy"),
75
  }
76
  }
 
159
  .join(",");
160
  format!(r#"{{"clients":[{}],"proceed":{}}}"#, rows, proceed)
161
  }
162
+
163
+ fn eligibility(w: &[u32]) -> String {
164
+ assert_eq!(w.len(), 22);
165
+ let selected = if w[20] == 1 { 2 } else { 1 };
166
+ let own = if w[17] == 0 { selected } else { w[17] };
167
+ let other = if w[16] == 0 { selected } else { w[16] };
168
+ let mut c = vec![
169
+ ObjectiveClient {
170
+ connection: 2,
171
+ health: 1,
172
+ team: selected,
173
+ ..ObjectiveClient::default()
174
+ };
175
+ (w[18] as usize).max(3)
176
+ ];
177
+ c[w[19] as usize] = ObjectiveClient {
178
+ connection: w[13],
179
+ health: w[14],
180
+ follow: w[15],
181
+ team: other,
182
+ busy: w[12],
183
+ };
184
+ c[2] = ObjectiveClient {
185
+ connection: w[8],
186
+ health: w[9],
187
+ follow: w[10],
188
+ team: own,
189
+ busy: w[11],
190
+ };
191
+ let old = c.clone();
192
+ let eligible = mode8_eligible(
193
+ &c,
194
+ Mode8EligibilityInput {
195
+ entity: w[0] != 0,
196
+ client: w[1] != 0,
197
+ enabled_a: w[2],
198
+ enabled_b: w[3],
199
+ tick: w[4],
200
+ start: w[5],
201
+ delay: w[6],
202
+ complete: w[7],
203
+ selector: w[20],
204
+ self_index: 2,
205
+ count: w[18] as usize,
206
+ },
207
+ )
208
+ .unwrap();
209
+ assert_eq!(w[21], 0);
210
+ format!(r#"{{"eligible":{},"unchanged":{}}}"#, eligible, c == old)
211
+ }
crates/urt-gameplay/src/move_callbacks.rs CHANGED
@@ -401,3 +401,135 @@ mod cleanup_tests {
401
  assert_eq!(c, old);
402
  }
403
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
401
  assert_eq!(c, old);
402
  }
403
  }
404
+
405
+ #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
406
+ pub struct ObjectiveClient {
407
+ pub connection: u32,
408
+ pub health: u32,
409
+ pub follow: u32,
410
+ pub team: u32,
411
+ pub busy: u32,
412
+ }
413
+ #[derive(Clone, Copy, Debug, PartialEq, Eq)]
414
+ pub struct Mode8EligibilityInput {
415
+ pub entity: bool,
416
+ pub client: bool,
417
+ pub enabled_a: u32,
418
+ pub enabled_b: u32,
419
+ pub tick: u32,
420
+ pub start: u32,
421
+ pub delay: u32,
422
+ pub complete: u32,
423
+ pub selector: u32,
424
+ pub self_index: usize,
425
+ pub count: usize,
426
+ }
427
+ /// Original153419 admission through153596. No objective lookup or mutation.
428
+ pub fn mode8_eligible(
429
+ clients: &[ObjectiveClient],
430
+ input: Mode8EligibilityInput,
431
+ ) -> Result<bool, Error> {
432
+ if !input.entity
433
+ || !input.client
434
+ || input.enabled_a == 0
435
+ || input.enabled_b == 0
436
+ || input.tick.wrapping_sub(input.start) as i32 <= input.delay.wrapping_mul(1000) as i32
437
+ || input.complete != 0
438
+ {
439
+ return Ok(false);
440
+ }
441
+ let own = *clients.get(input.self_index).ok_or(Error::InvalidIndex)?;
442
+ if own.connection != 2
443
+ || own.health as i32 <= 0
444
+ || own.follow != 0
445
+ || own.team != if input.selector == 1 { 2 } else { 1 }
446
+ {
447
+ return Ok(false);
448
+ }
449
+ if input.count > 64 || input.count > clients.len() {
450
+ return Err(Error::InvalidCount);
451
+ }
452
+ for (j, c) in clients[..input.count].iter().enumerate() {
453
+ if j != input.self_index
454
+ && c.connection == 2
455
+ && c.health as i32 > 0
456
+ && c.follow == 0
457
+ && c.team == own.team
458
+ && c.busy != 0
459
+ {
460
+ return Ok(false);
461
+ }
462
+ }
463
+ Ok(true)
464
+ }
465
+ #[cfg(test)]
466
+ mod eligibility_tests {
467
+ use super::*;
468
+ fn input() -> Mode8EligibilityInput {
469
+ Mode8EligibilityInput {
470
+ entity: true,
471
+ client: true,
472
+ enabled_a: 1,
473
+ enabled_b: 1,
474
+ tick: 10000,
475
+ start: 9000,
476
+ delay: 1,
477
+ complete: 0,
478
+ selector: 2,
479
+ self_index: 2,
480
+ count: 3,
481
+ }
482
+ }
483
+ fn clients() -> [ObjectiveClient; 3] {
484
+ [ObjectiveClient {
485
+ connection: 2,
486
+ health: 1,
487
+ follow: 0,
488
+ team: 1,
489
+ busy: 0,
490
+ }; 3]
491
+ }
492
+ #[test]
493
+ fn strict_time_and_exact_selector_are_preserved() {
494
+ let c = clients();
495
+ assert_eq!(mode8_eligible(&c, input()), Ok(false));
496
+ assert_eq!(
497
+ mode8_eligible(
498
+ &c,
499
+ Mode8EligibilityInput {
500
+ tick: 10001,
501
+ ..input()
502
+ }
503
+ ),
504
+ Ok(true)
505
+ );
506
+ assert_eq!(
507
+ mode8_eligible(
508
+ &c,
509
+ Mode8EligibilityInput {
510
+ tick: 10001,
511
+ selector: 1,
512
+ ..input()
513
+ }
514
+ ),
515
+ Ok(false)
516
+ );
517
+ }
518
+ #[test]
519
+ fn self_busy_is_skipped_and_only_live_teammates_block() {
520
+ let mut c = clients();
521
+ c[2].busy = 1;
522
+ let i = Mode8EligibilityInput {
523
+ tick: 10001,
524
+ ..input()
525
+ };
526
+ assert_eq!(mode8_eligible(&c, i), Ok(true));
527
+ c[0].busy = 1;
528
+ assert_eq!(mode8_eligible(&c, i), Ok(false));
529
+ c[0].follow = 1;
530
+ assert_eq!(mode8_eligible(&c, i), Ok(true));
531
+ let old = c;
532
+ assert!(mode8_eligible(&c, Mode8EligibilityInput { count: 65, ..i }).is_err());
533
+ assert_eq!(c, old);
534
+ }
535
+ }
docs/FINDINGS.md CHANGED
@@ -1935,3 +1935,5 @@ RE-998: Twenty-eight original-QVM fixtures recover152792 admission and its first
1935
  RE-999: Rust ports152792 admission and first scan across28 original cases, including full64-slot and zero-count pools. Typed indices replace supplied entity/client pointers; inactive targets are not dereferenced. Protected self returns before scanning, preserving other helpers. Dead/disconnected/following targets clear only assistance mask8192, active state and target. Invalid active indices/counts reject atomically; unit tests cover high-bit preservation, early bypass and rollback after a prior planned cleanup. [Evidence](research/RUST_MOVE_ASSIST_CLEANUP.md).
1936
 
1937
  RE-1000: Sixty-six original-QVM cases recover153419 admission through153596. Both global enables, strict signed wrapped elapsed greater than delay*1000, incomplete state, connected/live/non-following self and selected team are required. Selector exactly1 chooses team2; other values choose team1. A live connected non-following teammate with nonzero6164 blocks entry, while self is excluded. All fields remain unchanged. Exact/negative/wrapped times, three selectors, zero/64 pools and teammate bypasses are covered; flag search and objective effects remain open. [Evidence](research/QVM_MOVE_MODE8_ELIGIBILITY.md).
 
 
 
1935
  RE-999: Rust ports152792 admission and first scan across28 original cases, including full64-slot and zero-count pools. Typed indices replace supplied entity/client pointers; inactive targets are not dereferenced. Protected self returns before scanning, preserving other helpers. Dead/disconnected/following targets clear only assistance mask8192, active state and target. Invalid active indices/counts reject atomically; unit tests cover high-bit preservation, early bypass and rollback after a prior planned cleanup. [Evidence](research/RUST_MOVE_ASSIST_CLEANUP.md).
1936
 
1937
  RE-1000: Sixty-six original-QVM cases recover153419 admission through153596. Both global enables, strict signed wrapped elapsed greater than delay*1000, incomplete state, connected/live/non-following self and selected team are required. Selector exactly1 chooses team2; other values choose team1. A live connected non-following teammate with nonzero6164 blocks entry, while self is excluded. All fields remain unchanged. Exact/negative/wrapped times, three selectors, zero/64 pools and teammate bypasses are covered; flag search and objective effects remain open. [Evidence](research/QVM_MOVE_MODE8_ELIGIBILITY.md).
1938
+
1939
+ RE-1001: Rust ports the read-only mode8 eligibility boundary across66 original cases. Time comparisons wrap as signed words; selector exactly1 chooses team2. Typed teammate scanning excludes self and requires connected/live/non-following same-team busy state before blocking. Counts and indices reject explicitly when used. Unit tests cover strict timing, selector2, self exclusion, inactive teammates and input immutability. Objective search and mutation remain unimplemented. [Evidence](research/RUST_MOVE_MODE8_ELIGIBILITY.md).
docs/PROVISIONAL_TODO.md CHANGED
@@ -1992,3 +1992,5 @@ RE-974–983 completed routing, command rewrite, inactivity, movement-parameter
1992
  - [x] RE-999 Port assistance cleanup and typed target ownership ([evidence](research/RUST_MOVE_ASSIST_CLEANUP.md)).
1993
 
1994
  - [x] RE-1000 Recover mode8 eligibility and teammate exclusion ([evidence](research/QVM_MOVE_MODE8_ELIGIBILITY.md)).
 
 
 
1992
  - [x] RE-999 Port assistance cleanup and typed target ownership ([evidence](research/RUST_MOVE_ASSIST_CLEANUP.md)).
1993
 
1994
  - [x] RE-1000 Recover mode8 eligibility and teammate exclusion ([evidence](research/QVM_MOVE_MODE8_ELIGIBILITY.md)).
1995
+
1996
+ - [x] RE-1001 Port mode8 eligibility and teammate exclusion ([evidence](research/RUST_MOVE_MODE8_ELIGIBILITY.md)).
docs/RESEARCH_LEDGER.md CHANGED
@@ -2675,3 +2675,7 @@ Rust ports152792 admission and first scan across28 original cases, including ful
2675
  ## 2026-10-29 — RE-1000 Recover mode8 eligibility and teammate exclusion
2676
 
2677
  Sixty-six original-QVM cases recover153419 admission through153596. Both global enables, strict signed wrapped elapsed greater than delay*1000, incomplete state, connected/live/non-following self and selected team are required. Selector exactly1 chooses team2; other values choose team1. A live connected non-following teammate with nonzero6164 blocks entry, while self is excluded. All fields remain unchanged. Exact/negative/wrapped times, three selectors, zero/64 pools and teammate bypasses are covered; flag search and objective effects remain open. Focused `cargo xtask test-group qvm-move-mode8-eligibility` passes with deterministic report regeneration and selected regressions. Full suite deferred to task ten. Original assets unchanged; Python uses the flake-declared Nix runtime.
 
 
 
 
 
2675
  ## 2026-10-29 — RE-1000 Recover mode8 eligibility and teammate exclusion
2676
 
2677
  Sixty-six original-QVM cases recover153419 admission through153596. Both global enables, strict signed wrapped elapsed greater than delay*1000, incomplete state, connected/live/non-following self and selected team are required. Selector exactly1 chooses team2; other values choose team1. A live connected non-following teammate with nonzero6164 blocks entry, while self is excluded. All fields remain unchanged. Exact/negative/wrapped times, three selectors, zero/64 pools and teammate bypasses are covered; flag search and objective effects remain open. Focused `cargo xtask test-group qvm-move-mode8-eligibility` passes with deterministic report regeneration and selected regressions. Full suite deferred to task ten. Original assets unchanged; Python uses the flake-declared Nix runtime.
2678
+
2679
+ ## 2026-10-29 — RE-1001 Port mode8 eligibility and teammate exclusion
2680
+
2681
+ Rust ports the read-only mode8 eligibility boundary across66 original cases. Time comparisons wrap as signed words; selector exactly1 chooses team2. Typed teammate scanning excludes self and requires connected/live/non-following same-team busy state before blocking. Counts and indices reject explicitly when used. Unit tests cover strict timing, selector2, self exclusion, inactive teammates and input immutability. Objective search and mutation remain unimplemented. Focused `cargo xtask test-group rust-move-mode8-eligibility` passes with deterministic report regeneration and selected regressions. Full suite deferred to task ten. Original assets unchanged; Python uses the flake-declared Nix runtime.
docs/REVERSE_ENGINEERING_TODO.md CHANGED
@@ -2029,3 +2029,5 @@ RE-974–983 recover routing, command rewriting, inactivity and composed setup t
2029
  - [x] RE-999 Port assistance cleanup and typed target ownership ([evidence](research/RUST_MOVE_ASSIST_CLEANUP.md)).
2030
 
2031
  - [x] RE-1000 Recover mode8 eligibility and teammate exclusion ([evidence](research/QVM_MOVE_MODE8_ELIGIBILITY.md)).
 
 
 
2029
  - [x] RE-999 Port assistance cleanup and typed target ownership ([evidence](research/RUST_MOVE_ASSIST_CLEANUP.md)).
2030
 
2031
  - [x] RE-1000 Recover mode8 eligibility and teammate exclusion ([evidence](research/QVM_MOVE_MODE8_ELIGIBILITY.md)).
2032
+
2033
+ - [x] RE-1001 Port mode8 eligibility and teammate exclusion ([evidence](research/RUST_MOVE_MODE8_ELIGIBILITY.md)).
docs/research/README.md CHANGED
@@ -1563,3 +1563,5 @@ The extension collector adds five focused tests for native tail calls, command-r
1563
  - [RE-999: Port assistance cleanup and typed target ownership](RUST_MOVE_ASSIST_CLEANUP.md).
1564
 
1565
  - [RE-1000: Recover mode8 eligibility and teammate exclusion](QVM_MOVE_MODE8_ELIGIBILITY.md).
 
 
 
1563
  - [RE-999: Port assistance cleanup and typed target ownership](RUST_MOVE_ASSIST_CLEANUP.md).
1564
 
1565
  - [RE-1000: Recover mode8 eligibility and teammate exclusion](QVM_MOVE_MODE8_ELIGIBILITY.md).
1566
+
1567
+ - [RE-1001: Port mode8 eligibility and teammate exclusion](RUST_MOVE_MODE8_ELIGIBILITY.md).
docs/research/RUST_MOVE_MODE8_ELIGIBILITY.md ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ # Port mode8 eligibility and teammate exclusion
2
+
3
+ RE-1001. Focused validation: `cargo xtask test-group rust-move-mode8-eligibility`. [Machine report](rust-move-mode8-eligibility.json).
4
+
5
+ Rust ports the read-only mode8 eligibility boundary across66 original cases. Time comparisons wrap as signed words; selector exactly1 chooses team2. Typed teammate scanning excludes self and requires connected/live/non-following same-team busy state before blocking. Counts and indices reject explicitly when used. Unit tests cover strict timing, selector2, self exclusion, inactive teammates and input immutability. Objective search and mutation remain unimplemented.
docs/research/rust-health-impulse.json CHANGED
@@ -2985,7 +2985,7 @@
2985
  "exit_code": 2
2986
  }
2987
  ],
2988
- "rust_unit_tests": 255,
2989
  "source_report": "docs/research/qvm-health-impulse.json",
2990
  "source_report_sha256": "625fd8064004ea6dbdf398d51d0af844d9c635ff04c0a0790886dddb6ab81e71"
2991
  }
 
2985
  "exit_code": 2
2986
  }
2987
  ],
2988
+ "rust_unit_tests": 257,
2989
  "source_report": "docs/research/qvm-health-impulse.json",
2990
  "source_report_sha256": "625fd8064004ea6dbdf398d51d0af844d9c635ff04c0a0790886dddb6ab81e71"
2991
  }
docs/research/rust-move-mode8-eligibility.json ADDED
@@ -0,0 +1,667 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "limits": "Finite source/Rust contract parity; native time/file/network replies remain supplied or captured.",
3
+ "probes": [
4
+ {
5
+ "rust": {
6
+ "eligible": true,
7
+ "unchanged": true
8
+ },
9
+ "source": {
10
+ "eligible": true,
11
+ "unchanged": true
12
+ }
13
+ },
14
+ {
15
+ "rust": {
16
+ "eligible": true,
17
+ "unchanged": true
18
+ },
19
+ "source": {
20
+ "eligible": true,
21
+ "unchanged": true
22
+ }
23
+ },
24
+ {
25
+ "rust": {
26
+ "eligible": true,
27
+ "unchanged": true
28
+ },
29
+ "source": {
30
+ "eligible": true,
31
+ "unchanged": true
32
+ }
33
+ },
34
+ {
35
+ "rust": {
36
+ "eligible": false,
37
+ "unchanged": true
38
+ },
39
+ "source": {
40
+ "eligible": false,
41
+ "unchanged": true
42
+ }
43
+ },
44
+ {
45
+ "rust": {
46
+ "eligible": false,
47
+ "unchanged": true
48
+ },
49
+ "source": {
50
+ "eligible": false,
51
+ "unchanged": true
52
+ }
53
+ },
54
+ {
55
+ "rust": {
56
+ "eligible": false,
57
+ "unchanged": true
58
+ },
59
+ "source": {
60
+ "eligible": false,
61
+ "unchanged": true
62
+ }
63
+ },
64
+ {
65
+ "rust": {
66
+ "eligible": false,
67
+ "unchanged": true
68
+ },
69
+ "source": {
70
+ "eligible": false,
71
+ "unchanged": true
72
+ }
73
+ },
74
+ {
75
+ "rust": {
76
+ "eligible": false,
77
+ "unchanged": true
78
+ },
79
+ "source": {
80
+ "eligible": false,
81
+ "unchanged": true
82
+ }
83
+ },
84
+ {
85
+ "rust": {
86
+ "eligible": false,
87
+ "unchanged": true
88
+ },
89
+ "source": {
90
+ "eligible": false,
91
+ "unchanged": true
92
+ }
93
+ },
94
+ {
95
+ "rust": {
96
+ "eligible": false,
97
+ "unchanged": true
98
+ },
99
+ "source": {
100
+ "eligible": false,
101
+ "unchanged": true
102
+ }
103
+ },
104
+ {
105
+ "rust": {
106
+ "eligible": false,
107
+ "unchanged": true
108
+ },
109
+ "source": {
110
+ "eligible": false,
111
+ "unchanged": true
112
+ }
113
+ },
114
+ {
115
+ "rust": {
116
+ "eligible": false,
117
+ "unchanged": true
118
+ },
119
+ "source": {
120
+ "eligible": false,
121
+ "unchanged": true
122
+ }
123
+ },
124
+ {
125
+ "rust": {
126
+ "eligible": false,
127
+ "unchanged": true
128
+ },
129
+ "source": {
130
+ "eligible": false,
131
+ "unchanged": true
132
+ }
133
+ },
134
+ {
135
+ "rust": {
136
+ "eligible": false,
137
+ "unchanged": true
138
+ },
139
+ "source": {
140
+ "eligible": false,
141
+ "unchanged": true
142
+ }
143
+ },
144
+ {
145
+ "rust": {
146
+ "eligible": false,
147
+ "unchanged": true
148
+ },
149
+ "source": {
150
+ "eligible": false,
151
+ "unchanged": true
152
+ }
153
+ },
154
+ {
155
+ "rust": {
156
+ "eligible": true,
157
+ "unchanged": true
158
+ },
159
+ "source": {
160
+ "eligible": true,
161
+ "unchanged": true
162
+ }
163
+ },
164
+ {
165
+ "rust": {
166
+ "eligible": true,
167
+ "unchanged": true
168
+ },
169
+ "source": {
170
+ "eligible": true,
171
+ "unchanged": true
172
+ }
173
+ },
174
+ {
175
+ "rust": {
176
+ "eligible": true,
177
+ "unchanged": true
178
+ },
179
+ "source": {
180
+ "eligible": true,
181
+ "unchanged": true
182
+ }
183
+ },
184
+ {
185
+ "rust": {
186
+ "eligible": true,
187
+ "unchanged": true
188
+ },
189
+ "source": {
190
+ "eligible": true,
191
+ "unchanged": true
192
+ }
193
+ },
194
+ {
195
+ "rust": {
196
+ "eligible": true,
197
+ "unchanged": true
198
+ },
199
+ "source": {
200
+ "eligible": true,
201
+ "unchanged": true
202
+ }
203
+ },
204
+ {
205
+ "rust": {
206
+ "eligible": true,
207
+ "unchanged": true
208
+ },
209
+ "source": {
210
+ "eligible": true,
211
+ "unchanged": true
212
+ }
213
+ },
214
+ {
215
+ "rust": {
216
+ "eligible": false,
217
+ "unchanged": true
218
+ },
219
+ "source": {
220
+ "eligible": false,
221
+ "unchanged": true
222
+ }
223
+ },
224
+ {
225
+ "rust": {
226
+ "eligible": false,
227
+ "unchanged": true
228
+ },
229
+ "source": {
230
+ "eligible": false,
231
+ "unchanged": true
232
+ }
233
+ },
234
+ {
235
+ "rust": {
236
+ "eligible": false,
237
+ "unchanged": true
238
+ },
239
+ "source": {
240
+ "eligible": false,
241
+ "unchanged": true
242
+ }
243
+ },
244
+ {
245
+ "rust": {
246
+ "eligible": false,
247
+ "unchanged": true
248
+ },
249
+ "source": {
250
+ "eligible": false,
251
+ "unchanged": true
252
+ }
253
+ },
254
+ {
255
+ "rust": {
256
+ "eligible": false,
257
+ "unchanged": true
258
+ },
259
+ "source": {
260
+ "eligible": false,
261
+ "unchanged": true
262
+ }
263
+ },
264
+ {
265
+ "rust": {
266
+ "eligible": false,
267
+ "unchanged": true
268
+ },
269
+ "source": {
270
+ "eligible": false,
271
+ "unchanged": true
272
+ }
273
+ },
274
+ {
275
+ "rust": {
276
+ "eligible": false,
277
+ "unchanged": true
278
+ },
279
+ "source": {
280
+ "eligible": false,
281
+ "unchanged": true
282
+ }
283
+ },
284
+ {
285
+ "rust": {
286
+ "eligible": false,
287
+ "unchanged": true
288
+ },
289
+ "source": {
290
+ "eligible": false,
291
+ "unchanged": true
292
+ }
293
+ },
294
+ {
295
+ "rust": {
296
+ "eligible": false,
297
+ "unchanged": true
298
+ },
299
+ "source": {
300
+ "eligible": false,
301
+ "unchanged": true
302
+ }
303
+ },
304
+ {
305
+ "rust": {
306
+ "eligible": false,
307
+ "unchanged": true
308
+ },
309
+ "source": {
310
+ "eligible": false,
311
+ "unchanged": true
312
+ }
313
+ },
314
+ {
315
+ "rust": {
316
+ "eligible": false,
317
+ "unchanged": true
318
+ },
319
+ "source": {
320
+ "eligible": false,
321
+ "unchanged": true
322
+ }
323
+ },
324
+ {
325
+ "rust": {
326
+ "eligible": false,
327
+ "unchanged": true
328
+ },
329
+ "source": {
330
+ "eligible": false,
331
+ "unchanged": true
332
+ }
333
+ },
334
+ {
335
+ "rust": {
336
+ "eligible": false,
337
+ "unchanged": true
338
+ },
339
+ "source": {
340
+ "eligible": false,
341
+ "unchanged": true
342
+ }
343
+ },
344
+ {
345
+ "rust": {
346
+ "eligible": false,
347
+ "unchanged": true
348
+ },
349
+ "source": {
350
+ "eligible": false,
351
+ "unchanged": true
352
+ }
353
+ },
354
+ {
355
+ "rust": {
356
+ "eligible": false,
357
+ "unchanged": true
358
+ },
359
+ "source": {
360
+ "eligible": false,
361
+ "unchanged": true
362
+ }
363
+ },
364
+ {
365
+ "rust": {
366
+ "eligible": false,
367
+ "unchanged": true
368
+ },
369
+ "source": {
370
+ "eligible": false,
371
+ "unchanged": true
372
+ }
373
+ },
374
+ {
375
+ "rust": {
376
+ "eligible": false,
377
+ "unchanged": true
378
+ },
379
+ "source": {
380
+ "eligible": false,
381
+ "unchanged": true
382
+ }
383
+ },
384
+ {
385
+ "rust": {
386
+ "eligible": false,
387
+ "unchanged": true
388
+ },
389
+ "source": {
390
+ "eligible": false,
391
+ "unchanged": true
392
+ }
393
+ },
394
+ {
395
+ "rust": {
396
+ "eligible": false,
397
+ "unchanged": true
398
+ },
399
+ "source": {
400
+ "eligible": false,
401
+ "unchanged": true
402
+ }
403
+ },
404
+ {
405
+ "rust": {
406
+ "eligible": false,
407
+ "unchanged": true
408
+ },
409
+ "source": {
410
+ "eligible": false,
411
+ "unchanged": true
412
+ }
413
+ },
414
+ {
415
+ "rust": {
416
+ "eligible": false,
417
+ "unchanged": true
418
+ },
419
+ "source": {
420
+ "eligible": false,
421
+ "unchanged": true
422
+ }
423
+ },
424
+ {
425
+ "rust": {
426
+ "eligible": false,
427
+ "unchanged": true
428
+ },
429
+ "source": {
430
+ "eligible": false,
431
+ "unchanged": true
432
+ }
433
+ },
434
+ {
435
+ "rust": {
436
+ "eligible": false,
437
+ "unchanged": true
438
+ },
439
+ "source": {
440
+ "eligible": false,
441
+ "unchanged": true
442
+ }
443
+ },
444
+ {
445
+ "rust": {
446
+ "eligible": false,
447
+ "unchanged": true
448
+ },
449
+ "source": {
450
+ "eligible": false,
451
+ "unchanged": true
452
+ }
453
+ },
454
+ {
455
+ "rust": {
456
+ "eligible": true,
457
+ "unchanged": true
458
+ },
459
+ "source": {
460
+ "eligible": true,
461
+ "unchanged": true
462
+ }
463
+ },
464
+ {
465
+ "rust": {
466
+ "eligible": true,
467
+ "unchanged": true
468
+ },
469
+ "source": {
470
+ "eligible": true,
471
+ "unchanged": true
472
+ }
473
+ },
474
+ {
475
+ "rust": {
476
+ "eligible": true,
477
+ "unchanged": true
478
+ },
479
+ "source": {
480
+ "eligible": true,
481
+ "unchanged": true
482
+ }
483
+ },
484
+ {
485
+ "rust": {
486
+ "eligible": true,
487
+ "unchanged": true
488
+ },
489
+ "source": {
490
+ "eligible": true,
491
+ "unchanged": true
492
+ }
493
+ },
494
+ {
495
+ "rust": {
496
+ "eligible": true,
497
+ "unchanged": true
498
+ },
499
+ "source": {
500
+ "eligible": true,
501
+ "unchanged": true
502
+ }
503
+ },
504
+ {
505
+ "rust": {
506
+ "eligible": true,
507
+ "unchanged": true
508
+ },
509
+ "source": {
510
+ "eligible": true,
511
+ "unchanged": true
512
+ }
513
+ },
514
+ {
515
+ "rust": {
516
+ "eligible": true,
517
+ "unchanged": true
518
+ },
519
+ "source": {
520
+ "eligible": true,
521
+ "unchanged": true
522
+ }
523
+ },
524
+ {
525
+ "rust": {
526
+ "eligible": true,
527
+ "unchanged": true
528
+ },
529
+ "source": {
530
+ "eligible": true,
531
+ "unchanged": true
532
+ }
533
+ },
534
+ {
535
+ "rust": {
536
+ "eligible": true,
537
+ "unchanged": true
538
+ },
539
+ "source": {
540
+ "eligible": true,
541
+ "unchanged": true
542
+ }
543
+ },
544
+ {
545
+ "rust": {
546
+ "eligible": true,
547
+ "unchanged": true
548
+ },
549
+ "source": {
550
+ "eligible": true,
551
+ "unchanged": true
552
+ }
553
+ },
554
+ {
555
+ "rust": {
556
+ "eligible": true,
557
+ "unchanged": true
558
+ },
559
+ "source": {
560
+ "eligible": true,
561
+ "unchanged": true
562
+ }
563
+ },
564
+ {
565
+ "rust": {
566
+ "eligible": true,
567
+ "unchanged": true
568
+ },
569
+ "source": {
570
+ "eligible": true,
571
+ "unchanged": true
572
+ }
573
+ },
574
+ {
575
+ "rust": {
576
+ "eligible": true,
577
+ "unchanged": true
578
+ },
579
+ "source": {
580
+ "eligible": true,
581
+ "unchanged": true
582
+ }
583
+ },
584
+ {
585
+ "rust": {
586
+ "eligible": true,
587
+ "unchanged": true
588
+ },
589
+ "source": {
590
+ "eligible": true,
591
+ "unchanged": true
592
+ }
593
+ },
594
+ {
595
+ "rust": {
596
+ "eligible": true,
597
+ "unchanged": true
598
+ },
599
+ "source": {
600
+ "eligible": true,
601
+ "unchanged": true
602
+ }
603
+ },
604
+ {
605
+ "rust": {
606
+ "eligible": true,
607
+ "unchanged": true
608
+ },
609
+ "source": {
610
+ "eligible": true,
611
+ "unchanged": true
612
+ }
613
+ },
614
+ {
615
+ "rust": {
616
+ "eligible": true,
617
+ "unchanged": true
618
+ },
619
+ "source": {
620
+ "eligible": true,
621
+ "unchanged": true
622
+ }
623
+ },
624
+ {
625
+ "rust": {
626
+ "eligible": true,
627
+ "unchanged": true
628
+ },
629
+ "source": {
630
+ "eligible": true,
631
+ "unchanged": true
632
+ }
633
+ },
634
+ {
635
+ "rust": {
636
+ "eligible": false,
637
+ "unchanged": true
638
+ },
639
+ "source": {
640
+ "eligible": false,
641
+ "unchanged": true
642
+ }
643
+ },
644
+ {
645
+ "rust": {
646
+ "eligible": false,
647
+ "unchanged": true
648
+ },
649
+ "source": {
650
+ "eligible": false,
651
+ "unchanged": true
652
+ }
653
+ },
654
+ {
655
+ "rust": {
656
+ "eligible": false,
657
+ "unchanged": true
658
+ },
659
+ "source": {
660
+ "eligible": false,
661
+ "unchanged": true
662
+ }
663
+ }
664
+ ],
665
+ "source_report": "docs/research/qvm-move-mode8-eligibility.json",
666
+ "source_report_sha256": "035e884e8f9d7ea4aef6bd63b16dcf92fd7e8542ddd001b88b4adabb158d5764"
667
+ }
scripts/analyze_rust_move_mode8_eligibility.py ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Compare recovered Rust policy with original QVM fixtures."""
2
+ import hashlib,json,os,subprocess
3
+ from pathlib import Path
4
+ from analyze_qvm import write_json
5
+ from rust_lifecycle_support import result
6
+
7
+ def main():
8
+ subprocess.run(['cargo','build','--offline','--package','urt-gameplay','--bin','urt-move-callbacks'],check=True,capture_output=True)
9
+ binary=Path(os.environ.get('CARGO_TARGET_DIR','target'))/'debug'/'urt-move-callbacks'
10
+ path=Path('docs/research/qvm-move-mode8-eligibility.json');data=path.read_bytes();probes=[]
11
+ for p in json.loads(data)['probes']:
12
+ probes.append(dict(source={k:p[k] for k in ['eligible', 'unchanged']},rust=result(binary,'eligibility',encode(p['input']))))
13
+ write_json(Path('docs/research/rust-move-mode8-eligibility.json'),dict(source_report=str(path),source_report_sha256=hashlib.sha256(data).hexdigest(),probes=probes,limits='Finite source/Rust contract parity; native time/file/network replies remain supplied or captured.'))
14
+
15
+ def encode(a):return [a['entity'],a['client'],a['enabled_a'],a['enabled_b'],a['tick'],a['start'],a['delay'],a['complete'],a['connection'],a['health'],a['follow'],a['own_busy'],a['other_busy'],a['other_connection'],a['other_health'],a['other_follow'],a['other_team'],a['team'],a['count'],a['busy_slot'],a['selector'],0]
16
+ if __name__=='__main__':main()
scripts/test_rust_move_mode8_eligibility.py ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ import hashlib,json,unittest
2
+ from pathlib import Path
3
+ class PortTests(unittest.TestCase):
4
+ def test_original_parity(self):
5
+ r=json.loads(Path('docs/research/rust-move-mode8-eligibility.json').read_text());self.assertGreaterEqual(len(r['probes']),12)
6
+ for p in r['probes']:self.assertEqual(p['source'],p['rust'])
7
+ def test_identity(self):
8
+ r=json.loads(Path('docs/research/rust-move-mode8-eligibility.json').read_text());self.assertEqual(hashlib.sha256(Path(r['source_report']).read_bytes()).hexdigest(),r['source_report_sha256'])
9
+ if __name__=='__main__':unittest.main()
xtask/checks.tsv CHANGED
@@ -3908,3 +3908,29 @@ report scripts/analyze_qvm_move_mode8_eligibility.py docs/research/qvm-move-mode
3908
  edge docs/research/qvm-move-mode8-eligibility.json scripts/test_qvm_move_mode8_eligibility.py
3909
  group qvm-move-mode8-eligibility scripts/analyze_qvm_move_mode8_eligibility.py
3910
  edge scripts/analyze_qvm_move_mode8_eligibility.py scripts/analyze_qvm_resumer_audit.py
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3908
  edge docs/research/qvm-move-mode8-eligibility.json scripts/test_qvm_move_mode8_eligibility.py
3909
  group qvm-move-mode8-eligibility scripts/analyze_qvm_move_mode8_eligibility.py
3910
  edge scripts/analyze_qvm_move_mode8_eligibility.py scripts/analyze_qvm_resumer_audit.py
3911
+ report scripts/analyze_rust_move_mode8_eligibility.py docs/research/rust-move-mode8-eligibility.json
3912
+ edge docs/research/rust-move-mode8-eligibility.json scripts/test_rust_move_mode8_eligibility.py
3913
+ group rust-move-mode8-eligibility scripts/analyze_rust_move_mode8_eligibility.py
3914
+ edge scripts/analyze_rust_move_mode8_eligibility.py scripts/analyze_qvm_resumer_audit.py
3915
+ edge Cargo.toml scripts/analyze_rust_move_mode8_eligibility.py
3916
+ edge Cargo.lock scripts/analyze_rust_move_mode8_eligibility.py
3917
+ edge crates/urt-gameplay/Cargo.toml scripts/analyze_rust_move_mode8_eligibility.py
3918
+ edge crates/urt-gameplay/src/lib.rs scripts/analyze_rust_move_mode8_eligibility.py
3919
+ edge crates/urt-gameplay/src/filter_loading.rs scripts/analyze_rust_move_mode8_eligibility.py
3920
+ edge crates/urt-gameplay/src/filter_lifecycle.rs scripts/analyze_rust_move_mode8_eligibility.py
3921
+ edge crates/urt-gameplay/src/native_policy.rs scripts/analyze_rust_move_mode8_eligibility.py
3922
+ edge crates/urt-gameplay/src/bin/urt-filter-loading.rs scripts/analyze_rust_move_mode8_eligibility.py
3923
+ edge crates/urt-gameplay/src/qvm_format.rs scripts/analyze_rust_move_mode8_eligibility.py
3924
+ edge docs/research/qvm-move-mode8-eligibility.json scripts/analyze_rust_move_mode8_eligibility.py
3925
+ group rust-move-mode8-eligibility scripts/analyze_rust_health_impulse.py
3926
+ group gameplay scripts/analyze_rust_move_mode8_eligibility.py
3927
+ edge crates/urt-gameplay/src/filter_commands.rs scripts/analyze_rust_move_mode8_eligibility.py
3928
+ edge crates/urt-gameplay/src/bin/urt-filter-commands.rs scripts/analyze_rust_move_mode8_eligibility.py
3929
+ edge crates/urt-gameplay/src/client_timer.rs scripts/analyze_rust_move_mode8_eligibility.py
3930
+ edge crates/urt-gameplay/src/bin/urt-client-timer.rs scripts/analyze_rust_move_mode8_eligibility.py
3931
+ edge crates/urt-gameplay/src/client_processing.rs scripts/analyze_rust_move_mode8_eligibility.py
3932
+ edge crates/urt-gameplay/src/bin/urt-client-processing.rs scripts/analyze_rust_move_mode8_eligibility.py
3933
+ edge crates/urt-gameplay/src/client_move_context.rs scripts/analyze_rust_move_mode8_eligibility.py
3934
+ edge crates/urt-gameplay/src/bin/urt-client-move-context.rs scripts/analyze_rust_move_mode8_eligibility.py
3935
+ edge crates/urt-gameplay/src/move_callbacks.rs scripts/analyze_rust_move_mode8_eligibility.py
3936
+ edge crates/urt-gameplay/src/bin/urt-move-callbacks.rs scripts/analyze_rust_move_mode8_eligibility.py
xtask/src/lib.rs CHANGED
@@ -1139,6 +1139,7 @@ mod integration_tests {
1139
  ("move_callbacks", "move_meter"),
1140
  ("move_callbacks", "move_self_recovery"),
1141
  ("move_callbacks", "move_assist_cleanup"),
 
1142
  ("client_move_context", "client_move_scratch"),
1143
  ("client_processing", "client_routing"),
1144
  ("client_timer", "client_routing"),
@@ -1360,6 +1361,7 @@ mod integration_tests {
1360
  ("qvm-move-meter", "move_meter"),
1361
  ("qvm-move-self-recovery", "move_self_recovery"),
1362
  ("qvm-move-assist-cleanup", "move_assist_cleanup"),
 
1363
  ("qvm-client-move-scratch", "client_move_scratch"),
1364
  ("qvm-client-routing", "client_routing"),
1365
  ("qvm-client-command-rewrite", "client_command_rewrite"),
 
1139
  ("move_callbacks", "move_meter"),
1140
  ("move_callbacks", "move_self_recovery"),
1141
  ("move_callbacks", "move_assist_cleanup"),
1142
+ ("move_callbacks", "move_mode8_eligibility"),
1143
  ("client_move_context", "client_move_scratch"),
1144
  ("client_processing", "client_routing"),
1145
  ("client_timer", "client_routing"),
 
1361
  ("qvm-move-meter", "move_meter"),
1362
  ("qvm-move-self-recovery", "move_self_recovery"),
1363
  ("qvm-move-assist-cleanup", "move_assist_cleanup"),
1364
+ ("qvm-move-mode8-eligibility", "move_mode8_eligibility"),
1365
  ("qvm-client-move-scratch", "client_move_scratch"),
1366
  ("qvm-client-routing", "client_routing"),
1367
  ("qvm-client-command-rewrite", "client_command_rewrite"),