nmarshall commited on
Commit
cedc6d0
·
1 Parent(s): 59e33ea

gameplay: port temporary event construction

Browse files
crates/urt-gameplay/src/bin/urt-transition.rs CHANGED
@@ -176,6 +176,62 @@ fn run() -> Result<String, String> {
176
  map_clients_json(&c)
177
  ))
178
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
179
  _ => Err("unknown command".into()),
180
  }
181
  }
 
176
  map_clients_json(&c)
177
  ))
178
  }
179
+ "event" => {
180
+ if w.len() != 6 {
181
+ return Err("invalid event input".into());
182
+ }
183
+ let init = 0x13579bdf;
184
+ let mut e = EventRecord {
185
+ kind: init,
186
+ trajectory: [init; 3],
187
+ origin: [init; 3],
188
+ velocity: [init; 3],
189
+ caller_time: init,
190
+ owner: init,
191
+ field400: init,
192
+ flags: w[5],
193
+ current_origin: [init; 3],
194
+ class_token: init,
195
+ class_hash: init,
196
+ event_time: init as i32,
197
+ free_after_event: init,
198
+ };
199
+ let link = temporary_event(&mut e, w[..3].try_into().unwrap(), w[3], w[4] as i32)
200
+ .map_err(|e| format!("{e:?}"))?;
201
+ let f = object(
202
+ &[
203
+ 4, 12, 16, 20, 24, 28, 32, 36, 40, 44, 84, 140, 400, 424, 488, 492, 496, 536,
204
+ 540, 568, 572,
205
+ ],
206
+ &[
207
+ e.kind,
208
+ e.trajectory[0],
209
+ e.trajectory[1],
210
+ e.trajectory[2],
211
+ e.origin[0],
212
+ e.origin[1],
213
+ e.origin[2],
214
+ e.velocity[0],
215
+ e.velocity[1],
216
+ e.velocity[2],
217
+ e.caller_time,
218
+ e.owner,
219
+ e.field400,
220
+ e.flags,
221
+ e.current_origin[0],
222
+ e.current_origin[1],
223
+ e.current_origin[2],
224
+ e.class_token,
225
+ e.class_hash,
226
+ e.event_time as u32,
227
+ e.free_after_event,
228
+ ],
229
+ );
230
+ Ok(format!(
231
+ "{{\"fields\":{f},\"link\":{}}}",
232
+ link == EventIntent::Link
233
+ ))
234
+ }
235
  _ => Err("unknown command".into()),
236
  }
237
  }
crates/urt-gameplay/src/transition.rs CHANGED
@@ -371,3 +371,129 @@ mod normal_tests {
371
  assert_eq!(mark_connecting(&mut c, 1), Err(Error::InvalidCount));
372
  }
373
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
371
  assert_eq!(mark_connecting(&mut c, 1), Err(Error::InvalidCount));
372
  }
373
  }
