nmarshall commited on
Commit
c248c55
·
1 Parent(s): 41cce5d

qvm: rewind and restore selectors

Browse files
docs/ASSET_PIPELINE_TODO.md CHANGED
@@ -580,3 +580,6 @@ Research progress: RE-632 [report](research/QVM_REWIND_SNAPSHOT.md); remaining n
580
 
581
 
582
  Research progress: RE-633 [report](research/QVM_REWIND_RESTORE.md); remaining native/content/runtime/platform scope stays open.
 
 
 
 
580
 
581
 
582
  Research progress: RE-633 [report](research/QVM_REWIND_RESTORE.md); remaining native/content/runtime/platform scope stays open.
583
+
584
+
585
+ Research progress: RE-634 [report](research/QVM_REWIND_SELECTORS.md); remaining native/content/runtime/platform scope stays open.
docs/FINDINGS.md CHANGED
@@ -932,3 +932,6 @@ Bounded qagame paths recover bomb-plant/inactivity correction requests, direct f
932
 
933
 
934
  **RE-633 — Complete rewind record restoration and byte widening:** 144165 restores saved vectors/client fields, zero-extends the two animation bytes, clears4408 and links once; a second call is inactive. [Evidence](research/QVM_REWIND_RESTORE.md).
 
 
 
 
932
 
933
 
934
  **RE-633 — Complete rewind record restoration and byte widening:** 144165 restores saved vectors/client fields, zero-extends the two animation bytes, clears4408 and links once; a second call is inactive. [Evidence](research/QVM_REWIND_RESTORE.md).
935
+
936
+
937
+ **RE-634 — Fixed player rewind selection and actual restore enumeration:** 144082 and144413 scan exactly64 source slots using matching client/in-use/state/exclusion gates; the restore selector now executes recovered144165. [Evidence](research/QVM_REWIND_SELECTORS.md).
docs/PROVISIONAL_TODO.md CHANGED
@@ -900,3 +900,6 @@ Research progress: RE-632 [report](research/QVM_REWIND_SNAPSHOT.md); remaining n
900
 
901
 
902
  Research progress: RE-633 [report](research/QVM_REWIND_RESTORE.md); remaining native/content/runtime/platform scope stays open.
 
 
 
 
900
 
901
 
902
  Research progress: RE-633 [report](research/QVM_REWIND_RESTORE.md); remaining native/content/runtime/platform scope stays open.
903
+
904
+
905
+ Research progress: RE-634 [report](research/QVM_REWIND_SELECTORS.md); remaining native/content/runtime/platform scope stays open.
docs/RESEARCH_LEDGER.md CHANGED
@@ -842,3 +842,8 @@ Whole291641 now follows supplied world/entity traces through retry, ordered reli
842
  ## 2026-10-20 — RE-633 Complete rewind record restoration and byte widening (separate task commit)
843
 
844
  144165 restores saved vectors/client fields, zero-extends the two animation bytes, clears4408 and links once; a second call is inactive. 16 fixtures independently reconstruct all entity/client words, verify snapshot round-trip byte loss and native link ordering/idempotence. Full suite: 1015 tests pass. Report regenerates byte-identically (601178 bytes). Storage/filter, links and whitespace checks pass. No assets/Rust runtime/shared VM evaluator changed; native/reference/legal/full platform scope remains open.
 
 
 
 
 
 
842
  ## 2026-10-20 — RE-633 Complete rewind record restoration and byte widening (separate task commit)
843
 
844
  144165 restores saved vectors/client fields, zero-extends the two animation bytes, clears4408 and links once; a second call is inactive. 16 fixtures independently reconstruct all entity/client words, verify snapshot round-trip byte loss and native link ordering/idempotence. Full suite: 1015 tests pass. Report regenerates byte-identically (601178 bytes). Storage/filter, links and whitespace checks pass. No assets/Rust runtime/shared VM evaluator changed; native/reference/legal/full platform scope remains open.
845
+
846
+
847
+ ## 2026-10-20 — RE-634 Fixed player rewind selection and actual restore enumeration (separate task commit)
848
+
849
+ 144082 and144413 scan exactly64 source slots using matching client/in-use/state/exclusion gates; the restore selector now executes recovered144165. 16 selector/exclusion/global-count/delta fixtures independently check all64 predicates, call order, time ABI and actual child restoration. Full suite: 1019 tests pass. Report regenerates byte-identically (1219554 bytes). Storage/filter, links and whitespace checks pass. No assets/Rust runtime/shared VM evaluator changed; native/reference/legal/full platform scope remains open.
docs/REVERSE_ENGINEERING_TODO.md CHANGED
@@ -943,3 +943,6 @@ Purpose: turn the supplied installation into an evidence-backed specification of
943
 
