nmarshall commited on
Commit
0cb899f
·
1 Parent(s): b158258

gameplay: port formatter wrapper policies

Browse files
crates/urt-gameplay/src/bin/urt-lifecycle.rs CHANGED
@@ -394,7 +394,20 @@ fn run() -> Result<(), String> {
394
  second.cached_ping as u32
395
  );
396
  }
397
- "qvm-format" if w.len() >= 2 && w[0] <= 4096 && w[1] <= 64 => {
 
 
 
 
 
 
 
 
 
 
 
 
 
398
  let len = w[0] as usize;
399
  let n = w[1] as usize;
400
  if w.len() < 2 + len {
@@ -443,13 +456,41 @@ fn run() -> Result<(), String> {
443
  _ => urt_gameplay::qvm_format::Argument::Bytes(None),
444
  })
445
  .collect();
446
- let out = urt_gameplay::qvm_format::render(&template, &args)
447
- .map_err(|e| format!("unsupported input: {e:?}"))?;
448
- println!(
449
- "{{\"cstring_hex\":\"{}\",\"count\":{}}}",
450
- out.iter().map(|v| format!("{v:02x}")).collect::<String>(),
451
- out.len() - 1
452
- );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
453
  }
454
  "scoreboard-stream" if w.len() >= 6 && w[4] <= 64 && w[5] <= 64 => {
455
  let n = w[4] as usize;
 
394
  second.cached_ping as u32
395
  );
396
  }
397
+ "qvm-format" | "qvm-format-bounded" | "qvm-format-ring" => {
398
+ let parameter = if kind == "qvm-format" {
399
+ 0
400
+ } else {
401
+ *w.first().ok_or("missing wrapper parameter")?
402
+ };
403
+ let w = if kind == "qvm-format" {
404
+ w.as_slice()
405
+ } else {
406
+ &w[1..]
407
+ };
408
+ if w.len() < 2 || w[0] > 4096 || w[1] > 64 {
409
+ return Err("invalid format shape".into());
410
+ }
411
  let len = w[0] as usize;
412
  let n = w[1] as usize;
413
  if w.len() < 2 + len {
 
456
  _ => urt_gameplay::qvm_format::Argument::Bytes(None),
457
  })
458
  .collect();
459
+ if kind == "qvm-format-bounded" {
460
+ let p = urt_gameplay::qvm_format::bounded(&template, &args, parameter as usize)
461
+ .map_err(|e| format!("unsupported input: {e:?}"))?;
462
+ println!(
463
+ "{{\"buffer_hex\":\"{}\",\"return_count\":{},\"warning\":{}}}",
464
+ p.bytes
465
+ .iter()
466
+ .map(|v| format!("{v:02x}"))
467
+ .collect::<String>(),
468
+ p.return_count,
469
+ p.warning
470
+ );
471
+ } else if kind == "qvm-format-ring" {
472
+ let mut counter = parameter;
473
+ let (index, bytes) = urt_gameplay::qvm_format::ring(&template, &args, &mut counter)
474
+ .map_err(|e| format!("unsupported input: {e:?}"))?;
475
+ let end = bytes.iter().position(|b| *b == 0).unwrap();
476
+ println!(
477
+ "{{\"cstring_hex\":\"{}\",\"index\":{},\"counter\":{}}}",
478
+ bytes[..=end]
479
+ .iter()
480
+ .map(|v| format!("{v:02x}"))
481
+ .collect::<String>(),
482
+ index,
483
+ counter
484
+ );
485
+ } else {
486
+ let out = urt_gameplay::qvm_format::render(&template, &args)
487
+ .map_err(|e| format!("unsupported input: {e:?}"))?;
488
+ println!(
489
+ "{{\"cstring_hex\":\"{}\",\"count\":{}}}",
490
+ out.iter().map(|v| format!("{v:02x}")).collect::<String>(),
491
+ out.len() - 1
492
+ );
493
+ }
494
  }
