nmarshall commited on
Commit
ab95057
·
1 Parent(s): 40e833f

qvm: animation contacts

Browse files
docs/ASSET_PIPELINE_TODO.md CHANGED
@@ -264,3 +264,5 @@ Research progress: [QVM friction policies](research/QVM_FRICTION.md) completes b
264
  Research progress: [QVM content/ground producers](research/QVM_GROUND.md) completes bounded RE-437–439. RE-436/440 retain native callbacks/helper effects/full collision-state composition/live policy/runtime/platform work; broad game/rendering milestones remain open.
265
 
266
  Research progress: [QVM posture bounds and transitions](research/QVM_POSTURE.md) completes bounded RE-441–443. RE-440/444 retain native collision/callee effects/resource and semantic producers/full state composition/runtime/platform work; broad game/rendering milestones remain open.
 
 
 
264
  Research progress: [QVM content/ground producers](research/QVM_GROUND.md) completes bounded RE-437–439. RE-436/440 retain native callbacks/helper effects/full collision-state composition/live policy/runtime/platform work; broad game/rendering milestones remain open.
265
 
266
  Research progress: [QVM posture bounds and transitions](research/QVM_POSTURE.md) completes bounded RE-441–443. RE-440/444 retain native collision/callee effects/resource and semantic producers/full state composition/runtime/platform work; broad game/rendering milestones remain open.
267
+
268
+ Research progress: [QVM animation helpers and contacts](research/QVM_ANIMATION_CONTACTS.md) completes bounded RE-445–447. RE-444/448 retain native collision/resource/semantic producers, live animation tables/contact consumers/full state/runtime/platform work; broad game/rendering milestones remain open.
docs/FINDINGS.md CHANGED
@@ -522,3 +522,9 @@ Bounded qagame paths recover bomb-plant/inactivity correction requests, direct f
522
  **F-191 — Posture transitions:** guarded entry can deduct3000 from a converted resource word, request39525(36) and set timer200; exit requests a standing overlap trace, then clears bit1/field92 and requests39525(37) only for supplied zero allsolid. Final entity bit1 depends on horizontal speed>=300 and signed up<=-10. Native/callee effects remain omitted. [POSTURE-002](research/QVM_POSTURE.md).
523
 
524
  **F-192 — Posture consumer connection:**48 fixtures propagate evaluated height/bounds/flags/resource into earlier content/ground/scale slices. Resource deduction can change the prior220/340 policy under matched supplied inputs; full state/collision/runtime composition remains open. [POSTURE-003](research/QVM_POSTURE.md).
 
 
 
 
 
 
 
522
  **F-191 — Posture transitions:** guarded entry can deduct3000 from a converted resource word, request39525(36) and set timer200; exit requests a standing overlap trace, then clears bit1/field92 and requests39525(37) only for supplied zero allsolid. Final entity bit1 depends on horizontal speed>=300 and signed up<=-10. Native/callee effects remain omitted. [POSTURE-002](research/QVM_POSTURE.md).
523
 
524
  **F-192 — Posture consumer connection:**48 fixtures propagate evaluated height/bounds/flags/resource into earlier content/ground/scale slices. Resource deduction can change the prior220/340 policy under matched supplied inputs; full state/collision/runtime composition remains open. [POSTURE-003](research/QVM_POSTURE.md).
525
+
526
+ **F-193 — Animation setters and continuation:** direct helpers39338/39383 toggle bit128 and OR the raw request behind signed type/timer guards; only the leg setter rejects raw equality. Continuation wrappers also reject masked-old equality. Child effects are evaluated; semantic/legal/runtime domains remain open. [ANIM-001](research/QVM_ANIMATION_CONTACTS.md).
527
+
528
+ **F-194 — Forced and timed animation ordering:**39525 clears the leg timer before its guarded child unless raw animation equals the request;39552 clears torso timer, calls its child, then assigns a supplied table word at base35018540+selector*2560+request*32+28 even when the child rejects. Ten posture bridges apply selected child effects and caller timer200 stores. Actual tables/playback/full state remain open. [ANIM-002](research/QVM_ANIMATION_CONTACTS.md).
529
+
530
+ **F-195 — Contact registration:**39253 ignores entity1022 and count==32, scans for duplicates, then appends and increments the frame count. Bounded fixtures preserve every untouched slot and accept1023/synthetic-1 on the append path; real contact generation, invalid counts and list consumers remain open. [CONTACT-001](research/QVM_ANIMATION_CONTACTS.md).
docs/PROVISIONAL_TODO.md CHANGED
@@ -576,3 +576,5 @@ Research progress: [QVM friction policies](research/QVM_FRICTION.md) completes b
576
  Research progress: [QVM content/ground producers](research/QVM_GROUND.md) completes bounded RE-437–439. RE-436/440 retain native callbacks/helper effects/full collision-state composition/live policy/runtime/platform work; broad game/rendering milestones remain open.
577
 
578
  Research progress: [QVM posture bounds and transitions](research/QVM_POSTURE.md) completes bounded RE-441–443. RE-440/444 retain native collision/callee effects/resource and semantic producers/full state composition/runtime/platform work; broad game/rendering milestones remain open.
 
 
 
576
  Research progress: [QVM content/ground producers](research/QVM_GROUND.md) completes bounded RE-437–439. RE-436/440 retain native callbacks/helper effects/full collision-state composition/live policy/runtime/platform work; broad game/rendering milestones remain open.
577
 
