Verify bounded ordinary-input approach to the prison gate

This commit is contained in:
2026-09-19 19:21:21 +02:00
parent b7fba094d7
commit ec1071595d
90 changed files with 102832 additions and 35 deletions
+66 -13
View File
@@ -348,13 +348,15 @@ def validate_paths(root, steam, compat, profile):
refuse(f'Unexpected profile symlink: {path}')
def command_for(root, steam, compat, profile, project, packed=None, capture_prison_view=False):
def command_for(root, steam, compat, profile, project, packed=None, capture_prison_view=False, private_width=1280):
if project not in {'core', 'chat', CONTROL}:
refuse('Unsupported ordinary game project/control')
if capture_prison_view and (project == CONTROL or packed is not None):
refuse('Prison view capture requires loose successor source with its opt-in observer')
command = ['gamescope', '--backend', 'headless', '-W', '1280', '-H', '720',
'-w', '1280', '-h', '720', '-r', '30', '--',
if type(private_width) is not int or private_width not in (1280, 2560) or (private_width != 1280 and (project == CONTROL or packed is not None)):
refuse('Unsupported private headless viewport')
command = ['gamescope', '--backend', 'headless', '-W', str(private_width), '-H', '720',
'-w', str(private_width), '-h', '720', '-r', '30', '--',
str(steam/'steamapps/common/Proton - Experimental/proton'), 'run',
str(steam/'steamapps/common/Arma Reforger/ArmaReforgerSteam.exe')]
if packed is not None:
@@ -633,6 +635,10 @@ def validate_input_observer(a):
def validate_input_exercise(a):
if getattr(a, 'approach_wide_display', False) and not getattr(a, 'exercise_prison_approach', False):
refuse('Wide private viewport requires the explicit prison approach experiment')
if getattr(a, 'exercise_prison_approach', False) and not (getattr(a, 'exercise_relative_look', False) and getattr(a, 'observe_prison_gate', False)):
refuse('Prison approach requires explicit relative look and gate observation')
if getattr(a, 'exercise_relative_look', False) and not getattr(a, 'exercise_player_input', False):
refuse('Relative look requires the existing explicit keyboard exercise')
if not getattr(a, 'exercise_player_input', False):
@@ -832,7 +838,17 @@ def input_observer_result(result, content, nonce):
return result, evidence
def input_exercise_command(command, root, compat, profile, executable, run_id, host_display, display_context, look=False):
def approach_module_hashes(root):
hashes = {}
for name in ('prison_approach_proof.py', 'ordinary_gate_evidence.py', 'ordinary_look_evidence.py', 'prison_approach_evidence.py'):
path = root/'tools'/name
if path.is_symlink() or not path.is_file():
refuse('Approach requires regular local proof modules')
hashes[name] = hashlib.sha256(path.read_bytes()).hexdigest()
return hashes
def input_exercise_command(command, root, compat, profile, executable, run_id, host_display, display_context, look=False, approach=False):
"""Put the input supervisor around the display inspector, inside Gamescope."""
separator = command.index('--')
if command[:separator].count('--backend') != 1 or command[command.index('--backend')+1] != 'headless':
@@ -843,6 +859,12 @@ def input_exercise_command(command, root, compat, profile, executable, run_id, h
if (not display_context or len(child) < 3 or child[:2] != [sys.executable, str(ownership)]
or not re.fullmatch(r'[A-Za-z0-9]{1,32}', run_id)):
refuse('Input exercise requires the existing scoped display wrapper and observer nonce')
if type(approach) is not bool or (approach and not look):
refuse('Prison approach requires explicit relative look')
if approach:
indices = [i for i,value in enumerate(child) if value.startswith('-errOrdinaryGate')]
if len(indices) != 1 or child[indices[0]] != '-errOrdinaryGateObserve' or child[indices[0]+1:indices[0]+2] != [run_id]:
refuse('Prison approach requires its exact native gate observer nonce')
if type(look) is not bool:
refuse('Relative look selection must be boolean')
look_options = [i for i,value in enumerate(child) if value.startswith('-errOrdinaryLook')]
@@ -864,6 +886,10 @@ def input_exercise_command(command, root, compat, profile, executable, run_id, h
if look:
context['look_requested'] = True
wrapper.insert(-1, '--enable-private-look-proof')
if approach:
context['approach_requested'] = True
context['approach_module_sha256'] = approach_module_hashes(root)
wrapper.insert(-1, '--enable-private-approach-proof')
return command[:separator+1] + wrapper + child, context
@@ -885,6 +911,10 @@ def input_exercise_poll_failure(context):
for key in ('helper_sha256', 'ownership_helper_sha256', 'prefix', 'profile', 'game_executable', 'report', 'command', 'nonce'):
if report.get(key) != context[key]:
return False
if context.get('approach_requested') and report.get('approach_module_sha256') != context.get('approach_module_sha256'):
return None
if report.get('approach_requested', False) != context.get('approach_requested', False):
return None
if report.get('look_requested', False) != context.get('look_requested', False):
return False
return bool(report.get('error') or report.get('supervision_failed') is True or (
@@ -928,6 +958,13 @@ def input_exercise_result(result, context, session, observation, display_result)
for key in ('helper_sha256', 'ownership_helper_sha256', 'prefix', 'profile', 'game_executable', 'report', 'command', 'nonce'):
if report.get(key) != context[key]:
refuse('Input report binding mismatch: '+key)
approach_requested = context.get('approach_requested', False)
if type(report.get('approach_requested', False)) is not bool or report.get('approach_requested', False) != approach_requested:
refuse('Prison approach request does not match launch context')
if approach_requested and (report.get('approach_module_sha256') != context.get('approach_module_sha256') or context.get('approach_module_sha256') != approach_module_hashes(ROOT)):
refuse('Approach module sources changed or differ from launch hashes')
if not approach_requested and report.get('approach_proof') is not None:
refuse('Unexpected approach evidence in fixed input run')
look_requested = context.get('look_requested', False)
if type(report.get('look_requested', False)) is not bool or report.get('look_requested', False) != look_requested:
refuse('Relative look request does not match launch context')
@@ -944,11 +981,15 @@ def input_exercise_result(result, context, session, observation, display_result)
import math
events = report.get('events', [])
expected = [('W', True), ('W', False), ('Tab', True), ('Tab', False), ('Tab', True), ('Tab', False)]
if approach_requested:
if not isinstance(events, list) or not 2 <= len(events) <= 24 or len(events)%2:
refuse('Prison approach requires bounded W pairs')
expected = [('W', i%2 == 0) for i in range(len(events))]
if (not isinstance(events, list) or len(events) != len(expected)
or any(not isinstance(e, dict) or type(e.get('down')) is not bool or (e.get('key'), e['down']) != pair or e.get('finally') for e, pair in zip(events, expected))):
refuse('Input experiment does not contain the exact fixed key pairs')
times = [e.get('monotonic') for e in events]
if any(type(t) not in {int, float} or not math.isfinite(t) or t < 0 for t in times) or times != sorted(times) or times[-1]-times[0] > 25:
if any(type(t) not in {int, float} or not math.isfinite(t) or t < 0 for t in times) or times != sorted(times) or times[-1]-times[0] > (35 if approach_requested else 25):
refuse('Input event clock is invalid or outside its bound')
if not display_result or display_result.get('verified') is not True:
refuse('Input experiment lacks verified private display ownership')
@@ -958,15 +999,25 @@ def input_exercise_result(result, context, session, observation, display_result)
refuse('Input owner does not match the independently observed private game window')
native = report.get('observer', {})
if (report.get('console') != str(session/'console.log')
or native.get('events') != ['READY', 'INVENTORY_OPEN', 'INVENTORY_CLOSED']):
or native.get('events') != (['READY'] if approach_requested else ['READY', 'INVENTORY_OPEN', 'INVENTORY_CLOSED'])):
refuse('Input experiment lacks the exact native session/transition handshake')
if (not observation or observation.get('nonce') != context['nonce'] or not observation.get('sampler_complete')
or not observation.get('inventory_open_observed') or not observation.get('inventory_close_observed')
or not observation.get('movement', {}).get('action_observed')
or (not approach_requested and (not observation.get('inventory_open_observed') or not observation.get('inventory_close_observed')))
or (not approach_requested and (not observation.get('movement', {}).get('action_observed')
or not observation.get('movement', {}).get('physical_motion_observed')
or not observation.get('movement', {}).get('released_input_observed')):
or not observation.get('movement', {}).get('released_input_observed')))):
refuse('Native movement and inventory open/close proof is incomplete')
if look_requested:
if approach_requested:
from prison_approach_evidence import validate_approach_evidence
route = report.get('approach_proof', {})
route_events = route.get('events', [])
if len(route_events) != len(events) or any(not isinstance(a, dict) or a.get('key') != b.get('key') or a.get('down') is not b.get('down') or type(a.get('monotonic')) not in (int, float) or not math.isfinite(a['monotonic']) or abs(a['monotonic']-b['monotonic']) > .01 for a,b in zip(route_events,events)):
refuse('Approach adapter and route key evidence differ')
proof['approach_proof'] = validate_approach_evidence((session/'console.log').read_text(errors='replace'), context['nonce'], report.get('look_proof'), route)
proof['scope'] = 'private relative mouse and W input with independently observed interior prison approach; no held action, gate exit, full escape or multiplayer qualification'
if proof['approach_proof'].get('verified') is not True:
refuse('Independent native prison approach evidence is incomplete')
elif look_requested:
from ordinary_look_evidence import validate_look_evidence
proof['look_proof'] = validate_look_evidence((session/'console.log').read_text(errors='replace'), context['nonce'], report.get('look_proof'), events)
proof['scope'] = 'bounded private relative mouse and keyboard delivery with native view/movement/inventory evidence; no gate, full escape or multiplayer qualification'
@@ -1013,7 +1064,7 @@ def run(a):
def run_locked(a, steam, compat, profile, packed=None):
command = command_for(ROOT, steam, compat, profile, a.project, packed, getattr(a, 'capture_prison_view', False))
command = command_for(ROOT, steam, compat, profile, a.project, packed, getattr(a, 'capture_prison_view', False), 2560 if getattr(a, 'approach_wide_display', False) else 1280)
separator = command.index("--")
proton, executable = Path(command[separator+1]), Path(command[separator+3])
if not proton.is_file() or not executable.is_file():
@@ -1047,7 +1098,7 @@ def run_locked(a, steam, compat, profile, packed=None):
command, display_context = display_probe_command(command, ROOT, compat, profile, executable, run_id, os.environ.get('DISPLAY', ''))
exercise_context = None
if getattr(a, 'exercise_player_input', False):
command, exercise_context = input_exercise_command(command, ROOT, compat, profile, executable, run_id, os.environ.get('DISPLAY', ''), display_context, getattr(a, 'exercise_relative_look', False))
command, exercise_context = input_exercise_command(command, ROOT, compat, profile, executable, run_id, os.environ.get('DISPLAY', ''), display_context, getattr(a, 'exercise_relative_look', False), getattr(a, 'exercise_prison_approach', False))
silent_sink = 'erri_game_'+run_id
application_id = 'org.escapistsrereintroduced.game.'+run_id
env = os.environ.copy()
@@ -1173,7 +1224,7 @@ def run_locked(a, steam, compat, profile, packed=None):
'failure_observed_during_polling': monitor.first_failure_at is not None,
'first_failure_elapsed_seconds': (monitor.first_failure_at - monitor.started) if monitor.first_failure_at is not None else None,
'executable': str(executable), 'dedicated_server': False,
'display': 'headless Gamescope', 'audio': '-noSound plus own silent sink',
'display': 'headless Gamescope', 'private_viewport': {'width':2560 if getattr(a, 'approach_wide_display', False) else 1280,'height':720}, 'audio': '-noSound plus own silent sink',
'control_only': a.project == CONTROL,
'source_manifest_scope': 'none: packed input provenance is separate; loose source manifests cannot qualify this run' if packed else 'successor addon snapshot retained for provenance bookkeeping only; none loaded by vanilla control' if a.project == CONTROL else 'successor addon sources',
'scope': final.get('scope', 'ordinary game compile/startup; no scripted fixture, full escape or multiplayer acceptance')}
@@ -1211,6 +1262,8 @@ def main():
parser.add_argument('--inspect-headless-display', action='store_true', help='Read-only ownership inspection within the hidden display; no input sent, loose Core/Chat only')
parser.add_argument('--capture-prison-view', action='store_true', help='Opt-in single genuine local prison screenshot; loose successor source only, no camera or input changes')
parser.add_argument('--observe-player-input', action='store_true', help='Read-only native local-input sampler; loose Core/Chat only, requires --settle >=50; sends no input')
parser.add_argument('--approach-wide-display', action='store_true', help='Use a disposable 2560x720 headless viewport for the explicit approach experiment; no physical display changes')
parser.add_argument('--exercise-prison-approach', action='store_true', help='Explicit bounded relative-mouse/W approach inside prison; requires relative look and gate observer; never operates gate')
parser.add_argument('--observe-prison-gate', action='store_true', help='Read-only native registered prison lock/context/progress telemetry; requires --observe-player-input')
parser.add_argument('--exercise-relative-look', action='store_true', help='Explicit tiny relative right/return mouse experiment before W/Tab; requires --exercise-player-input and native view proof')
parser.add_argument('--exercise-player-input', action='store_true', help='Explicit bounded private W/Tab keyboard experiment; requires observer, display inspection, loose Core/Chat and settle >=50')