944
 
945
  - [x] RE-633 Complete rewind record restoration and byte widening ([report](research/QVM_REWIND_RESTORE.md)).
 
 
 
 
943
 
944
 
945
  - [x] RE-633 Complete rewind record restoration and byte widening ([report](research/QVM_REWIND_RESTORE.md)).
946
+
947
+
948
+ - [x] RE-634 Fixed player rewind selection and actual restore enumeration ([report](research/QVM_REWIND_SELECTORS.md)).
docs/research/QVM_REWIND_SELECTORS.md ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ # Fixed player rewind selection and actual restore enumeration
2
+
3
+ RE-634. Reproduce with `python3 scripts/analyze_qvm_rewind_selectors.py`; check with `python3 scripts/test_qvm_rewind_selectors.py`. The [machine report](qvm-rewind-selectors.json) retains source identity and bounded fixtures.
4
+
5
+ Actual144082 selects client!=0, in-use!=0, signed client3096<3, not excluded,3740==0 and4324==0. Its143100 calls are explicitly recorded under a nonmutating child contract, not treated as executed history rewind. Actual144413 uses the same64-slot gates and dispatches recovered144165 for each selected record. The source loops ignore global entity count; fixtures set count0/1024 and include slot63 to retain that distinction. Delta is passed unchanged only to rewind. Independent predicates check every slot, including inactive, no-client, states3/4, both exclusion fields and state2. Restore child records match independent full-state oracles. Native linking remains captured and full143100 interpolation remains unresolved.
6
+
7
+ No original executable/full QVM/native collision/physics/rendering executes. Native responses and legal input domains are supplied or unverified where stated; full runtime/macOS/Linux Rust/Bevy/Solari gameplay remains open.
docs/research/README.md CHANGED
@@ -501,3 +501,6 @@ The extension collector adds five focused tests for native tail calls, command-r
501
 
502
 
503
  [RE-633: Complete rewind record restoration and byte widening](QVM_REWIND_RESTORE.md). Reproduce with `python3 scripts/analyze_qvm_rewind_restore.py`; check with `python3 scripts/test_qvm_rewind_restore.py`.
 
 
 
 
501
 
502
 
503
  [RE-633: Complete rewind record restoration and byte widening](QVM_REWIND_RESTORE.md). Reproduce with `python3 scripts/analyze_qvm_rewind_restore.py`; check with `python3 scripts/test_qvm_rewind_restore.py`.
504
+
505
+
506
+ [RE-634: Fixed player rewind selection and actual restore enumeration](QVM_REWIND_SELECTORS.md). Reproduce with `python3 scripts/analyze_qvm_rewind_selectors.py`; check with `python3 scripts/test_qvm_rewind_selectors.py`.
docs/research/qvm-rewind-selectors.json ADDED
The diff for this file is too large to render. See raw diff
 