374
+
375
+ /// Word-level source record. Class token97191 identifies the original tempEntity
376
+ /// string; runtime code must resolve metadata rather than use it as a native pointer.
377
+ #[derive(Clone, Debug, PartialEq, Eq)]
378
+ pub struct EventRecord {
379
+ pub kind: u32,
380
+ pub trajectory: [u32; 3],
381
+ pub origin: [u32; 3],
382
+ pub velocity: [u32; 3],
383
+ pub caller_time: u32,
384
+ pub owner: u32,
385
+ pub field400: u32,
386
+ pub flags: u32,
387
+ pub current_origin: [u32; 3],
388
+ pub class_token: u32,
389
+ pub class_hash: u32,
390
+ pub event_time: i32,
391
+ pub free_after_event: u32,
392
+ }
393
+ #[derive(Clone, Copy, Debug, PartialEq, Eq)]
394
+ pub enum EventIntent {
395
+ Link,
396
+ }
397
+ pub fn temporary_event(
398
+ record: &mut EventRecord,
399
+ origin: [u32; 3],
400
+ kind: u32,
401
+ now: i32,
402
+ ) -> Result<EventIntent, Error> {
403
+ let mut snap = [0; 3];
404
+ for j in 0..3 {
405
+ let x = f32::from_bits(origin[j]);
406
+ if !x.is_finite() || !(-2147483648.0..2147483648.0).contains(&x) {
407
+ return Err(Error::FloatDomain);
408
+ }
409
+ snap[j] = ((x as i32) as f32).to_bits();
410
+ }
411
+ record.kind = kind.wrapping_add(19);
412
+ record.class_token = 97191;
413
+ record.class_hash = 662184166;
414
+ record.event_time = now;
415
+ record.free_after_event = 1;
416
+ record.origin = snap;
417
+ record.current_origin = snap;
418
+ record.trajectory = [0; 3];
419
+ record.velocity = [0; 3];
420
+ Ok(EventIntent::Link)
421
+ }
422
+ #[cfg(test)]
423
+ mod event_tests {
424
+ use super::*;
425
+ fn allocated() -> EventRecord {
426
+ EventRecord {
427
+ kind: 7,
428
+ trajectory: [7; 3],
429
+ origin: [7; 3],
430
+ velocity: [7; 3],
431
+ caller_time: 42,
432
+ owner: 99,
433
+ field400: 13,
434
+ flags: 16,
435
+ current_origin: [7; 3],
436
+ class_token: 7,
437
+ class_hash: 7,
438
+ event_time: 7,
439
+ free_after_event: 7,
440
+ }
441
+ }
442
+ #[test]
443
+ fn snapped_origin_preserves_allocator_and_caller_fields() {
444
+ let mut e = allocated();
445
+ assert_eq!(
446
+ temporary_event(
447
+ &mut e,
448
+ [1.9f32.to_bits(), (-1.9f32).to_bits(), (-0.0f32).to_bits()],
449
+ u32::MAX,
450
+ i32::MIN
451
+ ),
452
+ Ok(EventIntent::Link)
453
+ );
454
+ assert_eq!(e.kind, 18);
455
+ assert_eq!(e.origin, [1.0f32.to_bits(), (-1.0f32).to_bits(), 0]);
456
+ assert_eq!(e.current_origin, e.origin);
457
+ assert_eq!(
458
+ (e.flags, e.caller_time, e.owner, e.field400),
459
+ (16, 42, 99, 13)
460
+ );
461
+ assert_eq!(
462
+ (
463
+ e.class_token,
464
+ e.class_hash,
465
+ e.event_time,
466
+ e.free_after_event
467
+ ),
468
+ (97191, 662184166, i32::MIN, 1)
469
+ );
470
+ }
471
+ #[test]
472
+ fn invalid_conversion_rejects_before_mutation() {
473
+ for v in [
474
+ f32::NAN,
475
+ f32::INFINITY,
476
+ f32::NEG_INFINITY,
477
+ 2147483648.0,
478
+ -2147483904.0,
479
+ ] {
480
+ let mut e = allocated();
481
+ let old = e.clone();
482
+ assert_eq!(
483
+ temporary_event(&mut e, [0, v.to_bits(), 0], 69, 1000),
484
+ Err(Error::FloatDomain)
485
+ );
486
+ assert_eq!(e, old);
487
+ }
488
+ let mut e = allocated();
489
+ assert!(
490
+ temporary_event(
491
+ &mut e,
492
+ [(-2147483648.0f32).to_bits(), 2147483520.0f32.to_bits(), 0],
493
+ 69,
494
+ 0
495
+ )
496
+ .is_ok()
497
+ );
498
+ }
499
+ }
docs/FINDINGS.md CHANGED
@@ -1556,3 +1556,6 @@ RE-813–822 replaces the scoreboard decimal assumption with actual37914 executi
1556
 
1557
 
1558
  **RE-852:** 144 original 277695/278138 cases recover tempEntity metadata, wrapped event kind+19, CVFI/CVIF origin snapping, trajectory/current-origin writes and link order. Five invalid float domains reject. Allocator flags424 and caller-owned84/140 are preserved; allocation277165 and native linking remain supplied/captured. [Evidence](research/QVM_EVENT_CONSTRUCT.md).
 
 
 
 
1556
 
1557
 
1558
  **RE-852:** 144 original 277695/278138 cases recover tempEntity metadata, wrapped event kind+19, CVFI/CVIF origin snapping, trajectory/current-origin writes and link order. Five invalid float domains reject. Allocator flags424 and caller-owned84/140 are preserved; allocation277165 and native linking remain supplied/captured. [Evidence](research/QVM_EVENT_CONSTRUCT.md).
