qvm: weapon continuation guards
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_WEAPON_GUARDS.md +11 -0
- docs/research/README.md +3 -0
- docs/research/qvm-weapon-guards.json +0 -0
- scripts/analyze_qvm_weapon_guards.py +23 -0
- scripts/test_qvm_weapon_guards.py +37 -0
docs/ASSET_PIPELINE_TODO.md
CHANGED
|
@@ -415,3 +415,6 @@ Research progress: RE-574 [report](research/QVM_SPECIAL_QUERY.md); remaining nat
|
|
| 415 |
|
| 416 |
|
| 417 |
Research progress: RE-575 [report](research/QVM_WEAPON_SELECTION_CALLER.md); remaining native/content/runtime/platform scope stays open.
|
|
|
|
|
|
|
|
|
|
|
|
| 415 |
|
| 416 |
|
| 417 |
Research progress: RE-575 [report](research/QVM_WEAPON_SELECTION_CALLER.md); remaining native/content/runtime/platform scope stays open.
|
| 418 |
+
|
| 419 |
+
|
| 420 |
+
Research progress: RE-576 [report](research/QVM_WEAPON_GUARDS.md); remaining native/content/runtime/platform scope stays open.
|
docs/FINDINGS.md
CHANGED
|
@@ -773,3 +773,6 @@ Bounded qagame paths recover bomb-plant/inactivity correction requests, direct f
|
|
| 773 |
|
| 774 |
|
| 775 |
**RE-575 — Weapon prefix to actual selection handoff:** 50746 re-decodes the current ID after mode processing, requests48798 only when it differs from command byte24, and suppresses that request for currentID27 while its decremented timer is positive. [Evidence](research/QVM_WEAPON_SELECTION_CALLER.md).
|
|
|
|
|
|
|
|
|
|
|
|
| 773 |
|
| 774 |
|
| 775 |
**RE-575 — Weapon prefix to actual selection handoff:** 50746 re-decodes the current ID after mode processing, requests48798 only when it differs from command byte24, and suppresses that request for currentID27 while its decremented timer is positive. [Evidence](research/QVM_WEAPON_SELECTION_CALLER.md).
|
| 776 |
+
|
| 777 |
+
|
| 778 |
+
**RE-576 — Weapon continuation guards timer floors and cancellation:** 50923..51206 applies ordered traversal/action guards, floors timers at300, conditionally cancels state512 and returns while timer is positive; two ID checks literally assign localID12 on their fallback paths. [Evidence](research/QVM_WEAPON_GUARDS.md).
|
docs/PROVISIONAL_TODO.md
CHANGED
|
@@ -729,3 +729,6 @@ Research progress: RE-574 [report](research/QVM_SPECIAL_QUERY.md); remaining nat
|
|
| 729 |
|
| 730 |
|
| 731 |
Research progress: RE-575 [report](research/QVM_WEAPON_SELECTION_CALLER.md); remaining native/content/runtime/platform scope stays open.
|
|
|
|
|
|
|
|
|
|
|
|
| 729 |
|
| 730 |
|
| 731 |
Research progress: RE-575 [report](research/QVM_WEAPON_SELECTION_CALLER.md); remaining native/content/runtime/platform scope stays open.
|
| 732 |
+
|
| 733 |
+
|
| 734 |
+
Research progress: RE-576 [report](research/QVM_WEAPON_GUARDS.md); remaining native/content/runtime/platform scope stays open.
|
docs/RESEARCH_LEDGER.md
CHANGED
|
@@ -571,3 +571,8 @@ Actual54444 reload/interaction order exposes timer interference before the weapo
|
|
| 571 |
## 2026-10-20 — RE-575 Weapon prefix to actual selection handoff (separate task commit)
|
| 572 |
|
| 573 |
50746 re-decodes the current ID after mode processing, requests48798 only when it differs from command byte24, and suppresses that request for currentID27 while its decremented timer is positive. 41 source-order compositions check prefix oracles, equality and ID27 timer16/17 boundary, present/missing requested slot, helper rejection and actual early exits. Full suite: 819 tests pass. Report regenerates byte-identically (1148636 bytes). Storage/filter, links and whitespace checks pass. No assets/Rust runtime/shared VM evaluator changed; native/reference/legal/full platform scope remains open.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 571 |
## 2026-10-20 — RE-575 Weapon prefix to actual selection handoff (separate task commit)
|
| 572 |
|
| 573 |
50746 re-decodes the current ID after mode processing, requests48798 only when it differs from command byte24, and suppresses that request for currentID27 while its decremented timer is positive. 41 source-order compositions check prefix oracles, equality and ID27 timer16/17 boundary, present/missing requested slot, helper rejection and actual early exits. Full suite: 819 tests pass. Report regenerates byte-identically (1148636 bytes). Storage/filter, links and whitespace checks pass. No assets/Rust runtime/shared VM evaluator changed; native/reference/legal/full platform scope remains open.
|
| 574 |
+
|
| 575 |
+
|
| 576 |
+
## 2026-10-20 — RE-576 Weapon continuation guards timer floors and cancellation (separate task commit)
|
| 577 |
+
|
| 578 |
+
50923..51206 applies ordered traversal/action guards, floors timers at300, conditionally cancels state512 and returns while timer is positive; two ID checks literally assign localID12 on their fallback paths. 90 independent probes check exact state/stop/local-ID results, timer−1/0/1/299/300, state512/button priority and literal assignment for IDs outside11/13. Full suite: 823 tests pass. Report regenerates byte-identically (260588 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
|
@@ -769,3 +769,6 @@ Purpose: turn the supplied installation into an evidence-backed specification of
|
|
| 769 |
|
| 770 |
|
| 771 |
- [x] RE-575 Weapon prefix to actual selection handoff ([report](research/QVM_WEAPON_SELECTION_CALLER.md)).
|
|
|
|
|
|
|
|
|
|
|
|
| 769 |
|
| 770 |
|
| 771 |
- [x] RE-575 Weapon prefix to actual selection handoff ([report](research/QVM_WEAPON_SELECTION_CALLER.md)).
|
| 772 |
+
|
| 773 |
+
|
| 774 |
+
- [x] RE-576 Weapon continuation guards timer floors and cancellation ([report](research/QVM_WEAPON_GUARDS.md)).
|
docs/research/QVM_WEAPON_GUARDS.md
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Weapon continuation guards timer floors and cancellation
|
| 2 |
+
|
| 3 |
+
RE-576. Reproduce with `python3 scripts/analyze_qvm_weapon_guards.py`; check with `python3 scripts/test_qvm_weapon_guards.py`. The [machine report](qvm-weapon-guards.json) retains source identity and bounded fixtures.
|
| 4 |
+
|
| 5 |
+
This standalone slice starts at50923 with explicitly supplied parent local ID. PS12 bit8 plus state32 clears bits32/64, sets weapon state2 and exits. State16384 plus32 exits without those writes. In both groups IDs11/13 branch directly, while other IDs execute CONST12→local12 STORE and then branch on that nonzero value. The source therefore assigns12 rather than comparing ID to12; packed weapon is unchanged. Intended high-level logic is not inferred.
|
| 6 |
+
|
| 7 |
+
Next, state1024 or PS12 bit8 exits. State4 floors PS44 at300. State512 with button1 stays pending while timer is positive, otherwise clears512 and sets300. Without button1, state128/256 causes immediate exit retaining512; otherwise the timer is floored300 and512 clears. Finally positive PS44 exits; nonpositive timer reaches51207.
|
| 8 |
+
|
| 9 |
+
90 fixtures independently check exact state and source stop boundaries. No prefix is run in these standalone probes, so input state32 is not silently cleared by an earlier absent button1. Remaining: compose these gates after actual selection and recover ready/ammo/reload/phase branches while preserving local state and caller legality.
|
| 10 |
+
|
| 11 |
+
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
|
@@ -330,3 +330,6 @@ The extension collector adds five focused tests for native tail calls, command-r
|
|
| 330 |
|
| 331 |
|
| 332 |
[RE-575: Weapon prefix to actual selection handoff](QVM_WEAPON_SELECTION_CALLER.md). Reproduce with `python3 scripts/analyze_qvm_weapon_selection_caller.py`; check with `python3 scripts/test_qvm_weapon_selection_caller.py`.
|
|
|
|
|
|
|
|
|
|
|
|
| 330 |
|
| 331 |
|
| 332 |
[RE-575: Weapon prefix to actual selection handoff](QVM_WEAPON_SELECTION_CALLER.md). Reproduce with `python3 scripts/analyze_qvm_weapon_selection_caller.py`; check with `python3 scripts/test_qvm_weapon_selection_caller.py`.
|
| 333 |
+
|
| 334 |
+
|
| 335 |
+
[RE-576: Weapon continuation guards timer floors and cancellation](QVM_WEAPON_GUARDS.md). Reproduce with `python3 scripts/analyze_qvm_weapon_guards.py`; check with `python3 scripts/test_qvm_weapon_guards.py`.
|
docs/research/qvm-weapon-guards.json
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
scripts/analyze_qvm_weapon_guards.py
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""50923..51206 weapon state guards, timer floors and bit512 cancellation."""
|
| 3 |
+
import itertools
|
| 4 |
+
from pathlib import Path
|
| 5 |
+
from qvm_research_support import source,execute
|
| 6 |
+
from qvm_weapon_support import weapon_seed,weapon_state
|
| 7 |
+
from analyze_qvm_weapon_prefix import LOCAL
|
| 8 |
+
from analyze_qvm_acceleration import PS,FRAME
|
| 9 |
+
from analyze_qvm import write_json
|
| 10 |
+
|
| 11 |
+
def guards(ins,m,cmd=(0,0,0)):
|
| 12 |
+
initial=weapon_state(m);ident=initial['packed']&255;m[LOCAL+12]=ident
|
| 13 |
+
m,c,r=execute(ins,50923,53046,m,cmd,LOCAL,boundaries=[51207])
|
| 14 |
+
return m,{'initial':initial,'buttons':m[FRAME+20],'initial_local_id':ident,'final_local_id':m[LOCAL+12],'execution':r,'final':weapon_state(m)}
|
| 15 |
+
|
| 16 |
+
def main():
|
| 17 |
+
q,raw,h,ins=source();rows=[]
|
| 18 |
+
for flags,special,ident in itertools.product([0,8],[0,32,32|16384],[1,11,12,13,22]):
|
| 19 |
+
m=weapon_seed(raw,h);m.update({PS+312:ident,PS+12:flags,PS+220:special,PS+44:0,FRAME+20:1});rows.append(guards(ins,m)[1])
|
| 20 |
+
for special,buttons,timer in itertools.product([0,1024,4,512,512|128,512|256],[0,1],[-1,0,1,299,300]):
|
| 21 |
+
m=weapon_seed(raw,h);m.update({PS+312:22,PS+220:special,PS+44:timer,FRAME+20:buttons});rows.append(guards(ins,m)[1])
|
| 22 |
+
write_json(Path('docs/research/qvm-weapon-guards.json'),{'source':q['source'],'sha256':q['sha256'],'probes':rows,'limits':'Finite50923..51206 with supplied entry state/local ID; no prefix performed for standalone probes. Stops51207 or actual exit53046. Assignment-to-local12 observed literally, not inferred intended logic.'})
|
| 23 |
+
if __name__=='__main__':main()
|
scripts/test_qvm_weapon_guards.py
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import json,unittest,copy
|
| 2 |
+
from pathlib import Path
|
| 3 |
+
from test_qvm_torso_selector import signed
|
| 4 |
+
|
| 5 |
+
def guard_state(p):
|
| 6 |
+
a=copy.deepcopy(p['initial']);ident=p['initial_local_id'];s=a['state220'];stop=53046
|
| 7 |
+
if a['flags']&8 and s&32:
|
| 8 |
+
if ident not in [11,13]:ident=12
|
| 9 |
+
a['state220']&=~96;a['weapon_state']=2;return a,stop,ident
|
| 10 |
+
if s&16384 and s&32:
|
| 11 |
+
if ident not in [11,13]:ident=12
|
| 12 |
+
return a,stop,ident
|
| 13 |
+
if s&1024 or a['flags']&8:return a,stop,ident
|
| 14 |
+
if s&4 and signed(a['weapon_timer'])<300:a['weapon_timer']=300
|
| 15 |
+
if s&512:
|
| 16 |
+
if p['buttons']&1:
|
| 17 |
+
if signed(a['weapon_timer'])<=0:a['state220']&=~512;a['weapon_timer']=300
|
| 18 |
+
else:
|
| 19 |
+
if s&(256|128):return a,stop,ident
|
| 20 |
+
if signed(a['weapon_timer'])<300:a['weapon_timer']=300
|
| 21 |
+
a['state220']&=~512
|
| 22 |
+
if signed(a['weapon_timer'])<=0:stop=51207
|
| 23 |
+
return a,stop,ident
|
| 24 |
+
class WeaponGuardsTests(unittest.TestCase):
|
| 25 |
+
@classmethod
|
| 26 |
+
def setUpClass(cls):cls.r=json.loads(Path('docs/research/qvm-weapon-guards.json').read_text())
|
| 27 |
+
def test_all_exact_states_and_boundaries(self):
|
| 28 |
+
self.assertEqual(len(self.r['probes']),90)
|
| 29 |
+
for p in self.r['probes']:a,stop,ident=guard_state(p);self.assertEqual(p['final'],a);self.assertEqual(p['execution']['stop'],stop);self.assertEqual(p['final_local_id'],ident)
|
| 30 |
+
def test_literal_assignment_not_equality_for_id12(self):
|
| 31 |
+
rows=[p for p in self.r['probes'] if p['initial']['state220']&32 and (p['initial']['flags']&8 or p['initial']['state220']&16384)];self.assertTrue(rows)
|
| 32 |
+
for p in rows:self.assertEqual(p['execution']['stop'],53046);self.assertEqual(p['final_local_id'],p['initial_local_id'] if p['initial_local_id'] in [11,13] else 12)
|
| 33 |
+
def test_no_child_or_packed_weapon_mutation(self):
|
| 34 |
+
for p in self.r['probes']:self.assertEqual(p['execution']['calls'],[]);self.assertEqual(p['initial']['weapons'],p['final']['weapons']);self.assertEqual(p['initial']['queue'],p['final']['queue'])
|
| 35 |
+
def test_positive_floor_and_bit512_priority(self):
|
| 36 |
+
self.assertTrue(any(p['initial']['state220']==4 and p['final']['weapon_timer']==300 for p in self.r['probes']));self.assertTrue(any(p['initial']['state220']==512 and p['final']['state220']==0 for p in self.r['probes']))
|
| 37 |
+
if __name__=='__main__':unittest.main()
|