qvm: rewind snapshot restoration
Browse files- docs/ASSET_PIPELINE_TODO.md +3 -0
- docs/FINDINGS.md +3 -0
- docs/PROVISIONAL_TODO.md +3 -0
- docs/RESEARCH_LEDGER.md +5 -0
- docs/REVERSE_ENGINEERING_TODO.md +3 -0
- docs/research/QVM_REWIND_RESTORE.md +7 -0
- docs/research/README.md +3 -0
- docs/research/qvm-rewind-restore.json +0 -0
- scripts/analyze_qvm_rewind_restore.py +27 -0
- scripts/test_qvm_rewind_restore.py +28 -0
docs/ASSET_PIPELINE_TODO.md
CHANGED
|
@@ -577,3 +577,6 @@ Research progress: RE-631 [report](research/QVM_TRACE_RESPONSE_CHAIN.md); remain
|
|
| 577 |
|
| 578 |
|
| 579 |
Research progress: RE-632 [report](research/QVM_REWIND_SNAPSHOT.md); remaining native/content/runtime/platform scope stays open.
|
|
|
|
|
|
|
|
|
|
|
|
| 577 |
|
| 578 |
|
| 579 |
Research progress: RE-632 [report](research/QVM_REWIND_SNAPSHOT.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.
|
docs/FINDINGS.md
CHANGED
|
@@ -929,3 +929,6 @@ Bounded qagame paths recover bomb-plant/inactivity correction requests, direct f
|
|
| 929 |
|
| 930 |
|
| 931 |
**RE-632 — Rewind snapshot prefix and packed animation bytes:** 143100 saves entity vectors and selected client state, sets client4408 and packs low animation bytes before history selection. [Evidence](research/QVM_REWIND_SNAPSHOT.md).
|
|
|
|
|
|
|
|
|
|
|
|
| 929 |
|
| 930 |
|
| 931 |
**RE-632 — Rewind snapshot prefix and packed animation bytes:** 143100 saves entity vectors and selected client state, sets client4408 and packs low animation bytes before history selection. [Evidence](research/QVM_REWIND_SNAPSHOT.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).
|
docs/PROVISIONAL_TODO.md
CHANGED
|
@@ -897,3 +897,6 @@ Research progress: RE-631 [report](research/QVM_TRACE_RESPONSE_CHAIN.md); remain
|
|
| 897 |
|
| 898 |
|
| 899 |
Research progress: RE-632 [report](research/QVM_REWIND_SNAPSHOT.md); remaining native/content/runtime/platform scope stays open.
|
|
|
|
|
|
|
|
|
|
|
|
| 897 |
|
| 898 |
|
| 899 |
Research progress: RE-632 [report](research/QVM_REWIND_SNAPSHOT.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.
|
docs/RESEARCH_LEDGER.md
CHANGED
|
@@ -837,3 +837,8 @@ Whole291641 now follows supplied world/entity traces through retry, ordered reli
|
|
| 837 |
## 2026-10-20 — RE-632 Rewind snapshot prefix and packed animation bytes (separate task commit)
|
| 838 |
|
| 839 |
143100 saves entity vectors and selected client state, sets client4408 and packs low animation bytes before history selection. 16 raw-word/animation/null-client fixtures independently check every snapshot word, byte-store order and unchanged entity records. Full suite: 1011 tests pass. Report regenerates byte-identically (199100 bytes). Storage/filter, links and whitespace checks pass. No assets/Rust runtime/shared VM evaluator changed; native/reference/legal/full platform scope remains open.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 837 |
## 2026-10-20 — RE-632 Rewind snapshot prefix and packed animation bytes (separate task commit)
|
| 838 |
|
| 839 |
143100 saves entity vectors and selected client state, sets client4408 and packs low animation bytes before history selection. 16 raw-word/animation/null-client fixtures independently check every snapshot word, byte-store order and unchanged entity records. Full suite: 1011 tests pass. Report regenerates byte-identically (199100 bytes). Storage/filter, links and whitespace checks pass. No assets/Rust runtime/shared VM evaluator changed; native/reference/legal/full platform scope remains open.
|
| 840 |
+
|
| 841 |
+
|
| 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.
|
docs/REVERSE_ENGINEERING_TODO.md
CHANGED
|
@@ -940,3 +940,6 @@ Purpose: turn the supplied installation into an evidence-backed specification of
|
|
| 940 |
|
| 941 |
|
| 942 |
- [x] RE-632 Rewind snapshot prefix and packed animation bytes ([report](research/QVM_REWIND_SNAPSHOT.md)).
|
|
|
|
|
|
|
|
|
|
|
|
| 940 |
|
| 941 |
|
| 942 |
- [x] RE-632 Rewind snapshot prefix and packed animation bytes ([report](research/QVM_REWIND_SNAPSHOT.md)).
|
| 943 |
+
|
| 944 |
+
|
| 945 |
+
- [x] RE-633 Complete rewind record restoration and byte widening ([report](research/QVM_REWIND_RESTORE.md)).
|
docs/research/QVM_REWIND_RESTORE.md
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Complete rewind record restoration and byte widening
|
| 2 |
+
|
| 3 |
+
RE-633. Reproduce with `python3 scripts/analyze_qvm_rewind_restore.py`; check with `python3 scripts/test_qvm_rewind_restore.py`. The [machine report](qvm-rewind-restore.json) retains source identity and bounded fixtures.
|
| 4 |
+
|
| 5 |
+
Actual144166–144410 returns without effects when client4408==0. Otherwise it clears4408, copies saved vectors back into entity24/60/488/36, restores entity200/196 from bytes4448/4449 and restores six selected client words. Native−31 is requested only after all those writes. The report composes the recovered snapshot with explicitly supplied rewound live values, not actual history interpolation. Independent full-state checks verify the reverse map and expected animation truncation: original high bits are not recovered from byte snapshots. A second actual call performs no link or state change. Null-client legality and full143100 interpolation remain outside this helper contract; no native relink effect executes.
|
| 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
|
@@ -498,3 +498,6 @@ The extension collector adds five focused tests for native tail calls, command-r
|
|
| 498 |
|
| 499 |
|
| 500 |
[RE-632: Rewind snapshot prefix and packed animation bytes](QVM_REWIND_SNAPSHOT.md). Reproduce with `python3 scripts/analyze_qvm_rewind_snapshot.py`; check with `python3 scripts/test_qvm_rewind_snapshot.py`.
|
|
|
|
|
|
|
|
|
|
|
|
| 498 |
|
| 499 |
|
| 500 |
[RE-632: Rewind snapshot prefix and packed animation bytes](QVM_REWIND_SNAPSHOT.md). Reproduce with `python3 scripts/analyze_qvm_rewind_snapshot.py`; check with `python3 scripts/test_qvm_rewind_snapshot.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`.
|
docs/research/qvm-rewind-restore.json
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
scripts/analyze_qvm_rewind_restore.py
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""Complete144165 snapshot restore, including LOAD1 and native link boundary."""
|
| 3 |
+
import itertools
|
| 4 |
+
from pathlib import Path
|
| 5 |
+
from qvm_research_support import source,execute
|
| 6 |
+
from qvm_server_fire_support import ENTITY
|
| 7 |
+
from analyze_qvm_rewind_snapshot import fixture,snapshot,client_state
|
| 8 |
+
from analyze_qvm_temp_event import record
|
| 9 |
+
from analyze_qvm import write_json
|
| 10 |
+
LOCAL=0x79a00000
|
| 11 |
+
|
| 12 |
+
def restore_player(ins,m,pointer=ENTITY):
|
| 13 |
+
m=dict(m);client=m[pointer+520];initial=record(m,pointer);before=client_state(m,client);requests=[];m[LOCAL+68]=pointer;packed=m[client+4448]
|
| 14 |
+
def link(i,m,a,c):e={'arguments':a,'record_at_request':record(m,pointer),'client_at_request':client_state(m,client),'native_effect_not_executed':True};requests.append(e);return m,c,0,e
|
| 15 |
+
m,_,e=execute(ins,144166,144411,m,(0,0,0),LOCAL,{-31:link},byte_inputs={client+4448:packed&255,client+4449:(packed>>8)&255})
|
| 16 |
+
return m,{'pointer':pointer,'client':client,'initial_entity':initial,'initial_client':before,'final_entity':record(m,pointer),'final_client':client_state(m,client),'requests':requests,'execution':e}
|
| 17 |
+
|
| 18 |
+
def main():
|
| 19 |
+
q,raw,h,ins=source();rows=[]
|
| 20 |
+
for canary,animation in itertools.product([0,0x12345678,0xffffffff],[0,255,256,0x1234,0xffffffff]):
|
| 21 |
+
m=fixture(raw,h,canary,animation);m,s=snapshot(ins,m);client=m[ENTITY+520]
|
| 22 |
+
for o in [24,28,32,36,40,44,60,64,68,488,492,496,196,200]:m[ENTITY+o]=0x99887766
|
| 23 |
+
for o in [4380,4384,4388,4392,6204,6256]:m[client+o]=0x11223344
|
| 24 |
+
m,r=restore_player(ins,m);_,second=restore_player(ins,m);r['original_entity']=s['initial_entity'];r['original_client']=s['initial_client'];r['second_execution']=second['execution'];r['second_entity']=second['final_entity'];rows.append(r)
|
| 25 |
+
m=fixture(raw,h);m[m[ENTITY+520]+4408]=0;rows.append(restore_player(ins,m)[1])
|
| 26 |
+
write_json(Path('docs/research/qvm-rewind-restore.json'),{'source':q['source'],'sha256':q['sha256'],'probes':rows,'limits':'Complete144166–144410 using recovered snapshot plus explicitly supplied rewound live fields; native−31 captured only. Animation restore zero-extends saved bytes. No actual history interpolation, engine relink or full143100 rewind executes.'})
|
| 27 |
+
if __name__=='__main__':main()
|
scripts/test_qvm_rewind_restore.py
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import json,unittest
|
| 2 |
+
from pathlib import Path
|
| 3 |
+
|
| 4 |
+
def expected(p):
|
| 5 |
+
entity=dict(p['initial_entity']);client=dict(p['initial_client'])
|
| 6 |
+
if not client['4408']:return entity,client
|
| 7 |
+
client['4408']=0
|
| 8 |
+
for dst,src in [(24,4424),(60,4436),(488,4412),(36,4452)]:
|
| 9 |
+
for j in range(3):entity[str(dst+4*j)]=client[str(src+4*j)]
|
| 10 |
+
entity['200']=client['4448']&255;entity['196']=(client['4448']>>8)&255
|
| 11 |
+
for dst,src in [(4380,4468),(4384,4464),(4388,4476),(4392,4472),(6204,4484),(6256,4488)]:client[str(dst)]=client[str(src)]
|
| 12 |
+
return entity,client
|
| 13 |
+
class RewindRestoreTests(unittest.TestCase):
|
| 14 |
+
@classmethod
|
| 15 |
+
def setUpClass(cls):cls.r=json.loads(Path('docs/research/qvm-rewind-restore.json').read_text())
|
| 16 |
+
def test_independent_full_restore_oracle(self):
|
| 17 |
+
self.assertEqual(len(self.r['probes']),16)
|
| 18 |
+
for p in self.r['probes']:self.assertEqual((p['final_entity'],p['final_client']),expected(p))
|
| 19 |
+
def test_original_values_and_animation_byte_loss(self):
|
| 20 |
+
for p in self.r['probes'][:-1]:
|
| 21 |
+
original=dict(p['original_entity']);original['200']&=255;original['196']&=255;self.assertEqual(p['final_entity'],original)
|
| 22 |
+
for o in [4380,4384,4388,4392,6204,6256]:self.assertEqual(p['final_client'][str(o)],p['original_client'][str(o)])
|
| 23 |
+
def test_link_after_complete_restore_and_idempotent_guard(self):
|
| 24 |
+
for p in self.r['probes'][:-1]:
|
| 25 |
+
self.assertEqual([c['target'] for c in p['execution']['calls']],[-31]);self.assertEqual(p['requests'][0]['record_at_request'],p['final_entity']);self.assertEqual(p['requests'][0]['client_at_request'],p['final_client']);self.assertEqual(p['second_execution']['calls'],[]);self.assertEqual(p['second_entity'],p['final_entity'])
|
| 26 |
+
def test_inactive_guard_preserves_every_word(self):
|
| 27 |
+
p=self.r['probes'][-1];self.assertEqual(p['initial_entity'],p['final_entity']);self.assertEqual(p['initial_client'],p['final_client']);self.assertEqual(p['requests'],[])
|
| 28 |
+
if __name__=='__main__':unittest.main()
|