1559
+
1560
+
1561
+ **RE-853:** Rust matches 144 original temporary-event cases, including exact snap words, event-kind wrapping, metadata and preservation of allocator/caller fields. Five invalid source domains reject atomically in Rust. Allocation and native linking remain caller-owned; source class tokens require runtime metadata resolution. [Evidence](research/RUST_EVENT_CONSTRUCT.md).
docs/PROVISIONAL_TODO.md CHANGED
@@ -1547,7 +1547,7 @@ RE-842: 150 continuous 7805 fixtures now execute actual tie, readiness, begin-tr
1547
  RE-843: Rust composes recovered tie, readiness, begin and exit-log policies with prior exit timers/limits across150 continuous source fixtures. Atomic staged updates preserve shared pending/client state; reset and lower-level world/I/O effects remain explicit intentions. Source branch labels are independently classified by the tested composition oracle; captured requests and actual final source state supply effect parity. Full runtime completion remains open. [Evidence](research/RUST_INTERMISSION_FLOW.md).
1548
 
1549
 
1550
- - [ ] RE-844: Recover next-map/respawn/camera/publication/event allocation effects, broaden pointer/identity fixtures, and integrate ranking→intermission→scoreboard runtime traces. [Scope](research/QVM_INTERMISSION_BATCH.md).
1551
 
1552
 
1553
  RE-844: 72 original 6575 fixtures recover entity/player origin and view-angle copies, velocity clear, movement type5 and cleared entity/client fields. Coherent byte copies preserve arbitrary float words, including NaN payloads; two entity-to-client targets prove indirection. Mode/field4324 gates converge without extra effects. Native renderer/camera integration remains open. Full runtime completion remains open. [Evidence](research/QVM_CAMERA_PLACE.md).
@@ -1575,3 +1575,9 @@ RE-851: Rust matches 48 original normal-reset cases with separate pre-map score
1575
 
1576
 
1577
  RE-852: 144 original 277695/278138 cases recover tempEntity metadata, wrapped event kind+19, CVFI/CVIF origin snapping, trajectory/current-origin writes and link order. Five invalid float domains reject. Allocator flags424 and caller-owned84/140 are preserved; allocation277165 and native linking remain supplied/captured. Full runtime completion remains open. [Evidence](research/QVM_EVENT_CONSTRUCT.md).
 
 
 
 
 
 
 
1547
  RE-843: Rust composes recovered tie, readiness, begin and exit-log policies with prior exit timers/limits across150 continuous source fixtures. Atomic staged updates preserve shared pending/client state; reset and lower-level world/I/O effects remain explicit intentions. Source branch labels are independently classified by the tested composition oracle; captured requests and actual final source state supply effect parity. Full runtime completion remains open. [Evidence](research/RUST_INTERMISSION_FLOW.md).
1548
 
1549
 
1550
+ - [x] RE-844–853: Recover bounded camera, map-reset and event-construction policies; deeper native/runtime work continues under RE-854. [Batch evidence](research/QVM_CAMERA_MAP_EVENT_BATCH.md).
1551
 
1552
 
1553
  RE-844: 72 original 6575 fixtures recover entity/player origin and view-angle copies, velocity clear, movement type5 and cleared entity/client fields. Coherent byte copies preserve arbitrary float words, including NaN payloads; two entity-to-client targets prove indirection. Mode/field4324 gates converge without extra effects. Native renderer/camera integration remains open. Full runtime completion remains open. [Evidence](research/QVM_CAMERA_PLACE.md).
 
1575
 
1576
 
1577
  RE-852: 144 original 277695/278138 cases recover tempEntity metadata, wrapped event kind+19, CVFI/CVIF origin snapping, trajectory/current-origin writes and link order. Five invalid float domains reject. Allocator flags424 and caller-owned84/140 are preserved; allocation277165 and native linking remain supplied/captured. Full runtime completion remains open. [Evidence](research/QVM_EVENT_CONSTRUCT.md).