scripts/analyze_qvm_rewind_selectors.py ADDED
@@ -0,0 +1,51 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """Fixed64-slot rewind selection and actual recovered restoration selector."""
3
+ import itertools
4
+ from pathlib import Path
5
+ from qvm_research_support import source,execute
6
+ from analyze_qvm_rewind_snapshot import fixture as player_fixture,snapshot,CLIENT_OFFSETS
7
+ from analyze_qvm_rewind_restore import restore_player
8
+ from analyze_qvm_entity_init import POOL,STRIDE
9
+ from qvm_server_fire_support import ENTITY
10
+ from analyze_qvm import write_json
11
+ LOCAL=0x79b00000
12
+
13
+
14
+ def pool_fixture(raw,h,ins):
15
+ template=player_fixture(raw,h);template,_=snapshot(ins,template);source_client=template[ENTITY+520];m={34992508:0}
16
+ for index in range(64):
17
+ p=POOL+index*STRIDE
18
+ for o in range(0,STRIDE,4):m[p+o]=template[ENTITY+o]
19
+ m[p]=index;m[p+520]=0;m[p+532]=1
20
+ for index in [0,1,2,3,4,5,6,7,8,63]:
21
+ p=POOL+index*STRIDE;client=0x200000+index*0x2000;m[p+520]=client
22
+ for o in CLIENT_OFFSETS:m[client+o]=template[source_client+o]
23
+ m[client+3096]=0;m[client+3740]=0;m[client+4324]=0
24
+ for o in [24,28,32,36,40,44,60,64,68,488,492,496,196,200]:m[p+o]=0x99887766
25
+ m[POOL+2*STRIDE+520]=0;m[POOL+3*STRIDE+532]=0;m[0x200000+4*0x2000+3096]=3;m[0x200000+5*0x2000+3096]=4;m[0x200000+6*0x2000+3740]=1;m[0x200000+7*0x2000+4324]=1;m[0x200000+8*0x2000+3096]=2
26
+ return m
27
+
28
+
29
+ def select(ins,m,entry,excluded,delta=1200):
30
+ m=dict(m);rows=[]
31
+ for index in range(64):
32
+ p=POOL+index*STRIDE;client=m[p+520];rows.append({'index':index,'pointer':p,'client':client,'used':m[p+532],'state3096':m.get(client+3096) if client else None,'field3740':m.get(client+3740) if client else None,'field4324':m.get(client+4324) if client else None})
33
+ requests=[];effects=[]
34
+ if entry==144082:
35
+ base=LOCAL;m[base+36]=delta;m[base+40]=excluded
36
+ def capture(i,m,a,c):e={'arguments':a,'child_not_executed':True};requests.append(e);return m,c,0,e
37
+ m,_,e=execute(ins,144083,144163,m,(0,0,0),base,{143100:capture});scope='selection-only-nonmutating-child-contract'
38
+ else:
39
+ assert entry==144413;base=LOCAL+0x1000;m[base+32]=excluded
40
+ def child(i,m,a,c):m,r=restore_player(i,m,a[8]);requests.append({'arguments':a});effects.append(r);return m,c,0,r
41
+ m,_,e=execute(ins,144414,144491,m,(0,0,0),base,{144165:child});scope='actual144165-restoration'
42
+ if entry==144413:e['children']=[{'call':child['call'],'effect':{'restore_effect_index':j}} for j,child in enumerate(e['children'])]
43
+ return m,{'entry':entry,'excluded':excluded,'delta':delta,'global_entity_count':m[34992508],'slots':rows,'requests':requests,'restore_effects':effects,'execution':e,'scope':scope}
44
+
45
+
46
+ def main():
47
+ q,raw,h,ins=source();rows=[]
48
+ for entry,excluded,count,delta in itertools.product([144082,144413],[0,20],[0,1024],[0,1200]):
49
+ m=pool_fixture(raw,h,ins);m[34992508]=count;rows.append(select(ins,m,entry,POOL+excluded*STRIDE,delta)[1])
50
+ write_json(Path('docs/research/qvm-rewind-selectors.json'),{'source':q['source'],'sha256':q['sha256'],'probes':rows,'limits':'Actual64-slot144082 selection with nonmutating captured143100 calls, and actual144413→144165 restore. Captured rewind callbacks are not full rewind execution. Entity count global is intentionally ignored by source fixed64 loops; native links captured.'})
51
+ if __name__=='__main__':main()
scripts/test_qvm_rewind_selectors.py ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json,unittest
2
+ from pathlib import Path
3
+ from test_qvm_rewind_restore import expected
4
+ class RewindSelectorTests(unittest.TestCase):
5
+ @classmethod
6
+ def setUpClass(cls):cls.r=json.loads(Path('docs/research/qvm-rewind-selectors.json').read_text())
7
+ def test_independent_all64_slot_predicate(self):
8
+ self.assertEqual(len(self.r['probes']),16)
9
+ for p in self.r['probes']:
10
+ eligible=[s['pointer'] for s in p['slots'] if s['client'] and s['used'] and s['state3096']<3 and s['pointer']!=p['excluded'] and not s['field3740'] and not s['field4324']];self.assertEqual([r['arguments']['8'] for r in p['requests']],eligible);self.assertEqual([s['index'] for s in p['slots']],list(range(64)))
11
+ def test_global_count_does_not_change_fixed_pool_scope(self):
12
+ for entry in [144082,144413]:
13
+ for excluded in [34091368,34091368+20*880]:
14
+ rows=[p for p in self.r['probes'] if p['entry']==entry and p['excluded']==excluded];self.assertTrue(all([r['arguments']['8'] for r in p['requests']]==[r['arguments']['8'] for r in rows[0]['requests']] for p in rows))
15
+ def test_rewind_requests_pass_delta_without_fake_effects(self):
16
+ for p in self.r['probes']:
17
+ if p['entry']==144082:
18
+ self.assertEqual(p['restore_effects'],[])
19
+ for r in p['requests']:self.assertEqual(r['arguments']['12'],p['delta']);self.assertTrue(r['child_not_executed'])
20
+ def test_restore_selector_executes_recovered_child(self):
21
+ for p in self.r['probes']:
22
+ if p['entry']==144413:
23
+ self.assertEqual([c['target'] for c in p['execution']['calls']],[144165]*len(p['requests']))
24
+ for r in p['restore_effects']:self.assertEqual((r['final_entity'],r['final_client']),expected(r));self.assertEqual(r['final_client']['4408'],0)
25
+ if __name__=='__main__':unittest.main()