578
  Research progress: [QVM posture bounds and transitions](research/QVM_POSTURE.md) completes bounded RE-441–443. RE-440/444 retain native collision/callee effects/resource and semantic producers/full state composition/runtime/platform work; broad game/rendering milestones remain open.
579
+
580
+ Research progress: [QVM animation helpers and contacts](research/QVM_ANIMATION_CONTACTS.md) completes bounded RE-445–447. RE-444/448 retain native collision/resource/semantic producers, live animation tables/contact consumers/full state/runtime/platform work; broad game/rendering milestones remain open.
docs/RESEARCH_LEDGER.md CHANGED
@@ -327,3 +327,7 @@ Completed bounded RE-437–439 with `analyze_qvm_ground.py`: verified shipped qa
327
  ## 2026-10-15 — Game-QVM posture bounds, transitions and consumer inputs
328
 
329
  Completed bounded RE-441–443 with `analyze_qvm_posture.py`: verified shipped qagame helper47422, 420 posture probes and48 scalar/bounds bridges. POSTURE-001–003/F-190–192 recover initialized bounds, early-mode height, final flag/animation-dependent height; entry/exit guards, converted resource deduction/clamp, timer stores, tentative standing overlap query and recorded animation requests; and horizontal speed/up-byte entity-bit policy. Evaluated bounds/height/flags/resource transfer into earlier supplied-content/ground-trace/command-scale fixtures. Native trace and sqrt returns are supplied, helper39525 effects omitted, and no complete QVM/original executable/native collision/physics executes. Semantic names, legal domains, resource producers and full state composition remain open. Ground analyzer adds optional explicit bounds with unchanged default report. Eight independent tests verify all transitions, query bounds/arguments, early exclusions, resource/animation policy, equality boundaries, consumer handoffs with prior ground/scale oracles, provenance and scope. All495 tests pass. The direct ground report regenerates byte-identically; the posture report reproduces twice byte-identically (749071 bytes). Storage/filter setup, documentation links and whitespace checks pass. RE-440 remains open; RE-444 tracks overlap-trace ABI/results, animation-helper effects, resource/semantic producers and complete frame/PS/content/ground/friction/gameplay/macOS/Linux/Rust/Bevy/Solari validation; packet/entity/player/snapshot/rendering milestones remain open.
 
 
 
 
 
327
  ## 2026-10-15 — Game-QVM posture bounds, transitions and consumer inputs
328
 
329
  Completed bounded RE-441–443 with `analyze_qvm_posture.py`: verified shipped qagame helper47422, 420 posture probes and48 scalar/bounds bridges. POSTURE-001–003/F-190–192 recover initialized bounds, early-mode height, final flag/animation-dependent height; entry/exit guards, converted resource deduction/clamp, timer stores, tentative standing overlap query and recorded animation requests; and horizontal speed/up-byte entity-bit policy. Evaluated bounds/height/flags/resource transfer into earlier supplied-content/ground-trace/command-scale fixtures. Native trace and sqrt returns are supplied, helper39525 effects omitted, and no complete QVM/original executable/native collision/physics executes. Semantic names, legal domains, resource producers and full state composition remain open. Ground analyzer adds optional explicit bounds with unchanged default report. Eight independent tests verify all transitions, query bounds/arguments, early exclusions, resource/animation policy, equality boundaries, consumer handoffs with prior ground/scale oracles, provenance and scope. All495 tests pass. The direct ground report regenerates byte-identically; the posture report reproduces twice byte-identically (749071 bytes). Storage/filter setup, documentation links and whitespace checks pass. RE-440 remains open; RE-444 tracks overlap-trace ABI/results, animation-helper effects, resource/semantic producers and complete frame/PS/content/ground/friction/gameplay/macOS/Linux/Rust/Bevy/Solari validation; packet/entity/player/snapshot/rendering milestones remain open.
330
+
331
+ ## 2026-10-15 — Game-QVM animation helper effects and contact registration
332
+
333
+ Completed bounded RE-445–447 with `analyze_qvm_animation_contacts.py`: verified seven shipped qagame helper partitions,576 animation probes,30 contact-list probes and ten posture request bridges. ANIM-001–002/CONTACT-001/F-193–195 recover direct leg/torso type/timer guards, raw equality asymmetry, bit128 toggle/OR and continuation masks; forced timer-reset-before-child ordering, timed torso table address arithmetic/final store; and contact sentinel1022, count==32 capacity, signed duplicate scan and append/count effects. Known animation children39338/39383 execute as bounded synthetic instruction slices with separate local frames; selected posture bridges evaluate39525 requests and caller timer200 stores. Actual animation table words are supplied, with selectors/duration/request legality and live table producers unresolved. Contact fixtures cover counts0–32 only; invalid counts, native contact generation and entity/list-consumer effects remain open. No complete QVM/original executable/native collision/rendering executes. The shared evaluator adds signed32 BXOR without changing existing opcode paths. Eight independent tests check all policies/mutations/nested calls, equality/toggle/reset asymmetries, timed address/unconditional store, contacts/canaries, selected posture compositions, signed/high-bit XOR/local isolation, provenance/complete partitions and scope. All503 tests pass. Direct acceleration/ground/posture reports regenerate byte-identically; the new report reproduces twice byte-identically (606122 bytes). Storage/filter setup, documentation links and whitespace checks pass. RE-444 remains open; RE-448 tracks live animation tables/durations/selector-request domains, semantic/type/timer producers, contact reset/consumers/entity/invalid-count/alias behavior, full movement state composition and gameplay/macOS/Linux/Rust/Bevy/Solari compatibility; packet/entity/player/snapshot/rendering milestones remain open.
docs/REVERSE_ENGINEERING_TODO.md CHANGED
@@ -537,3 +537,8 @@ Purpose: turn the supplied installation into an evidence-backed specification of
537
  - [x] RE-442 Recover crouch entry/exit guards, resource/timer stores, tentative stand-up query and horizontal-speed/up-bit policy with supplied callback returns ([POSTURE-002](research/QVM_POSTURE.md)).