1578
+
1579
+
1580
+ RE-853: Rust matches 144 original temporary-event cases, including exact snap words, event-kind wrapping, metadata and preservation of allocator/caller fields. Five invalid source domains reject atomically in Rust. Allocation and native linking remain caller-owned; source class tokens require runtime metadata resolution. Full runtime completion remains open. [Evidence](research/RUST_EVENT_CONSTRUCT.md).
1581
+
1582
+
1583
+ - [ ] RE-854: Compose recovered camera/map/event policies into intermission flows; recover searches, angles, commands, allocation, respawn and publication; validate actual macOS/Linux runtime traces. [Scope](research/QVM_CAMERA_MAP_EVENT_BATCH.md).
docs/RESEARCH_LEDGER.md CHANGED
@@ -1938,3 +1938,12 @@ Rust matches 48 original normal-reset cases with separate pre-map score reset an
1938
  ## 2026-10-27 — RE-852 Original temporary event construction and origin snapping
1939
 
1940
  144 original 277695/278138 cases recover tempEntity metadata, wrapped event kind+19, CVFI/CVIF origin snapping, trajectory/current-origin writes and link order. Five invalid float domains reject. Allocator flags424 and caller-owned84/140 are preserved; allocation277165 and native linking remain supplied/captured. Focused `cargo xtask test-group event-construct` 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.
 
 
 
 
 
 
 
 
 
 
1938
  ## 2026-10-27 — RE-852 Original temporary event construction and origin snapping
1939
 
1940
  144 original 277695/278138 cases recover tempEntity metadata, wrapped event kind+19, CVFI/CVIF origin snapping, trajectory/current-origin writes and link order. Five invalid float domains reject. Allocator flags424 and caller-owned84/140 are preserved; allocation277165 and native linking remain supplied/captured. Focused `cargo xtask test-group event-construct` 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.