495
  "scoreboard-stream" if w.len() >= 6 && w[4] <= 64 && w[5] <= 64 => {
496
  let n = w[4] as usize;
crates/urt-gameplay/src/qvm_format.rs CHANGED
@@ -141,10 +141,65 @@ pub fn render(template: &[u8], arguments: &[Argument<'_>]) -> Result<Vec<u8>, Er
141
  out.push(0);
142
  Ok(out)
143
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
144
  #[cfg(test)]
145
  mod tests {
146
  use super::*;
147
  #[test]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
148
  fn source_padding_precision_and_unknown_dispatch() {
149
  assert_eq!(
150
  render(
 
141
  out.push(0);
142
  Ok(out)
143
  }
144
+ #[derive(Clone, Debug, PartialEq, Eq)]
145
+ pub struct Bounded {
146
+ pub bytes: Vec<u8>,
147
+ pub return_count: usize,
148
+ pub warning: bool,
149
+ }
150
+ /// Source70386 success/clipping path with explicit native strncpy byte policy.
151
+ /// Fatal32000 and diagnostic side effects are represented by rejection/flag.
152
+ pub fn bounded(template: &[u8], args: &[Argument<'_>], capacity: usize) -> Result<Bounded, Error> {
153
+ if !(1..=32000).contains(&capacity) {
154
+ return Err(Error::Limit);
155
+ }
156
+ let raw = render(template, args)?;
157
+ let count = raw.len() - 1;
158
+ if count >= 32000 {
159
+ return Err(Error::Limit);
160
+ }
161
+ let visible = raw.iter().position(|b| *b == 0).unwrap();
162
+ let copied = visible.min(capacity - 1);
163
+ let mut bytes = vec![0; capacity];
164
+ bytes[..copied].copy_from_slice(&raw[..copied]);
165
+ Ok(Bounded {
166
+ bytes,
167
+ return_count: count.min(capacity - 1),
168
+ warning: count >= capacity,
169
+ })
170
+ }
171
+ /// Source70458 successful alternating-buffer contract. Error-side-effect timing
172
+ /// and native storage ownership are excluded; valid outputs retain opaque bytes.
173
+ pub fn ring(
174
+ template: &[u8],
175
+ args: &[Argument<'_>],
176
+ counter: &mut u32,
177
+ ) -> Result<(u32, Vec<u8>), Error> {
178
+ let bytes = render(template, args)?;
179
+ let index = *counter & 1;
180
+ *counter = counter.wrapping_add(1);
181
+ Ok((index, bytes))
182
+ }
183
  #[cfg(test)]
184
  mod tests {
185
  use super::*;
186
  #[test]
187
+ fn bounded_count_can_exceed_visible_length() {
188
+ let p = bounded(b"%c/AB", &[Argument::Word(0)], 3).unwrap();
189
+ assert_eq!(p.bytes, [0, 0, 0]);
190
+ assert_eq!(p.return_count, 2);
191
+ assert!(p.warning);
192
+ assert_eq!(bounded(b"abc", &[], 0), Err(Error::Limit));
193
+ }
194
+ #[test]
195
+ fn ring_wrap_and_successive_slots() {
196
+ let mut c = u32::MAX;
197
+ let a = ring(b"%s", &[Argument::Bytes(None)], &mut c).unwrap();
198
+ let b = ring(b"X", &[], &mut c).unwrap();
199
+ assert_eq!((a.0, b.0, c), (1, 0, 1));
200
+ assert_eq!(a.1, b"(null)\0");
201
+ }
202
+ #[test]
203
  fn source_padding_precision_and_unknown_dispatch() {
204
  assert_eq!(
205
  render(
docs/FINDINGS.md CHANGED
@@ -1457,3 +1457,6 @@ RE-802–811 adds opt-in coherent memory, original string/scoreboard control flo
1457
 
1458
 
1459
  **RE-819:** 70386 warns at count>=capacity, returns the clipped count and copies a guaranteed terminal NUL; embedded NUL separates reported count from visible string length. [Evidence](research/QVM_FORMAT_WRAPPERS.md).
 
 
 
 
1457
 
1458
 
1459
  **RE-819:** 70386 warns at count>=capacity, returns the clipped count and copies a guaranteed terminal NUL; embedded NUL separates reported count from visible string length. [Evidence](research/QVM_FORMAT_WRAPPERS.md).
1460
+
1461
+
1462
+ **RE-820:** Rust matches original clipping counts, visible bytes, warning thresholds and alternating buffer counters across56 wrapper cases. [Evidence](research/RUST_FORMAT_WRAPPERS.md).
docs/PROVISIONAL_TODO.md CHANGED
@@ -1467,3 +1467,6 @@ RE-818: Rust scoreboard construction now uses recovered decimal/format semantics
1467
 
1468
 
1469
  RE-819: 70386 warns at count>=capacity, returns the clipped count and copies a guaranteed terminal NUL; embedded NUL separates reported count from visible string length. Full runtime completion remains open. [Evidence](research/QVM_FORMAT_WRAPPERS.md).
 
 
 
 
1467
 
1468
 
1469
  RE-819: 70386 warns at count>=capacity, returns the clipped count and copies a guaranteed terminal NUL; embedded NUL separates reported count from visible string length. Full runtime completion remains open. [Evidence](research/QVM_FORMAT_WRAPPERS.md).
1470
+
1471
+
1472
+ RE-820: Rust matches original clipping counts, visible bytes, warning thresholds and alternating buffer counters across56 wrapper cases. Full runtime completion remains open. [Evidence](research/RUST_FORMAT_WRAPPERS.md).
docs/RESEARCH_LEDGER.md CHANGED
@@ -1763,3 +1763,8 @@ Rust scoreboard construction now uses recovered decimal/format semantics, replac
1763
  ## 2026-10-27 — RE-819 Original bounded formatter clipping diagnostics and output-ring policy
1764
 
1765
  70386 warns at count>=capacity, returns the clipped count and copies a guaranteed terminal NUL; embedded NUL separates reported count from visible string length. Focused `cargo xtask test-group format-wrappers` passes (5 Python tests); selected reports regenerate twice deterministically, with storage, documentation links and whitespace guards. Full suite deferred to task ten. Original assets unchanged; source fixtures do not establish complete native/runtime parity.
 
 
 
 
 
 
1763
  ## 2026-10-27 — RE-819 Original bounded formatter clipping diagnostics and output-ring policy
1764
 
1765
  70386 warns at count>=capacity, returns the clipped count and copies a guaranteed terminal NUL; embedded NUL separates reported count from visible string length. Focused `cargo xtask test-group format-wrappers` passes (5 Python tests); selected reports regenerate twice deterministically, with storage, documentation links and whitespace guards. Full suite deferred to task ten. Original assets unchanged; source fixtures do not establish complete native/runtime parity.
1766
+
1767
+
1768
+ ## 2026-10-27 — RE-820 Rust bounded and rotating formatter wrapper contracts
1769
+
1770
+ Rust matches original clipping counts, visible bytes, warning thresholds and alternating buffer counters across56 wrapper cases. Focused `cargo xtask test-group rust-format-wrappers` passes (11 Python tests); selected reports regenerate twice deterministically, with storage, documentation links and whitespace guards. Full suite deferred to task ten. Original assets unchanged; source fixtures do not establish complete native/runtime parity.
docs/REVERSE_ENGINEERING_TODO.md CHANGED
@@ -1507,3 +1507,6 @@ Purpose: turn the supplied installation into an evidence-backed specification of
1507
 
1508
 
1509
  - [x] RE-819 Original bounded formatter clipping diagnostics and output-ring policy ([evidence](research/QVM_FORMAT_WRAPPERS.md)).
 
 
 
 
1507
 
1508
 
1509
  - [x] RE-819 Original bounded formatter clipping diagnostics and output-ring policy ([evidence](research/QVM_FORMAT_WRAPPERS.md)).
1510
+
1511
+
1512
+ - [x] RE-820 Rust bounded and rotating formatter wrapper contracts ([evidence](research/RUST_FORMAT_WRAPPERS.md)).
docs/research/README.md CHANGED
@@ -1047,3 +1047,6 @@ The extension collector adds five focused tests for native tail calls, command-r
1047
 
1048
 
1049
  [RE-819: Original bounded formatter clipping diagnostics and output-ring policy](QVM_FORMAT_WRAPPERS.md).
 
 
 
 
1047
 
1048
 
1049
  [RE-819: Original bounded formatter clipping diagnostics and output-ring policy](QVM_FORMAT_WRAPPERS.md).
1050
+
1051
+
1052
+ [RE-820: Rust bounded and rotating formatter wrapper contracts](RUST_FORMAT_WRAPPERS.md).
docs/research/RUST_FORMAT_WRAPPERS.md ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ # Rust bounded and rotating formatter wrapper contracts
2
+
3
+ RE-820. Reproduce with `python3 scripts/analyze_rust_format_wrappers.py`; focused validation: `cargo xtask test-group rust-format-wrappers`. [Machine report](rust-format-wrappers.json).
4
+
5
+ Typed bounded and ring APIs compose the recovered byte formatter. Embedded NUL handling preserves the difference between rendered count and visible C-string length. Unit tests check truncation, invalid capacity and successive wrap/slot behavior; existing55 formatter parity cases rerun after CLI parser reuse. Native storage, warning effects and fatal/error-side-effect timing remain outside this contract.
6
+
7
+ Original executable/native runtime and complete Linux/macOS Rust/Bevy/Solari acceptance remain unverified.
docs/research/rust-format-wrappers.json ADDED
@@ -0,0 +1,681 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "bounded": [
3
+ {
4
+ "rust": {
5
+ "buffer_hex": "00",
6
+ "return_count": 0,
7
+ "warning": true
8
+ },
9
+ "source": {
10
+ "buffer_hex": "00",
11
+ "return_count": 0,
12
+ "warning": true
13
+ }
14
+ },
15
+ {
16
+ "rust": {
17
+ "buffer_hex": "2d00",
18
+ "return_count": 1,
19
+ "warning": true
20
+ },
21
+ "source": {
22
+ "buffer_hex": "2d00",
23
+ "return_count": 1,
24
+ "warning": true
25
+ }
26
+ },
27
+ {
28
+ "rust": {
29
+ "buffer_hex": "2d313200",
30
+ "return_count": 3,
31
+ "warning": false
32
+ },
33
+ "source": {
34
+ "buffer_hex": "2d313200",
35
+ "return_count": 3,
36
+ "warning": false
37
+ }
38
+ },
39
+ {
40
+ "rust": {
41
+ "buffer_hex": "2d31320000000000",
42
+ "return_count": 3,
43
+ "warning": false
44
+ },
45
+ "source": {
46
+ "buffer_hex": "2d31320000000000",
47
+ "return_count": 3,
48
+ "warning": false
49
+ }
50
+ },
51
+ {
52
+ "rust": {
53
+ "buffer_hex": "2d31320000000000000000000000000000000000000000000000000000000000",
54
+ "return_count": 3,
55
+ "warning": false
56
+ },
57
+ "source": {
58
+ "buffer_hex": "2d31320000000000000000000000000000000000000000000000000000000000",
59
+ "return_count": 3,
60
+ "warning": false
61
+ }
62
+ },
63
+ {
64
+ "rust": {
65
+ "buffer_hex": "00",
66
+ "return_count": 0,
67
+ "warning": true
68
+ },
69
+ "source": {
70
+ "buffer_hex": "00",
71
+ "return_count": 0,
72
+ "warning": true
73
+ }
74
+ },
75
+ {
76
+ "rust": {
77
+ "buffer_hex": "3000",
78
+ "return_count": 1,
79
+ "warning": true
80
+ },
81
+ "source": {
82
+ "buffer_hex": "3000",
83
+ "return_count": 1,
84
+ "warning": true
85
+ }
86
+ },
87
+ {
88
+ "rust": {
89
+ "buffer_hex": "30302d00",
90
+ "return_count": 3,
91
+ "warning": true
92
+ },
93
+ "source": {
94
+ "buffer_hex": "30302d00",
95
+ "return_count": 3,
96
+ "warning": true
97
+ }
98
+ },
99
+ {
100
+ "rust": {
101
+ "buffer_hex": "30302d3132000000",
102
+ "return_count": 5,
103
+ "warning": false
104
+ },
105
+ "source": {
106
+ "buffer_hex": "30302d3132000000",
107
+ "return_count": 5,
108
+ "warning": false
109
+ }
110
+ },
111
+ {
112
+ "rust": {
113
+ "buffer_hex": "30302d3132000000000000000000000000000000000000000000000000000000",
114
+ "return_count": 5,
115
+ "warning": false
116
+ },
117
+ "source": {
118
+ "buffer_hex": "30302d3132000000000000000000000000000000000000000000000000000000",
119
+ "return_count": 5,
120
+ "warning": false
121
+ }
122
+ },
123
+ {
124
+ "rust": {
125
+ "buffer_hex": "00",
126
+ "return_count": 0,
127
+ "warning": true
128
+ },
129
+ "source": {
130
+ "buffer_hex": "00",
131
+ "return_count": 0,
132
+ "warning": true
133
+ }
134
+ },
135
+ {
136
+ "rust": {
137
+ "buffer_hex": "4100",
138
+ "return_count": 1,
139
+ "warning": true
140
+ },
141
+ "source": {
142
+ "buffer_hex": "4100",
143
+ "return_count": 1,
144
+ "warning": true
145
+ }
146
+ },
147
+ {
148
+ "rust": {
149
+ "buffer_hex": "41420000",
150
+ "return_count": 2,
151
+ "warning": false
152
+ },
153
+ "source": {
154
+ "buffer_hex": "41420000",
155
+ "return_count": 2,
156
+ "warning": false
157
+ }
158
+ },
159
+ {
160
+ "rust": {
161
+ "buffer_hex": "4142000000000000",
162
+ "return_count": 2,
163
+ "warning": false
164
+ },
165
+ "source": {
166
+ "buffer_hex": "4142000000000000",
167
+ "return_count": 2,
168
+ "warning": false
169
+ }
170
+ },
171
+ {
172
+ "rust": {
173
+ "buffer_hex": "4142000000000000000000000000000000000000000000000000000000000000",
174
+ "return_count": 2,
175
+ "warning": false
176
+ },
177
+ "source": {
178
+ "buffer_hex": "4142000000000000000000000000000000000000000000000000000000000000",
179
+ "return_count": 2,
180
+ "warning": false
181
+ }
182
+ },
183
+ {
184
+ "rust": {
185
+ "buffer_hex": "00",
186
+ "return_count": 0,
187
+ "warning": true
188
+ },
189
+ "source": {
190
+ "buffer_hex": "00",
191
+ "return_count": 0,
192
+ "warning": true
193
+ }
194
+ },
195
+ {
196
+ "rust": {
197
+ "buffer_hex": "6100",
198
+ "return_count": 1,
199
+ "warning": true
200
+ },
201
+ "source": {
202
+ "buffer_hex": "6100",
203
+ "return_count": 1,
204
+ "warning": true
205
+ }
206
+ },
207
+ {
208
+ "rust": {
209
+ "buffer_hex": "61626300",
210
+ "return_count": 3,
211
+ "warning": true
212
+ },
213
+ "source": {
214
+ "buffer_hex": "61626300",
215
+ "return_count": 3,
216
+ "warning": true
217
+ }
218
+ },
219
+ {
220
+ "rust": {
221
+ "buffer_hex": "6162634142434400",
222
+ "return_count": 7,
223
+ "warning": true
224
+ },
225
+ "source": {
226
+ "buffer_hex": "6162634142434400",
227
+ "return_count": 7,
228
+ "warning": true
229
+ }
230
+ },
231
+ {
232
+ "rust": {
233
+ "buffer_hex": "6162634142434445464700000000000000000000000000000000000000000000",
234
+ "return_count": 10,
235
+ "warning": false
236
+ },
237
+ "source": {
238
+ "buffer_hex": "6162634142434445464700000000000000000000000000000000000000000000",
239
+ "return_count": 10,
240
+ "warning": false
241
+ }
242
+ },
243
+ {
244
+ "rust": {
245
+ "buffer_hex": "00",
246
+ "return_count": 0,
247
+ "warning": true
248
+ },
249
+ "source": {
250
+ "buffer_hex": "00",
251
+ "return_count": 0,
252
+ "warning": true
253
+ }
254
+ },
255
+ {
256
+ "rust": {
257
+ "buffer_hex": "0000",
258
+ "return_count": 1,
259
+ "warning": true
260
+ },
261
+ "source": {
262
+ "buffer_hex": "0000",
263
+ "return_count": 1,
264
+ "warning": true
265
+ }
266
+ },
267
+ {
268
+ "rust": {
269
+ "buffer_hex": "00000000",
270
+ "return_count": 3,
271
+ "warning": true
272
+ },
273
+ "source": {
274
+ "buffer_hex": "00000000",
275
+ "return_count": 3,
276
+ "warning": true
277
+ }
278
+ },
279
+ {
280
+ "rust": {
281
+ "buffer_hex": "0000000000000000",
282
+ "return_count": 7,
283
+ "warning": true
284
+ },
285
+ "source": {
286
+ "buffer_hex": "0000000000000000",
287
+ "return_count": 7,
288
+ "warning": true
289
+ }
290
+ },
291
+ {
292
+ "rust": {
293
+ "buffer_hex": "0000000000000000000000000000000000000000000000000000000000000000",
294
+ "return_count": 13,
295
+ "warning": false
296
+ },
297
+ "source": {
298
+ "buffer_hex": "0000000000000000000000000000000000000000000000000000000000000000",
299
+ "return_count": 13,
300
+ "warning": false
301
+ }
302
+ },
303
+ {
304
+ "rust": {
305
+ "buffer_hex": "00",
306
+ "return_count": 0,
307
+ "warning": true
308
+ },
309
+ "source": {
310
+ "buffer_hex": "00",
311
+ "return_count": 0,
312
+ "warning": true
313
+ }
314
+ },
315
+ {
316
+ "rust": {
317
+ "buffer_hex": "2500",
318
+ "return_count": 1,
319
+ "warning": false
320
+ },
321
+ "source": {
322
+ "buffer_hex": "2500",
323
+ "return_count": 1,
324
+ "warning": false
325
+ }
326
+ },
327
+ {
328
+ "rust": {
329
+ "buffer_hex": "25000000",
330
+ "return_count": 1,
331
+ "warning": false
332
+ },
333
+ "source": {
334
+ "buffer_hex": "25000000",
335
+ "return_count": 1,
336
+ "warning": false
337
+ }
338
+ },
339
+ {
340
+ "rust": {
341
+ "buffer_hex": "2500000000000000",
342
+ "return_count": 1,
343
+ "warning": false
344
+ },
345
+ "source": {
346
+ "buffer_hex": "2500000000000000",
347
+ "return_count": 1,
348
+ "warning": false
349
+ }
350
+ },
351
+ {
352
+ "rust": {
353
+ "buffer_hex": "2500000000000000000000000000000000000000000000000000000000000000",
354
+ "return_count": 1,
355
+ "warning": false
356
+ },
357
+ "source": {
358
+ "buffer_hex": "2500000000000000000000000000000000000000000000000000000000000000",
359
+ "return_count": 1,
360
+ "warning": false
361
+ }
362
+ },
363
+ {
364
+ "rust": {
365
+ "buffer_hex": "00",
366
+ "return_count": 0,
367
+ "warning": true
368
+ },
369
+ "source": {
370
+ "buffer_hex": "00",
371
+ "return_count": 0,
372
+ "warning": true
373
+ }
374
+ },
375
+ {
376
+ "rust": {
377
+ "buffer_hex": "2800",
378
+ "return_count": 1,
379
+ "warning": true
380
+ },
381
+ "source": {
382
+ "buffer_hex": "2800",
383
+ "return_count": 1,
384
+ "warning": true
385
+ }
386
+ },
387
+ {
388
+ "rust": {
389
+ "buffer_hex": "286e7500",
390
+ "return_count": 3,
391
+ "warning": true
392
+ },
393
+ "source": {
394
+ "buffer_hex": "286e7500",
395
+ "return_count": 3,
396
+ "warning": true
397
+ }
398
+ },
399
+ {
400
+ "rust": {
401
+ "buffer_hex": "286e756c6c290000",
402
+ "return_count": 6,
403
+ "warning": false
404
+ },
405
+ "source": {
406
+ "buffer_hex": "286e756c6c290000",
407
+ "return_count": 6,
408
+ "warning": false
409
+ }
410
+ },
411
+ {
412
+ "rust": {
413
+ "buffer_hex": "286e756c6c290000000000000000000000000000000000000000000000000000",
414
+ "return_count": 6,
415
+ "warning": false
416
+ },
417
+ "source": {
418
+ "buffer_hex": "286e756c6c290000000000000000000000000000000000000000000000000000",
419
+ "return_count": 6,
420
+ "warning": false
421
+ }
422
+ }
423
+ ],
424
+ "limits": "Rust successful/clipping70386 and rotating70458 wrapper semantics match56 actual-QVM fixtures. Native strncpy bytes modeled; warnings represented as flags and fatal/error-side-effect behavior excluded.",
425
+ "rings": [
426
+ {
427
+ "rust": {
428
+ "counter": 1,
429
+ "cstring_hex": "2d313200",
430
+ "index": 0
431
+ },
432
+ "source": {
433
+ "counter": 1,
434
+ "cstring_hex": "2d313200",
435
+ "index": 0
436
+ }
437
+ },
438
+ {
439
+ "rust": {
440
+ "counter": 2,
441
+ "cstring_hex": "2d313200",
442
+ "index": 1
443
+ },
444
+ "source": {
445
+ "counter": 2,
446
+ "cstring_hex": "2d313200",
447
+ "index": 1
448
+ }
449
+ },
450
+ {
451
+ "rust": {
452
+ "counter": 0,
453
+ "cstring_hex": "2d313200",
454
+ "index": 1
455
+ },
456
+ "source": {
457
+ "counter": 0,
458
+ "cstring_hex": "2d313200",
459
+ "index": 1
460
+ }
461
+ },
462
+ {
463
+ "rust": {
464
+ "counter": 1,
465
+ "cstring_hex": "30302d313200",
466
+ "index": 0
467
+ },
468
+ "source": {
469
+ "counter": 1,
470
+ "cstring_hex": "30302d313200",
471
+ "index": 0
472
+ }
473
+ },
474
+ {
475
+ "rust": {
476
+ "counter": 2,
477
+ "cstring_hex": "30302d313200",
478
+ "index": 1
479
+ },
480
+ "source": {
481
+ "counter": 2,
482
+ "cstring_hex": "30302d313200",
483
+ "index": 1
484
+ }
485
+ },
486
+ {
487
+ "rust": {
488
+ "counter": 0,
489
+ "cstring_hex": "30302d313200",
490
+ "index": 1
491
+ },
492
+ "source": {
493
+ "counter": 0,
494
+ "cstring_hex": "30302d313200",
495
+ "index": 1
496
+ }
497
+ },
498
+ {
499
+ "rust": {
500
+ "counter": 1,
501
+ "cstring_hex": "414200",
502
+ "index": 0
503
+ },
504
+ "source": {
505
+ "counter": 1,
506
+ "cstring_hex": "414200",
507
+ "index": 0
508
+ }
509
+ },
510
+ {
511
+ "rust": {
512
+ "counter": 2,
513
+ "cstring_hex": "414200",
514
+ "index": 1
515
+ },
516
+ "source": {
517
+ "counter": 2,
518
+ "cstring_hex": "414200",
519
+ "index": 1
520
+ }
521
+ },
522
+ {
523
+ "rust": {
524
+ "counter": 0,
525
+ "cstring_hex": "414200",
526
+ "index": 1
527
+ },
528
+ "source": {
529
+ "counter": 0,
530
+ "cstring_hex": "414200",
531
+ "index": 1
532
+ }
533
+ },
534
+ {
535
+ "rust": {
536
+ "counter": 1,
537
+ "cstring_hex": "6162634142434445464700",
538
+ "index": 0
539
+ },
540
+ "source": {
541
+ "counter": 1,
542
+ "cstring_hex": "6162634142434445464700",
543
+ "index": 0
544
+ }
545
+ },
546
+ {
547
+ "rust": {
548
+ "counter": 2,
549
+ "cstring_hex": "6162634142434445464700",
550
+ "index": 1
551
+ },
552
+ "source": {
553
+ "counter": 2,
554
+ "cstring_hex": "6162634142434445464700",
555
+ "index": 1
556
+ }
557
+ },
558
+ {
559
+ "rust": {
560
+ "counter": 0,
561
+ "cstring_hex": "6162634142434445464700",
562
+ "index": 1
563
+ },
564
+ "source": {
565
+ "counter": 0,
566
+ "cstring_hex": "6162634142434445464700",
567
+ "index": 1
568
+ }
569
+ },
570
+ {
571
+ "rust": {
572
+ "counter": 1,
573
+ "cstring_hex": "00",
574
+ "index": 0
575
+ },
576
+ "source": {
577
+ "counter": 1,
578
+ "cstring_hex": "00",
579
+ "index": 0
580
+ }
581
+ },
582
+ {
583
+ "rust": {
584
+ "counter": 2,
585
+ "cstring_hex": "00",
586
+ "index": 1
587
+ },
588
+ "source": {
589
+ "counter": 2,
590
+ "cstring_hex": "00",
591
+ "index": 1
592
+ }
593
+ },
594
+ {
595
+ "rust": {
596
+ "counter": 0,
597
+ "cstring_hex": "00",
598
+ "index": 1
599
+ },
600
+ "source": {
601
+ "counter": 0,
602
+ "cstring_hex": "00",
603
+ "index": 1
604
+ }
605
+ },
606
+ {
607
+ "rust": {
608
+ "counter": 1,
609
+ "cstring_hex": "2500",
610
+ "index": 0
611
+ },
612
+ "source": {
613
+ "counter": 1,
614
+ "cstring_hex": "2500",
615
+ "index": 0
616
+ }
617
+ },
618
+ {
619
+ "rust": {
620
+ "counter": 2,
621
+ "cstring_hex": "2500",
622
+ "index": 1
623
+ },
624
+ "source": {
625
+ "counter": 2,
626
+ "cstring_hex": "2500",
627
+ "index": 1
628
+ }
629
+ },
630
+ {
631
+ "rust": {
632
+ "counter": 0,
633
+ "cstring_hex": "2500",
634
+ "index": 1
635
+ },
636
+ "source": {
637
+ "counter": 0,
638
+ "cstring_hex": "2500",
639
+ "index": 1
640
+ }
641
+ },
642
+ {
643
+ "rust": {
644
+ "counter": 1,
645
+ "cstring_hex": "286e756c6c2900",
646
+ "index": 0
647
+ },
648
+ "source": {
649
+ "counter": 1,
650
+ "cstring_hex": "286e756c6c2900",
651
+ "index": 0
652
+ }
653
+ },
654
+ {
655
+ "rust": {
656
+ "counter": 2,
657
+ "cstring_hex": "286e756c6c2900",
658
+ "index": 1
659
+ },
660
+ "source": {
661
+ "counter": 2,
662
+ "cstring_hex": "286e756c6c2900",
663
+ "index": 1
664
+ }
665
+ },
666
+ {
667
+ "rust": {
668
+ "counter": 0,
669
+ "cstring_hex": "286e756c6c2900",
670
+ "index": 1
671
+ },
672
+ "source": {
673
+ "counter": 0,
674
+ "cstring_hex": "286e756c6c2900",
675
+ "index": 1
676
+ }
677
+ }
678
+ ],
679
+ "source_report": "docs/research/qvm-format-wrappers.json",
680
+ "source_report_sha256": "ea796cc59eced38b448d46ad71a03331bb1ee799ea9ed0cfc8afe9ecfc739bd5"
681
+ }
docs/research/rust-health-impulse.json CHANGED
@@ -2985,7 +2985,7 @@
2985
  "exit_code": 2
2986
  }
2987
  ],
2988
- "rust_unit_tests": 74,
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": 76,
2989
  "source_report": "docs/research/qvm-health-impulse.json",
2990
  "source_report_sha256": "625fd8064004ea6dbdf398d51d0af844d9c635ff04c0a0790886dddb6ab81e71"
2991
  }
scripts/analyze_rust_format_wrappers.py ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ import hashlib,json
3
+ from pathlib import Path
4
+ from analyze_rust_qvm_format import encode
5
+ from rust_lifecycle_support import binary,result
6
+ from analyze_qvm import write_json
7
+
8
+ def main():
9
+ b=binary();p=Path('docs/research/qvm-format-wrappers.json');data=p.read_bytes();r=json.loads(data);rows=[];rings=[]
10
+ for s in r['bounded']:rows.append({'source':{'buffer_hex':s['buffer_hex'],'return_count':s['return_count'],'warning':bool(s['diagnostics'])},'rust':result(b,'qvm-format-bounded',[s['capacity'],*encode(s)])})
11
+ for s in r['rings']:rings.append({'source':{'cstring_hex':s['cstring_hex'],'index':int(s['pointer']!=35032232),'counter':s['after']},'rust':result(b,'qvm-format-ring',[s['before'],*encode(s)])})
12
+ write_json(Path('docs/research/rust-format-wrappers.json'),{'source_report':str(p),'source_report_sha256':hashlib.sha256(data).hexdigest(),'bounded':rows,'rings':rings,'limits':'Rust successful/clipping70386 and rotating70458 wrapper semantics match56 actual-QVM fixtures. Native strncpy bytes modeled; warnings represented as flags and fatal/error-side-effect behavior excluded.'})
13
+ if __name__=='__main__':main()
scripts/test_rust_format_wrappers.py ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ import hashlib,json,unittest
2
+ from pathlib import Path
3
+ class RustFormatWrapperTests(unittest.TestCase):
4
+ def test_all_original_wrapper_outputs(self):
5
+ r=json.loads(Path('docs/research/rust-format-wrappers.json').read_text());self.assertEqual((len(r['bounded']),len(r['rings'])),(35,21))
6
+ for p in r['bounded']+r['rings']:self.assertEqual(p['rust'],p['source'])
7
+ def test_source_identity(self):
8
+ r=json.loads(Path('docs/research/rust-format-wrappers.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
@@ -2025,3 +2025,16 @@ report scripts/analyze_qvm_format_wrappers.py docs/research/qvm-format-wrappers.
2025
  edge docs/research/qvm-format-wrappers.json scripts/test_qvm_format_wrappers.py
2026
  group format-wrappers scripts/analyze_qvm_format_wrappers.py
2027
  edge scripts/analyze_qvm_format_wrappers.py scripts/analyze_qvm_resumer_audit.py
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2025
  edge docs/research/qvm-format-wrappers.json scripts/test_qvm_format_wrappers.py
2026
  group format-wrappers scripts/analyze_qvm_format_wrappers.py
2027
  edge scripts/analyze_qvm_format_wrappers.py scripts/analyze_qvm_resumer_audit.py
2028
+ edge Cargo.toml scripts/analyze_rust_format_wrappers.py
2029
+ edge Cargo.lock scripts/analyze_rust_format_wrappers.py
2030
+ edge crates/urt-gameplay/Cargo.toml scripts/analyze_rust_format_wrappers.py
2031
+ edge crates/urt-gameplay/src/lib.rs scripts/analyze_rust_format_wrappers.py
2032
+ edge crates/urt-gameplay/src/bin/urt-lifecycle.rs scripts/analyze_rust_format_wrappers.py
2033
+ edge crates/urt-gameplay/src/qvm_format.rs scripts/analyze_rust_format_wrappers.py
2034
+ group gameplay scripts/analyze_rust_format_wrappers.py
2035
+ group rust-format-wrappers scripts/analyze_rust_qvm_format.py
2036
+ group rust-format-wrappers scripts/analyze_rust_health_impulse.py
2037
+ report scripts/analyze_rust_format_wrappers.py docs/research/rust-format-wrappers.json
2038
+ edge docs/research/rust-format-wrappers.json scripts/test_rust_format_wrappers.py
2039
+ group rust-format-wrappers scripts/analyze_rust_format_wrappers.py
2040
+ edge scripts/analyze_rust_format_wrappers.py scripts/analyze_qvm_resumer_audit.py