538
  - [x] RE-443 Bridge evaluated bounds/height/flags/resource into prior content/ground/command-scale fixtures with independent consumer oracles ([POSTURE-003](research/QVM_POSTURE.md)).
539
  - [ ] RE-444 Recover native overlap-trace ABI/results, helper39525 effects, resource producers/reset/ranges and posture/type/animation/flag/height meanings; compose complete frame/PS/content/ground/friction state and validate gameplay/macOS/Linux before Rust/Bevy/Solari claims. Continue packet/entity/player/snapshot/rendering work.
 
 
 
 
 
 
537
  - [x] RE-442 Recover crouch entry/exit guards, resource/timer stores, tentative stand-up query and horizontal-speed/up-bit policy with supplied callback returns ([POSTURE-002](research/QVM_POSTURE.md)).
538
  - [x] RE-443 Bridge evaluated bounds/height/flags/resource into prior content/ground/command-scale fixtures with independent consumer oracles ([POSTURE-003](research/QVM_POSTURE.md)).
539
  - [ ] RE-444 Recover native overlap-trace ABI/results, helper39525 effects, resource producers/reset/ranges and posture/type/animation/flag/height meanings; compose complete frame/PS/content/ground/friction state and validate gameplay/macOS/Linux before Rust/Bevy/Solari claims. Continue packet/entity/player/snapshot/rendering work.