1941
+
1942
+
1943
+ ## 2026-10-27 — RE-853 Rust temporary event construction and ownership boundaries
1944
+
1945
+ Rust matches 144 original temporary-event cases, including exact snap words, event-kind wrapping, metadata and preservation of allocator/caller fields. Five invalid source domains reject atomically in Rust. Allocation and native linking remain caller-owned; source class tokens require runtime metadata resolution. Focused `cargo xtask test-group rust-event-construct` passes (9 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.
1946
+
1947
+ RE-853 final review: targeted xtask registry regression passes for all five source-report parity edges, transition module selection and the shared intermission mode-classifier dependency. Added batch evidence, validation cadence and RE-854 integration/native follow-ups.
1948
+
1949
+ Batch RE-844–853 boundary: the single `cargo xtask test-full --since 15d7fcac` run passed after commit ten: 1,668 Python tests, 147 Rust tests (assets 10, gameplay 109, xtask 28), 46 deterministic report producers, storage policy, 414 documentation files and whitespace checks. No generated report drift; validation notes amended into task ten. Original assets unchanged.
docs/REVERSE_ENGINEERING_TODO.md CHANGED
@@ -1584,7 +1584,7 @@ Purpose: turn the supplied installation into an evidence-backed specification of
1584
  - [x] RE-843 Rust composed exit and intermission state machine ([evidence](research/RUST_INTERMISSION_FLOW.md)).
1585
 
1586
 
1587
- - [ ] RE-844: Recover next-map/respawn/camera/publication/event allocation effects, broaden pointer/identity fixtures, and integrate ranking→intermission→scoreboard runtime traces. [Scope](research/QVM_INTERMISSION_BATCH.md).
1588
 
1589
 
1590
  - [x] RE-844 Original intermission camera placement and client indirection ([evidence](research/QVM_CAMERA_PLACE.md)).
@@ -1612,3 +1612,9 @@ Purpose: turn the supplied installation into an evidence-backed specification of
1612
 
1613
 
1614
  - [x] RE-852 Original temporary event construction and origin snapping ([evidence](research/QVM_EVENT_CONSTRUCT.md)).
 
 
 
 
 
 
 
1584
  - [x] RE-843 Rust composed exit and intermission state machine ([evidence](research/RUST_INTERMISSION_FLOW.md)).
1585
 
1586
 
1587
+ - [x] RE-844–853: Recover bounded camera, map-reset and event-construction policies; deeper native/runtime work continues under RE-854. [Batch evidence](research/QVM_CAMERA_MAP_EVENT_BATCH.md).
1588
 
1589
 
1590
  - [x] RE-844 Original intermission camera placement and client indirection ([evidence](research/QVM_CAMERA_PLACE.md)).
 
1612
 
1613
 
1614
  - [x] RE-852 Original temporary event construction and origin snapping ([evidence](research/QVM_EVENT_CONSTRUCT.md)).
1615
+
1616
+
1617
+ - [x] RE-853 Rust temporary event construction and ownership boundaries ([evidence](research/RUST_EVENT_CONSTRUCT.md)).
1618
+
1619
+
1620
+ - [ ] RE-854: Compose recovered camera/map/event policies into intermission flows; recover searches, angles, commands, allocation, respawn and publication; validate actual macOS/Linux runtime traces. [Scope](research/QVM_CAMERA_MAP_EVENT_BATCH.md).
docs/TESTING.md CHANGED
@@ -138,3 +138,8 @@ Use `cargo xtask test-group <name>` with `format-float`, `rust-format-float`, `e
138
  ## Exit predicate and intermission groups
139
 
140
  Focused source/Rust pairs: `exit-predicate`/`rust-exit-predicate`, `intermission-ready`/`rust-intermission-ready`, `intermission-begin`/`rust-intermission-begin`, `exit-log`/`rust-exit-log`, and `intermission-flow`/`rust-intermission-flow`. Run `cargo xtask test-group <name>` before the task commit. The scoped intermission executable has its own adapter inputs; common module changes select every intermission consumer and scoped gameplay test-count metadata. The registry regression verifies source-report edges and module selection. Run one `test-full --since <batch-base>` after ten commits. See the [batch contracts and boundaries](research/QVM_INTERMISSION_BATCH.md).
 
 
 
 
 
 
138
  ## Exit predicate and intermission groups
139
 
140
  Focused source/Rust pairs: `exit-predicate`/`rust-exit-predicate`, `intermission-ready`/`rust-intermission-ready`, `intermission-begin`/`rust-intermission-begin`, `exit-log`/`rust-exit-log`, and `intermission-flow`/`rust-intermission-flow`. Run `cargo xtask test-group <name>` before the task commit. The scoped intermission executable has its own adapter inputs; common module changes select every intermission consumer and scoped gameplay test-count metadata. The registry regression verifies source-report edges and module selection. Run one `test-full --since <batch-base>` after ten commits. See the [batch contracts and boundaries](research/QVM_INTERMISSION_BATCH.md).
141
+
142
+
143
+ ## Camera, map and event groups
144
+
145
+ Focused source/Rust pairs: `camera-place`/`rust-camera-place`, `camera-select`/`rust-camera-select`, `nextmap-series`/`rust-nextmap-series`, `nextmap-reset`/`rust-nextmap-reset`, and `event-construct`/`rust-event-construct`. Invoke `cargo xtask test-group <name>` before each task commit. The scoped transition adapter covers each word-level contract; Rust groups also run scoped gameplay tests and refresh their test-count metadata. The registry regression verifies source-report edges, transition module consumers and the shared mode-classifier dependency. Continue one full suite after ten commits. [Batch contracts and boundaries](research/QVM_CAMERA_MAP_EVENT_BATCH.md).
docs/research/QVM_CAMERA_MAP_EVENT_BATCH.md ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Camera, next-map and event construction: RE-844–853
2
+
3
+ Five original source contracts now have matching Rust ports. This batch recovers concrete world-record mutations and ordered intentions; complete runtime, renderer and native side-effect parity remain open.
4
+
5
+ | Task | Evidence |
6
+ | --- | --- |
7
+ | RE-844 | [6575 intermission camera placement](QVM_CAMERA_PLACE.md) |
8
+ | RE-845 | [Rust camera placement](RUST_CAMERA_PLACE.md) |
9
+ | RE-846 | [6717 camera selection and target direction](QVM_CAMERA_SELECT.md) |
10
+ | RE-847 | [Rust view selection](RUST_CAMERA_SELECT.md) |
11
+ | RE-848 | [6939 team-series branch](QVM_NEXTMAP_SERIES.md) |
12
+ | RE-849 | [Rust series policy](RUST_NEXTMAP_SERIES.md) |
13
+ | RE-850 | [6939 normal reset suffix](QVM_NEXTMAP_RESET.md) |
14
+ | RE-851 | [Rust staged normal reset](RUST_NEXTMAP_RESET.md) |
15
+ | RE-852 | [277695 temporary event and 278138 origin setter](QVM_EVENT_CONSTRUCT.md) |
16
+ | RE-853 | [Rust temporary event construction](RUST_EVENT_CONSTRUCT.md) |
17
+
18
+ ## Findings and evidence limits
19
+
20
+ Camera placement has 72 coherent byte/word fixtures. It copies the camera position into entity and resolved-player origins, copies view angles, zeros velocity, sets movement type 5 and clears the observed entity/player fields. Both apparent mode/field 4324 gates reach the same body. Raw NaN payloads, infinities and signed zero copy exactly. Two distinct entity-to-client pointers exercise indirection; the Rust API accepts a resolved mutable client. This does not establish arbitrary pointer alias behavior or renderer integration.
21
+
22
+ View selection has 36 fixtures covering absent selection, absent target key, failed target lookup and found target. Original source copies selected or supplied-fallback view vectors and computes f32 target-minus-origin. Rust returns ordered search/fallback/aim intentions and exact finite direction words. The selector results and fallback 168114 writes are supplied. The original 62944 angle converter is captured without modifying angles, so this is not complete aiming or camera setup.
23
+
24
+ The series branch has 300 cases. It handles only positive phase and team-classified modes. A nonzero option requests restart unless both flag bits 2 and 4 are set. Otherwise it saves second then first score, clears active scores/pending/blocked/phase, removes those flags, and requests formatted configstrings 1005 and 2 plus downstream work. Actual 33715 mode classification executes. The original format strings are `%d` and `1 %i %i`; 70458 output is supplied as distinct pointers. The normal branch stops at 7070 in this report.
25
+
26
+ The normal suffix has 48 cases. After captured 122230/7188 calls, it clears round/blocked/global scores and connected-client scores; mode 9 additionally clears fields 304/308. A snapshot at captured 249007 proves those changes happen before the second loop changes connected state 2 to 1. Pending, phase, series flags and saved scores remain untouched. Rust exposes separate preparation and post-map connection APIs so real map-child mutations can occur between stages. Fixture parity assumes those captured children do not mutate state.
27
+
28
+ The temporary-event constructor has 144 valid cases and five invalid float-domain rejections. Actual 277695 and 278138 execute against a supplied allocator record. Origin components truncate through CVFI/CVIF, normalizing negative zero, and feed both trajectory base and current origin. The constructor zeros trajectory time/type/duration and velocity, writes wrapped kind + 19, the `tempEntity` class token/hash, clock 568 and free flag 572, then requests syscall−31. It preserves allocator flags 424 and caller-owned fields 84/140. This does not prove the real allocator supplies flags 16. Rust validates conversion before changing its record, then returns a link intention; invalid-domain atomic rejection is a deliberate safe boundary. Original class token 97191 is source metadata, not a portable native pointer.
29
+
30
+ ## Validation
31
+
32
+ Each task used its focused cargo xtask group before its own commit; affected reports regenerated twice and relevant Python plus scoped gameplay Rust tests passed. Storage, documentation links and whitespace guards ran throughout. The registry regression verifies all five source-report-to-Rust edges, transition module selection and the shared intermission mode-classifier dependency. The single full-suite run after commit ten, `cargo xtask test-full --since 15d7fcac`, passed: 1,668 Python tests and 147 Rust tests (assets 10, gameplay 109, xtask 28). All 46 affected report producers regenerated twice deterministically. Storage policy, links across 414 documentation files and whitespace checks passed. No generated reports changed. Validation notes were amended into task ten.
33
+
34
+ Use `cargo xtask test-group <name>` with `camera-place`, `rust-camera-place`, `camera-select`, `rust-camera-select`, `nextmap-series`, `rust-nextmap-series`, `nextmap-reset`, `rust-nextmap-reset`, `event-construct` or `rust-event-construct`.
35
+
36
+ ## Next work: RE-854
37
+
38
+ - [ ] Recover actual 276433/276586 searches, 168114 fallback selection and 62944 direction-to-angle conversion.
39
+ - [ ] Compose 6717 and 6575 into begin-intermission source/Rust flows, including client-pointer alias fixtures and actual camera/respawn state ownership.
40
+ - [ ] Recover 122230/7188/249007 and 256248/256651 command/map effects, then compose complete 6939 into readiness and runtime handling.
41
+ - [ ] Recover 277165 allocation/initialization, resolve class metadata portably and integrate 277695 with mode 9 caller fields and real linking/event lifetime.
42
+ - [ ] Recover respawn 175123 and slow setup 156523, and execute publication 6328 with verified formatter/configstring/network ownership.
43
+ - [ ] Integrate ranking→exit/intermission→camera/map/event→scoreboard state, compare real game/native traces, and continue legacy trace audits.
44
+ - [ ] Validate actual macOS/Linux Bevy/Solari gameplay and rendering. These bounded Rust policies do not establish full-game completion.
docs/research/README.md CHANGED
@@ -1150,3 +1150,8 @@ The extension collector adds five focused tests for native tail calls, command-r
1150
 
1151
 
1152
  [RE-852: Original temporary event construction and origin snapping](QVM_EVENT_CONSTRUCT.md).
 
 
 
 
 
 
1150
 
1151
 
1152
  [RE-852: Original temporary event construction and origin snapping](QVM_EVENT_CONSTRUCT.md).
1153
+
1154
+
1155
+ [RE-853: Rust temporary event construction and ownership boundaries](RUST_EVENT_CONSTRUCT.md).
1156
+
1157
+ - [RE-844–853 camera, map and event batch evidence and next work](QVM_CAMERA_MAP_EVENT_BATCH.md).
docs/research/RUST_EVENT_CONSTRUCT.md ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ # Rust temporary event construction and ownership boundaries
2
+
3
+ RE-853. Reproduce with `python3 scripts/analyze_rust_event_construct.py`; focused validation: `cargo xtask test-group rust-event-construct`. [Machine report](rust-event-construct.json).
4
+
5
+ Rust matches 144 original temporary-event cases, including exact snap words, event-kind wrapping, metadata and preservation of allocator/caller fields. Five invalid source domains reject atomically in Rust. Allocation and native linking remain caller-owned; source class tokens require runtime metadata resolution.
6
+
7
+ Original executable/native runtime and complete Linux/macOS Rust/Bevy/Solari acceptance remain unverified.
docs/research/rust-event-construct.json ADDED
The diff for this file is too large to render. See raw diff
 
docs/research/rust-health-impulse.json CHANGED
@@ -2985,7 +2985,7 @@
2985
  "exit_code": 2
2986
  }
2987
  ],
2988
- "rust_unit_tests": 107,
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": 109,
2989
  "source_report": "docs/research/qvm-health-impulse.json",
2990
  "source_report_sha256": "625fd8064004ea6dbdf398d51d0af844d9c635ff04c0a0790886dddb6ab81e71"
2991
  }
scripts/analyze_rust_event_construct.py ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ import hashlib,json
3
+ from pathlib import Path
4
+ from rust_transition_support import binary,result,invoke
5
+ from analyze_qvm import write_json
6
+ def encode(s):
7
+ a=s['input'];return [*a['origin'],a['kind'],a['now'],a['flags']]
8
+ def project(s):return {'fields':s['fields'],'link':s['requests'][-1]['target']==-31}
9
+ def main():
10
+ b=binary();p=Path('docs/research/qvm-event-construct.json');data=p.read_bytes();rows=[]
11
+ for s in json.loads(data)['probes']:rows.append({'source':project(s),'rust':result(b,'event',encode(s))})
12
+ rejected=[]
13
+ for bad in json.loads(data)['rejections']:
14
+ r=invoke(b,'event',[*bad['origin'],69,1000,16],check=False);assert r.returncode!=0;rejected.append({'origin':bad['origin'],'exit_code':r.returncode,'error':r.stderr.strip()})
15
+ write_json(Path('docs/research/rust-event-construct.json'),{'source_report':str(p),'source_report_sha256':hashlib.sha256(data).hexdigest(),'probes':rows,'rejections':rejected,'limits':'Rust matches 144 original temporary-event cases, including exact snap words, event-kind wrapping, metadata and preservation of allocator/caller fields. Five invalid source domains reject atomically in Rust. Allocation and native linking remain caller-owned; source class tokens require runtime metadata resolution.'})
16
+ if __name__=='__main__':main()
scripts/test_rust_event_construct.py ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import hashlib,json,unittest
2
+ from pathlib import Path
3
+ class PortTests(unittest.TestCase):
4
+ def test_all_original_cases(self):
5
+ r=json.loads(Path('docs/research/rust-event-construct.json').read_text());src=json.loads(Path(r['source_report']).read_text());self.assertEqual(len(r['probes']),len(src['probes']));self.assertGreater(len(r['probes']),20)
6
+ for p in r['probes']:self.assertEqual(p['rust'],p['source'])
7
+ self.assertEqual(len(r['rejections']),5)
8
+ for p in r['rejections']:self.assertNotEqual(p['exit_code'],0);self.assertEqual(p['error'],'FloatDomain')
9
+ def test_source_identity(self):
10
+ r=json.loads(Path('docs/research/rust-event-construct.json').read_text());self.assertEqual(hashlib.sha256(Path(r['source_report']).read_bytes()).hexdigest(),r['source_report_sha256'])
11
+ if __name__=='__main__':unittest.main()
xtask/checks.tsv CHANGED
@@ -2320,3 +2320,16 @@ report scripts/analyze_qvm_event_construct.py docs/research/qvm-event-construct.
2320
  edge docs/research/qvm-event-construct.json scripts/test_qvm_event_construct.py
2321
  group event-construct scripts/analyze_qvm_event_construct.py
2322
  edge scripts/analyze_qvm_event_construct.py scripts/analyze_qvm_resumer_audit.py
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2320
  edge docs/research/qvm-event-construct.json scripts/test_qvm_event_construct.py
2321
  group event-construct scripts/analyze_qvm_event_construct.py
2322
  edge scripts/analyze_qvm_event_construct.py scripts/analyze_qvm_resumer_audit.py
2323
+ edge Cargo.toml scripts/analyze_rust_event_construct.py
2324
+ edge Cargo.lock scripts/analyze_rust_event_construct.py
2325
+ edge crates/urt-gameplay/Cargo.toml scripts/analyze_rust_event_construct.py
2326
+ edge crates/urt-gameplay/src/lib.rs scripts/analyze_rust_event_construct.py
2327
+ edge crates/urt-gameplay/src/transition.rs scripts/analyze_rust_event_construct.py
2328
+ edge crates/urt-gameplay/src/bin/urt-transition.rs scripts/analyze_rust_event_construct.py
2329
+ edge docs/research/qvm-event-construct.json scripts/analyze_rust_event_construct.py
2330
+ group rust-event-construct scripts/analyze_rust_health_impulse.py
2331
+ group gameplay scripts/analyze_rust_event_construct.py
2332
+ report scripts/analyze_rust_event_construct.py docs/research/rust-event-construct.json
2333
+ edge docs/research/rust-event-construct.json scripts/test_rust_event_construct.py
2334
+ group rust-event-construct scripts/analyze_rust_event_construct.py
2335
+ edge scripts/analyze_rust_event_construct.py scripts/analyze_qvm_resumer_audit.py
xtask/src/lib.rs CHANGED
@@ -1049,6 +1049,8 @@ mod integration_tests {
1049
  ("exit_prefix", "exit_prefix"),
1050
  ("exit_rules", "exit_class"),
1051
  ("intermission", "intermission_flow"),
 
 
1052
  ("leaderboard", "leaderboard"),
1053
  ("recipient_wire", "recipient_wire"),
1054
  ] {
@@ -1109,6 +1111,11 @@ mod integration_tests {
1109
  ("qvm-intermission-begin", "intermission_begin"),
1110
  ("qvm-exit-log", "exit_log"),
1111
  ("qvm-intermission-flow", "intermission_flow"),
 
 
 
 
 
1112
  ] {
1113
  let selected = plan(
1114
  &g,
 
1049
  ("exit_prefix", "exit_prefix"),
1050
  ("exit_rules", "exit_class"),
1051
  ("intermission", "intermission_flow"),
1052
+ ("intermission", "nextmap_series"),
1053
+ ("transition", "event_construct"),
1054
  ("leaderboard", "leaderboard"),
1055
  ("recipient_wire", "recipient_wire"),
1056
  ] {
 
1111
  ("qvm-intermission-begin", "intermission_begin"),
1112
  ("qvm-exit-log", "exit_log"),
1113
  ("qvm-intermission-flow", "intermission_flow"),
1114
+ ("qvm-camera-place", "camera_place"),
1115
+ ("qvm-camera-select", "camera_select"),
1116
+ ("qvm-nextmap-series", "nextmap_series"),
1117
+ ("qvm-nextmap-reset", "nextmap_reset"),
1118
+ ("qvm-event-construct", "event_construct"),
1119
  ] {
1120
  let selected = plan(
1121
  &g,