540
+
541
+ - [x] RE-445 Recover direct leg/torso animation guards, toggle-bit/raw-request writes and continuation wrapper equality/timer policy with evaluated child effects ([ANIM-001](research/QVM_ANIMATION_CONTACTS.md)).
542
+ - [x] RE-446 Recover forced leg reset/child ordering, timed torso address/store behavior and selected posture-to-animation child compositions ([ANIM-002](research/QVM_ANIMATION_CONTACTS.md)).
543
+ - [x] RE-447 Recover bounded contact-list sentinel/capacity/dedup/append policy and untouched slot canaries ([CONTACT-001](research/QVM_ANIMATION_CONTACTS.md)).
544
+ - [ ] RE-448 Recover animation table allocation/loading/durations/selector-request legality and type/timer/flag producers; contact count resets/list consumers/entity effects/invalid-count and alias domains; compose complete posture/ground/list/animation/movement state and validate runtime/macOS/Linux before Rust/Bevy/Solari claims. Continue packet/entity/player/snapshot/rendering work.
docs/research/QVM_ANIMATION_CONTACTS.md ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Game-QVM animation helpers and contact registration
2
+
3
+ RE-445–447 / F-193–195. [qvm-animation-contacts.json](qvm-animation-contacts.json) retains seven complete instruction partitions,576 animation probes,30 contact probes and ten posture bridges from the verified shipped qagame. Reproduce with `python3 scripts/analyze_qvm_animation_contacts.py`; check with `python3 scripts/test_qvm_animation_contacts.py`.
4
+
5
+ These are bounded synthetic instruction evaluations, following [posture recovery](QVM_POSTURE.md) and [ground classification](QVM_GROUND.md). Original executables, complete QVM, native collision and rendering do not execute. Recovered animation children are evaluated at recorded call boundaries with separate synthetic local frames. Timed-animation table addresses are computed from the instructions, but their words are supplied fixtures: neither initialized nor live table contents, selector domains, request legality or real animation playback are established. Field names describe candidate roles and remain provisional.
6
+
7
+ ## ANIM-001 — Direct setters and continuation wrappers (RE-445)
8
+
9
+ The helper family reads the PS pointer through global35024032 and its movement-frame pointer. PS+76/+72 are called legs/timer below, and PS+84/+80 torso/timer. Direct helper39338 rejects PS type+4 >=3 or positive torso timer. Otherwise it writes torso as `((old & 128) XOR 128) OR request`. It does not reject an identical raw request: old25/request25 becomes153 when permitted.
10
+
11
+ Direct helper39383 first rejects an identical raw leg word/request, then rejects type>=3 or positive leg timer. Otherwise it applies the same toggle/OR expression to PS+76. Old25/request25 remains25; old153/request25 can become25 through this direct setter. Requests themselves are not normalized or masked: a request containing bit128 can force that bit into the result regardless of the toggled old bit.
12
+
13
+ Continuation helper39439 rejects either raw leg equality or equality after clearing bit128 from the current leg word, then rejects a positive leg timer; otherwise it calls39383(request). Torso continuation helper39482 applies the matching policy to PS+84/+80 and calls39338. These wrappers do not check type themselves; the evaluated child supplies that guard. They compare the masked old word to the raw request, rather than masking both operands.
14
+
15
+ Fixtures cover signed type-1/0/3, timer-1/0/1, old words25/153 and requests25/36/153/164. Negative types and timers are synthetic guard probes, not established reachable gameplay inputs. Tests independently model the raw/masked comparisons, toggle algebra, timer/type guards and nested requests.
16
+
17
+ ## ANIM-002 — Forced requests, timed torso and posture composition (RE-446)
18
+
19
+ Helper39525 returns immediately for raw leg equality. Otherwise it first clears PS+72, then calls39383(request). Thus a positive prior timer does not block this forced path. Type>=3 still blocks the child's animation assignment, but the parent's timer reset survives. Raw equality skips both the reset and the child.
20
+
21
+ Helper39552 always clears PS+80, calls39338(request), then assigns PS+80 from this computed word address:
22
+
23
+ `35018540 + frame[+248] * 2560 + request * 32 + 28`
24
+
25
+ The final duration assignment occurs even if the child rejects type>=3. Timed fixtures cover types-1/0/3, old torso26/154, timers-1/0/1, requests36/164, selectors0/2 and supplied duration words-1/0/321. This recovers request/selector strides and load ordering, not the table's allocation bounds, producers, loaded animation definitions or actual durations. No generic array-domain compatibility policy should be inferred from these samples.
26
+
27
+ Ten bridges take the actual helper39525 request sequence from prior posture probes, evaluate its children and then apply the caller's timer200 store. They cover ordinary/no transition, entering, standing up, blocked stand-up, initial timer suppression, early type3 and identical/toggle-bearing current animations. For example, entry from animation0 requests36 and yields164, then timer200. Entry with raw animation36 skips helper animation/reset work, but the posture caller still assigns200. Successful stand-up requests37 with the same raw-equality/toggle policy. This removes the previously omitted animation-child effects from these selected request boundaries only: other PS/frame mutations, native collision/sqrt effects and complete movement composition remain outside the bridge.
28
+
29
+ The bounded evaluator now supports standard signed32 `BXOR`; existing opcode paths are unchanged. The test covers bit128 toggles and high-bit/negative operands. Separate synthetic child frames preserve parent words; source-backed child/parent local-address checks verify isolation.
30
+
31
+ ## CONTACT-001 — Touch/contact list policy (RE-447)
32
+
33
+ Helper39253 receives an entity word. Entity1022 returns immediately. A count at frame+52 equal32 also returns. Otherwise a signed loop scans entries starting at frame+56, each four bytes; any matching word returns without appending. A new entity is written at `frame+56+4*count`, then count increments.
34
+
35
+ Thirty fixtures cover counts0/1/2/31/32, first/last matches, fresh99, -1,1022 and1023 (some input rows coincide for empty/single lists). The append branch accepts1023 and synthetic-1; no semantic validity check occurs in this helper. This does not establish that the complete runtime submits either. A33rd canary slot remains untouched, and full-list requests leave every slot/count unchanged. The equality capacity guard is specifically `==32`, not `>=32`; invalid negative/oversized counts and pointer aliases are untested and remain open.
36
+
37
+ The prior ground helper records39253(trace entity), so these instructions explain that request's candidate list effect. Native contact generation, entity callbacks and subsequent list consumers are not composed here.
38
+
39
+ ## Validation and next tasks
40
+
41
+ Eight tests independently verify all576 animation policies/results and nested requests, direct-setter asymmetry/masked continuation, reset-before-type ordering/raw request bits, timed address arithmetic/unconditional duration store,30 contact list/canary outcomes, ten posture request/timer compositions, XOR/local-frame behavior, source hash/complete partitions and scope. Direct posture/ground/scale reports are regenerated unchanged; the new report reproduces twice byte-identically. Full-suite and storage/link/whitespace outcomes are recorded in the ledger.
42
+
43
+ RE-444 remains open for native collision/resource/semantic/full-state work. RE-448 tracks animation table allocation/loading and real duration/selector/request domains, type/timer/flag producers, frame contact count reset/list consumers/entity effects, invalid-count/alias domains, complete posture/ground/list/animation/movement composition and runtime/macOS/Linux validation before Rust/Bevy/Solari compatibility claims. Packet/entity/player/snapshot/rendering milestones remain open.
docs/research/QVM_GROUND.md CHANGED
@@ -67,3 +67,5 @@ Eight tests independently verify sampling/masks/points/requests, trace arguments
67
  RE-436 remains open for complete policy/producers/runtime work. RE-440 must recover native point/trace callback ABI and collision results, content/surface bit meanings and bounds/height producers, recovery/ground-lost/landing/touch helper effects, actual scratch reset and prior state, full frame/PS/collision-to-friction composition, coefficient writers and gameplay/macOS/Linux before Rust/Bevy/Solari compatibility claims. Packet/entity/player/snapshot/rendering milestones remain open.
68
 
69
  Follow-up: [posture recovery](QVM_POSTURE.md) identifies helper47422 bounds/height producers and transfers selected outputs into content and ground fixtures (RE-441–443). Optional explicit bounds in the ground analyzer preserve default report bytes. Actual native collision and complete state composition remain open under RE-440/444.
 
 
 
67
  RE-436 remains open for complete policy/producers/runtime work. RE-440 must recover native point/trace callback ABI and collision results, content/surface bit meanings and bounds/height producers, recovery/ground-lost/landing/touch helper effects, actual scratch reset and prior state, full frame/PS/collision-to-friction composition, coefficient writers and gameplay/macOS/Linux before Rust/Bevy/Solari compatibility claims. Packet/entity/player/snapshot/rendering milestones remain open.
68
 
69
  Follow-up: [posture recovery](QVM_POSTURE.md) identifies helper47422 bounds/height producers and transfers selected outputs into content and ground fixtures (RE-441–443). Optional explicit bounds in the ground analyzer preserve default report bytes. Actual native collision and complete state composition remain open under RE-440/444.
70
+
71
+ Follow-up: [contact-list recovery](QVM_ANIMATION_CONTACTS.md) explains helper39253 sentinel/capacity/dedup/append behavior for bounded synthetic lists (RE-447). Native contact generation and complete ground/list/entity effects remain open.
docs/research/QVM_POSTURE.md CHANGED
@@ -41,3 +41,5 @@ A grounded first entry with resource2999 yields zero and the prior scale helper'
41
  Eight tests check all420 transitions against an independent branch/arithmetic oracle, tentative trace bounds and arguments, early-return exclusions, resource clamping and animation toggles, horizontal speed/up equality boundaries,48 consumer handoffs and prior ground/scale oracles, source hash/instruction partition and explicit limitations. The direct ground dependency is regenerated unchanged and the new posture report reproduces twice byte-identically. Full suite and storage/link/whitespace checks are recorded in the research ledger.
42
 
43
  RE-440 remains open for native collision and full state composition. RE-444 tracks actual overlap-trace ABI/results, helper39525 effects, resource producers/reset/ranges, animation/type/flag/height meanings, complete frame/PS/content/ground/friction composition and gameplay/macOS/Linux validation before Rust/Bevy/Solari compatibility claims. Packet/entity/player/snapshot and rendering milestones remain open.
 
 
 
41
  Eight tests check all420 transitions against an independent branch/arithmetic oracle, tentative trace bounds and arguments, early-return exclusions, resource clamping and animation toggles, horizontal speed/up equality boundaries,48 consumer handoffs and prior ground/scale oracles, source hash/instruction partition and explicit limitations. The direct ground dependency is regenerated unchanged and the new posture report reproduces twice byte-identically. Full suite and storage/link/whitespace checks are recorded in the research ledger.
42
 
43
  RE-440 remains open for native collision and full state composition. RE-444 tracks actual overlap-trace ABI/results, helper39525 effects, resource producers/reset/ranges, animation/type/flag/height meanings, complete frame/PS/content/ground/friction composition and gameplay/macOS/Linux validation before Rust/Bevy/Solari compatibility claims. Packet/entity/player/snapshot and rendering milestones remain open.
44
+
45
+ Follow-up: [animation-helper recovery](QVM_ANIMATION_CONTACTS.md) evaluates39525 and its guarded child at ten selected posture request boundaries, then applies the caller timer200 store (RE-446). Other native/collision/resource/full-state effects remain open under RE-444/448.
docs/research/README.md CHANGED
@@ -177,3 +177,5 @@ The extension collector adds five focused tests for native tail calls, command-r
177
  [QVM content sampling, ground classification and friction inputs](QVM_GROUND.md) adds RE-437–439. Reproduce with `python3 scripts/analyze_qvm_ground.py`; check with `python3 scripts/test_qvm_ground.py`. Callback results are supplied and only scalar states bridge into friction; full collision/runtime work remains open.
178
 
179
  [QVM posture bounds, transitions and consumer bridges](QVM_POSTURE.md) adds RE-441–443. Reproduce with `python3 scripts/analyze_qvm_posture.py`; check with `python3 scripts/test_qvm_posture.py`. Trace/sqrt results are supplied and only selected state transfers into consumers; native collision/callees/full runtime remain open.
 
 
 
177
  [QVM content sampling, ground classification and friction inputs](QVM_GROUND.md) adds RE-437–439. Reproduce with `python3 scripts/analyze_qvm_ground.py`; check with `python3 scripts/test_qvm_ground.py`. Callback results are supplied and only scalar states bridge into friction; full collision/runtime work remains open.
178
 
179
  [QVM posture bounds, transitions and consumer bridges](QVM_POSTURE.md) adds RE-441–443. Reproduce with `python3 scripts/analyze_qvm_posture.py`; check with `python3 scripts/test_qvm_posture.py`. Trace/sqrt results are supplied and only selected state transfers into consumers; native collision/callees/full runtime remain open.
180
+
181
+ [QVM animation helpers and contact registration](QVM_ANIMATION_CONTACTS.md) adds RE-445–447. Reproduce with `python3 scripts/analyze_qvm_animation_contacts.py`; check with `python3 scripts/test_qvm_animation_contacts.py`. Animation children are evaluated, table duration words supplied, and selected posture requests composed; full collision/runtime remains open.
docs/research/qvm-animation-contacts.json ADDED
The diff for this file is too large to render. See raw diff
 
scripts/analyze_deferred_auth.py CHANGED
@@ -76,9 +76,9 @@ def trace_slice(instructions, start, stop, memory, locals_map, limit=256, byte_m
76
  raise ValueError('shift outside bounded evaluator domain')
77
  shifted = left << right if op == 'LSH' else signed32(left) >> right if op == 'RSHI' else (left & 0xffffffff) >> right
78
  stack.append(signed32(shifted))
79
- elif op in ('BAND', 'BOR'):
80
  right, left = stack.pop(), stack.pop()
81
- stack.append(signed32(left & right if op == 'BAND' else left | right))
82
  elif op in ('MULF', 'ADDF', 'SUBF', 'DIVF'):
83
  right, left = stack.pop(), stack.pop()
84
  a, b = [struct.unpack('<f', struct.pack('<I', v & 0xffffffff))[0] for v in (left, right)]
 
76
  raise ValueError('shift outside bounded evaluator domain')
77
  shifted = left << right if op == 'LSH' else signed32(left) >> right if op == 'RSHI' else (left & 0xffffffff) >> right
78
  stack.append(signed32(shifted))
79
+ elif op in ('BAND', 'BOR', 'BXOR'):
80
  right, left = stack.pop(), stack.pop()
81
+ stack.append(signed32(left & right if op == 'BAND' else left | right if op == 'BOR' else left ^ right))
82
  elif op in ('MULF', 'ADDF', 'SUBF', 'DIVF'):
83
  right, left = stack.pop(), stack.pop()
84
  a, b = [struct.unpack('<f', struct.pack('<I', v & 0xffffffff))[0] for v in (left, right)]
scripts/analyze_qvm_animation_contacts.py ADDED
@@ -0,0 +1,67 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """Bounded QVM animation helper effects, caller bridges and contact registration."""
3
+ import itertools
4
+ import json
5
+ from pathlib import Path
6
+ from analyze_bsp_references import trace_fields
7
+ from analyze_deferred_auth import trace_slice
8
+ from analyze_qvm import decode,write_json
9
+ from analyze_qvm_acceleration import FRAME,PS
10
+ from analyze_qvm_posture import posture_probe
11
+ from analyze_vm_extensions import verified
12
+
13
+ LOCAL=0x48000000
14
+ TABLE=35018540
15
+ PARTITIONS={39253:(39338,39336),39338:(39383,39381),39383:(39439,39437),39439:(39482,39480),39482:(39525,39523),39525:(39552,39550),39552:(39595,39593)}
16
+
17
+
18
+ def evaluate(ins,entry,arg,m,depth=0):
19
+ """Interpret a helper and its recovered children with disjoint local frames."""
20
+ end,stop=PARTITIONS[entry];base=LOCAL+depth*0x1000;size=ins[entry]['operand'];m=dict(m);m[base+size+8]=arg;pc=entry+1;trace=[];calls=[];children=[]
21
+ boundaries=[i['index']+2 for i in ins[entry:end] if i['opcode']=='CALL']
22
+ while pc!=stop:
23
+ t=trace_slice(ins,entry+1,stop,m,{},stop_targets=boundaries,resume_at=pc,local_base=base,limit=4096);trace+=t['trace'];calls+=t['calls'];m=t['memory'];pc=t.get('stop',stop)
24
+ for c in t['calls']:
25
+ if c['target'] not in (39338,39383):raise ValueError('unrecovered animation child')
26
+ child=evaluate(ins,c['target'],c['args'][8],m,depth+1);m=child['memory'];children.append({'entry':c['target'],'calls':child['calls'],**trace_fields({'trace':child['trace']},False)})
27
+ if pc==stop:break
28
+ # Continue after the stopped CALL/POP boundary; do not stop there again.
29
+ t=trace_slice(ins,entry+1,stop,m,{},resume_at=pc,local_base=base,limit=4096)
30
+ if t['calls']:raise ValueError('unexpected additional nested call')
31
+ trace+=t['trace'];m=t['memory'];pc=stop
32
+ return {'memory':m,'calls':calls,'trace':trace,'children':children}
33
+
34
+
35
+ def animation_probe(ins,entry,kind=0,legs=25,torso=26,leg_timer=0,torso_timer=0,request=36,selector=0,duration=321):
36
+ address=TABLE+selector*2560+request*32+28
37
+ m={35024032:FRAME,FRAME:PS,PS+4:kind,PS+76:legs,PS+84:torso,PS+72:leg_timer,PS+80:torso_timer,FRAME+248:selector,address:duration,PS+92:0x12345678}
38
+ t=evaluate(ins,entry,request,m);m=t['memory']
39
+ return {'entry':entry,'type_word':kind,'initial_legs_word':legs,'initial_torso_word':torso,'initial_leg_timer_word':leg_timer,'initial_torso_timer_word':torso_timer,'request_word':request,'table_selector_word':selector,'supplied_table_address':address,'supplied_duration_word':duration,'result_legs_word':m[PS+76]&0xffffffff,'result_torso_word':m[PS+84]&0xffffffff,'result_leg_timer_word':m[PS+72],'result_torso_timer_word':m[PS+80],'canary_word':m[PS+92],'calls':t['calls'],'children':t['children'],**trace_fields(t,False)}
40
+
41
+
42
+ def contact_probe(ins,contacts,entity):
43
+ m={35024032:FRAME,FRAME+52:len(contacts)}
44
+ for i in range(33):m[FRAME+56+i*4]=contacts[i] if i<len(contacts) else -777
45
+ t=evaluate(ins,39253,entity,m);mem=t['memory'];return {'initial_contacts':contacts,'entity_word':entity,'result_count_word':mem[FRAME+52],'result_slots':[mem[FRAME+56+i*4] for i in range(33)],'calls':t['calls'],**trace_fields(t,False)}
46
+
47
+
48
+ def posture_bridge(ins,kwargs):
49
+ p=posture_probe(ins,**kwargs);m={35024032:FRAME,FRAME:PS,PS+4:p['type_word'],PS+76:p['animation_word'],PS+72:p['initial_timer_word'],PS+84:26,PS+80:0};steps=[]
50
+ for call in p['calls']:
51
+ if call['target']!=39525:continue
52
+ t=evaluate(ins,39525,call['args'][8],m);m=t['memory'];steps.append({'request_word':call['args'][8],'helper_legs_word':m[PS+76]&0xffffffff,'helper_leg_timer_word':m[PS+72],'calls':t['calls'],'children':t['children']})
53
+ # Caller writes timer200 immediately after both posture animation requests.
54
+ m[PS+72]=200
55
+ return {'posture_probe':p,'animation_steps':steps,'result_legs_word':m[PS+76]&0xffffffff,'result_leg_timer_word':m[PS+72]}
56
+
57
+
58
+ def main():
59
+ q=json.loads(Path('docs/research/qvm/qagame.json').read_text());raw=verified(Path(q['source']['member']['output']).read_bytes(),q['sha256']);_,ins=decode(raw)
60
+ probes=[animation_probe(ins,e,kind=k,legs=a,torso=a,leg_timer=t,torso_timer=t,request=r) for e,k,a,t,r in itertools.product([39338,39383,39439,39482,39525],[-1,0,3],[25,153],[-1,0,1],[25,36,153,164])]
61
+ probes += [animation_probe(ins,39552,kind=k,torso=a,torso_timer=t,request=r,selector=s,duration=d) for k,a,t,r,s,d in itertools.product([-1,0,3],[26,154],[-1,0,1],[36,164],[0,2],[-1,0,321])]
62
+ contacts=[contact_probe(ins,list(range(count)),e) for count in [0,1,2,31,32] for e in [-1,0,max(0,count-1),99,1022,1023]]
63
+ variants=[{}, {'up':-1}, {'up':-1,'kind':3}, {'up':-1,'animation':36}, {'up':-1,'animation':164}, {'flags':1}, {'flags':1,'allsolid':1}, {'flags':1,'animation':37}, {'flags':1,'animation':165}, {'flags':1,'timer':1}]
64
+ out={'source':q['source'],'sha256':q['sha256'],'helper_partitions':{str(e):ins[e:end] for e,(end,_) in PARTITIONS.items()},'animation_probes':probes,'contact_probes':contacts,'posture_bridges':[posture_bridge(ins,k) for k in variants],'limitations':'Bounded synthetic shipped-QVM helper evaluations only; no complete QVM/original executable/native collision/rendering executes. Animation helpers39338/39383 are evaluated as children with disjoint local frames; table addresses and supplied duration words do not validate initialized/live table contents or selector/request legality. Contact count fixtures are 0..32 and list slots are synthetic; invalid counts/aliasing and real entity/touch effects remain open. Posture bridges apply recovered39525 children at recorded requests and caller timer200 stores; other caller/collision/sqrt/state effects remain as supplied in prior probes. Semantic names and runtime/macOS/Linux/Rust/Bevy/Solari compatibility remain unverified.'};write_json(Path('docs/research/qvm-animation-contacts.json'),out);print('Recovered animation helpers, posture request effects and bounded contact registration')
65
+
66
+
67
+ if __name__=='__main__':main()
scripts/test_qvm_animation_contacts.py ADDED
@@ -0,0 +1,90 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """Independent animation policies, composed requests and bounded contact-list oracles."""
3
+ import hashlib
4
+ import json
5
+ from pathlib import Path
6
+ import unittest
7
+ from analyze_deferred_auth import trace_slice
8
+ from analyze_qvm import decode
9
+ from analyze_qvm_animation_contacts import evaluate,LOCAL
10
+ from analyze_qvm_acceleration import FRAME,PS
11
+
12
+
13
+ def animation_oracle(p):
14
+ e=p['entry'];kind=p['type_word'];legs=p['initial_legs_word'];torso=p['initial_torso_word'];lt=p['initial_leg_timer_word'];tt=p['initial_torso_timer_word'];r=p['request_word'];calls=[]
15
+ def toggle(old):return (((old&128)^128)|r)&0xffffffff
16
+ if e==39338:
17
+ if kind<3 and tt<=0:torso=toggle(torso)
18
+ elif e==39383:
19
+ if legs!=r and kind<3 and lt<=0:legs=toggle(legs)
20
+ elif e==39439:
21
+ if legs!=r and (legs&~128)!=r and lt<=0:
22
+ calls.append(39383)
23
+ if kind<3:legs=toggle(legs)
24
+ elif e==39482:
25
+ if torso!=r and (torso&~128)!=r and tt<=0:
26
+ calls.append(39338)
27
+ if kind<3:torso=toggle(torso)
28
+ elif e==39525:
29
+ if legs!=r:
30
+ lt=0;calls.append(39383)
31
+ if kind<3:legs=toggle(legs)
32
+ elif e==39552:
33
+ tt=p['supplied_duration_word'];calls.append(39338)
34
+ if kind<3:torso=toggle(torso)
35
+ else:raise AssertionError(e)
36
+ return legs,torso,lt,tt,calls
37
+
38
+
39
+ class QvmAnimationContactTests(unittest.TestCase):
40
+ @classmethod
41
+ def setUpClass(cls):cls.r=json.loads(Path('docs/research/qvm-animation-contacts.json').read_text())
42
+
43
+ def test_all_animation_mutations_and_child_requests(self):
44
+ self.assertEqual(len(self.r['animation_probes']),576)
45
+ for p in self.r['animation_probes']:
46
+ legs,torso,lt,tt,calls=animation_oracle(p);self.assertEqual([p['result_legs_word'],p['result_torso_word'],p['result_leg_timer_word'],p['result_torso_timer_word']],[legs,torso,lt,tt]);self.assertEqual([c['target'] for c in p['calls']],calls);self.assertTrue(all(c['args']=={'8':p['request_word']} for c in p['calls']));self.assertEqual([c['entry'] for c in p['children']],calls);self.assertEqual(p['canary_word'],0x12345678)
47
+
48
+ def test_direct_leg_equality_and_torso_retrigger_asymmetry(self):
49
+ rows=self.r['animation_probes']
50
+ leg=next(p for p in rows if p['entry']==39383 and p['type_word']==0 and p['initial_legs_word']==p['request_word']==25 and p['initial_leg_timer_word']==0);torso=next(p for p in rows if p['entry']==39338 and p['type_word']==0 and p['initial_torso_word']==p['request_word']==25 and p['initial_torso_timer_word']==0);self.assertEqual(leg['result_legs_word'],25);self.assertEqual(torso['result_torso_word'],153)
51
+ self.assertTrue(any(p['entry']==39439 and p['initial_legs_word']==153 and p['request_word']==25 and not p['calls'] for p in rows))
52
+
53
+ def test_forced_reset_precedes_type_guard_and_raw_request_bits_survive(self):
54
+ rows=self.r['animation_probes'];self.assertTrue(any(p['entry']==39525 and p['type_word']==3 and p['initial_leg_timer_word']==1 and p['request_word']!=p['initial_legs_word'] and p['result_leg_timer_word']==0 and p['result_legs_word']==p['initial_legs_word'] for p in rows));self.assertTrue(any(p['entry']==39383 and p['type_word']==0 and p['initial_legs_word']==153 and p['request_word']==164 and p['initial_leg_timer_word']==0 and p['result_legs_word']==164 for p in rows))
55
+
56
+ def test_timed_torso_table_address_stride_and_unconditional_duration_store(self):
57
+ rows=[p for p in self.r['animation_probes'] if p['entry']==39552];self.assertEqual(len(rows),216)
58
+ for p in rows:self.assertEqual(p['supplied_table_address'],35018540+p['table_selector_word']*2560+p['request_word']*32+28);self.assertEqual(p['result_torso_timer_word'],p['supplied_duration_word']);self.assertEqual(p['calls'][0]['instruction'],39564)
59
+ self.assertTrue(any(p['type_word']==3 and p['supplied_duration_word']==-1 for p in rows))
60
+
61
+ def test_contact_capacity_sentinel_dedup_append_and_canary(self):
62
+ self.assertEqual(len(self.r['contact_probes']),30)
63
+ for p in self.r['contact_probes']:
64
+ contacts=p['initial_contacts'];entity=p['entity_word'];expected=contacts if entity==1022 or len(contacts)==32 or entity in contacts else contacts+[entity];self.assertEqual(p['result_count_word'],len(expected));self.assertEqual(p['result_slots'],expected+[-777]*(33-len(expected)));self.assertEqual(p['calls'],[])
65
+ self.assertTrue(any(p['entity_word']==1023 and p['result_count_word']==1 for p in self.r['contact_probes']))
66
+
67
+ def test_posture_requests_evaluate_children_before_caller_timer_store(self):
68
+ self.assertEqual(len(self.r['posture_bridges']),10)
69
+ for b in self.r['posture_bridges']:
70
+ p=b['posture_probe'];legs=p['animation_word'];timer=p['initial_timer_word'];requests=[c['args']['8'] for c in p['calls'] if c['target']==39525];self.assertEqual([s['request_word'] for s in b['animation_steps']],requests)
71
+ for request,step in zip(requests,b['animation_steps']):
72
+ if legs!=request:
73
+ timer=0
74
+ if p['type_word']<3:legs=(((legs&128)^128)|request)&0xffffffff
75
+ self.assertEqual([step['helper_legs_word'],step['helper_leg_timer_word']],[legs,timer]);timer=200
76
+ self.assertEqual([b['result_legs_word'],b['result_leg_timer_word']],[legs,timer]);self.assertEqual(timer,p['result_timer_word'])
77
+
78
+ def test_xor_opcode_signed32_and_local_frame_isolation(self):
79
+ for left,right in [(0,128),(128,128),(-1,128),(0x80000000,0xffffffff)]:
80
+ rows=[{'index':0,'opcode':'CONST','operand':left},{'index':1,'opcode':'CONST','operand':right},{'index':2,'opcode':'BXOR','operand':None}];t=trace_slice(rows,0,3,{}, {},capture_stack=True);self.assertEqual(t['stack'][0]&0xffffffff,(left^right)&0xffffffff)
81
+ q=json.loads(Path('docs/research/qvm/qagame.json').read_text());_,ins=decode(Path(q['source']['member']['output']).read_bytes());m={35024032:FRAME,FRAME:PS,PS+4:0,PS+80:1,PS+84:26,FRAME+248:2,35018540+2*2560+36*32+28:321,LOCAL+8:0x12345678};t=evaluate(ins,39552,36,m);self.assertEqual(t['memory'][LOCAL+8],0x12345678);self.assertEqual(t['memory'][LOCAL+12],FRAME);self.assertEqual(t['memory'][LOCAL+0x1000+8],PS);self.assertEqual(t['memory'][PS+80],321)
82
+
83
+ def test_source_complete_partitions_and_scope(self):
84
+ q=json.loads(Path('docs/research/qvm/qagame.json').read_text());self.assertEqual(self.r['source'],q['source']);self.assertEqual(self.r['sha256'],hashlib.sha256(Path(q['source']['member']['output']).read_bytes()).hexdigest())
85
+ ends={39253:39338,39338:39383,39383:39439,39439:39482,39482:39525,39525:39552,39552:39595}
86
+ for e,end in ends.items():part=self.r['helper_partitions'][str(e)];self.assertEqual(part[0]['index'],e);self.assertEqual(part[-1]['index'],end-1)
87
+ for text in ['no complete QVM/original executable/native collision/rendering executes','supplied duration words','invalid counts/aliasing','other caller/collision/sqrt/state effects remain as supplied']:self.assertIn(text,self.r['limitations'])
88
+
89
+
90
+ if __name__=='__main__':unittest.main()