Qualify route gain and observe native gate context through ordinary input

This commit is contained in:
2026-09-19 19:44:22 +02:00
parent ec1071595d
commit 53eaf3894d
72 changed files with 71413 additions and 36 deletions
+133 -21
View File
@@ -117,9 +117,10 @@ def yaw_delta(first, second):
class RelativeMouse:
def __init__(self, query, keyboard, owner, enable_approach=False):
def __init__(self, query, keyboard, owner, enable_approach=False, enable_alignment=False):
self.query, self.keyboard, self.owner = query, keyboard, owner
self.enable_approach = enable_approach
self.enable_alignment = enable_alignment
self.xt = keyboard.xt
self.xt.XTestFakeRelativeMotionEvent.restype = C.c_int
self.xt.XTestFakeRelativeMotionEvent.argtypes = [C.c_void_p, C.c_int, C.c_int, C.c_ulong]
@@ -163,6 +164,16 @@ class RelativeMouse:
within_lease()
self._move(dx, dy, guard, before_send=within_lease)
def alignment_move(self, dx, dy, guard, *, deadline, clock=time.monotonic):
if (self.enable_alignment is not True or self.enable_approach is not True or type(dx) is not int
or not 1 <= abs(dx) <= 128 or type(dy) is not int or dy != 0):
raise Refused('Unsupported or unenabled alignment relative step')
def within_lease():
if clock() >= deadline:
raise Refused('Original alignment deadline exceeded before relative input')
within_lease()
self._move(dx, dy, guard, before_send=within_lease)
def _move(self, dx, dy, guard, before_send=None):
target = guard.check()
self.keyboard.require_idle()
@@ -293,7 +304,7 @@ class Observer:
self.enable_look = enable_look
self.enable_approach = enable_approach
self.approach_lines = []; self.gate_records = []; self.gate_received = {}
self.gate_complete_end = 0; self.approach_previous = None
self.gate_complete_end = 0; self.approach_previous = None; self.alignment_previous = None
self.ready_at = self.ready_real = self.queued_real = None
self.look_records = []; self.input_samples = []; self.ready_sample = None
@@ -414,21 +425,29 @@ class Observer:
return content, data
def approach_snapshots(self):
return self._snapshot_batch('approach_previous')
def alignment_snapshots(self):
return self._snapshot_batch('alignment_previous')
def _snapshot_batch(self, cursor):
_, data = self.approach_data()
snapshots = data['snapshots']; latest = max(snapshots)
first = latest if self.approach_previous is None else min(self.approach_previous + 1, latest)
if self.approach_previous is not None and latest < self.approach_previous:
previous = getattr(self, cursor)
first = latest if previous is None else min(previous + 1, latest)
if previous is not None and latest < previous:
raise Refused('Native approach snapshot index reversed')
result = []
for index in range(first, latest + 1):
item = snapshots[index]; geo, action, native, pose = item['geometry'], item['action'], item['input'], item['look']
result.append({'sample': index, 'real_ms': item['real_ms'], 'received_monotonic': self.gate_received[index],
'origin': native['origin'], 'direction': pose['direction'], 'body_forward': pose['body_forward'],
'origin': native['origin'], 'direction': pose['direction'], 'body_forward': pose['body_forward'], 'camera': pose['camera'],
**{key: action[key] for key in ('selected', 'context_selected', 'cached_performable', 'progress_bound', 'pressed', 'progress', 'performing', 'duration')},
'input': native['input'], 'forward': native['forward'], 'velocity': native['velocity'],
'inventory': native['inventory'], 'phase': action['phase'], 'door_state': geo['door_state'], 'door_control': geo['door_control'],
'geometry': {'nonce': self.nonce, 'basis': 'registered-prison-lock-native-interaction',
**{key: geo[key] for key in ('prison', 'gate', 'gate_forward', 'context', 'radius')}}})
self.approach_previous = latest
setattr(self, cursor, latest)
return result
@@ -524,9 +543,12 @@ def confirm_release(guard, keyboard, report, key, clock=time.monotonic, sleep=ti
observation['end'] = clock()
def sequence(guard, keyboard, observer, report, clock=time.monotonic, sleep=time.sleep, mouse=None, approach=False):
def sequence(guard, keyboard, observer, report, clock=time.monotonic, sleep=time.sleep, mouse=None, approach=False, alignment=False):
deadline = clock() + 70
held = None
approach_completed = False
if alignment and (not approach or mouse is None):
raise Refused("Alignment requires approach and look capabilities")
report.update(events=[], paired_release_errors=[], sequence_completed=False)
def check(native=True):
@@ -596,6 +618,10 @@ def sequence(guard, keyboard, observer, report, clock=time.monotonic, sleep=time
if clock() >= deadline:
raise Refused("Original approach deadline exceeded")
keyboard.require_idle()
approach_completed = True
if alignment:
notify({'kind': 'approach-finished', 'successful': True})
run_alignment(guard, keyboard, observer, mouse, report, clock, sleep)
report["sequence_completed"] = True
return
pulse('W', 0.30)
@@ -630,7 +656,7 @@ def sequence(guard, keyboard, observer, report, clock=time.monotonic, sleep=time
route = report.get('approach_proof', {})
report['paired_release_errors'].extend(route.get('paired_release_errors', []))
report['unconfirmed_held_key'] = route.get('unconfirmed_held_key')
notify({'kind': 'approach-finished', 'successful': report['sequence_completed']})
notify({'kind': 'approach-finished', 'successful': approach_completed if alignment else report['sequence_completed']})
report['all_attempted_presses_released'] = not report.get('unconfirmed_held_key') and not report['paired_release_errors']
@@ -691,10 +717,71 @@ def run_approach(guard, keyboard, observer, mouse, report, clock, sleep):
raise Refused('Approach module did not complete')
class AlignmentMouse:
"""Explicit yaw-only capability. Caller cannot extend its original lease."""
def __init__(self, mouse, report, clock, deadline):
self.mouse, self.report, self.clock, self.deadline = mouse, report, clock, deadline
def move(self, dx, dy, guard, *, deadline):
if type(deadline) not in (int, float) or not math.isfinite(deadline) or deadline > self.deadline:
raise Refused('Alignment module attempted to extend original deadline')
self.report['input_attempted'] = True
self.mouse.alignment_move(dx, dy, guard, deadline=deadline, clock=self.clock)
self.report['input_sent'] = True
def run_alignment(guard, keyboard, observer, mouse, report, clock, sleep):
from prison_alignment_proof import perform_alignment
from prison_approach_evidence import validate_approach_evidence
if observer.ready_at is None or observer.ready_real is None or observer.queued_real is None:
raise Refused('Original native alignment clocks missing')
original_end = observer.ready_at + (45000 - (observer.ready_real - observer.queued_real))/1000 - .5
started = clock()
deadline = min(started + 8, original_end)
if deadline <= started:
raise Refused('Original observation lifetime unavailable for alignment')
notify({'kind': 'alignment-pending', 'deadline_monotonic': deadline})
successful = False
try:
content, data = observer.approach_data()
route = report.get('approach_proof')
evidence = validate_approach_evidence(content, observer.nonce, report['look_proof'], route)
if not evidence['verified']:
raise Refused('Native approach prerequisite refused: ' + '; '.join(evidence['errors']))
if clock() >= deadline:
raise Refused('Original alignment deadline exceeded during prerequisite validation')
poses = {r['sample']: r for r in observer.look_records if r['kind'] == 'sample'}
look = report['look_proof']
calibration = {'nonce': observer.nonce, 'completed': True, 'pixels': 24,
'baseline_direction': poses[look['baseline_samples'][-1]]['direction'],
'response_direction': poses[look['response_sample']]['direction'],
'ready_real_ms': observer.ready_real, 'ready_monotonic': observer.ready_at,
'observer_start_real_ms': observer.queued_real, 'observer_deadline_monotonic': original_end,
'alignment_deadline_monotonic': deadline}
observer.alignment_previous = evidence['last_sample'] - 1
def alignment_notify(event):
if event.get('kind') != 'alignment-finished':
notify(event)
# Native ownership checks remain live, but no keyboard-sending capability
# is handed to this stage. It may only assert that all keys are idle.
perform_alignment(guard, keyboard.require_idle, AlignmentMouse(mouse, report, clock, deadline),
observer.alignment_snapshots, route['geometry'], calibration, evidence, report,
clock=clock, sleep=sleep, notify=alignment_notify)
if not report.get('alignment_proof', {}).get('completed'):
raise Refused('Alignment module did not complete')
guard.check(); keyboard.require_idle()
observer.approach_data()
if clock() >= deadline:
raise Refused('Original alignment deadline exceeded at final observation')
successful = True
finally:
notify({'kind': 'alignment-finished', 'successful': successful})
def arguments(argv=None):
p = argparse.ArgumentParser(description=__doc__)
p.add_argument('--enable-private-keyboard-proof', action='store_true', required=True)
p.add_argument('--enable-private-look-proof', action='store_true')
p.add_argument('--enable-private-alignment-proof', action='store_true', help='After successful approach, attempt stationary native lock selection using horizontal relative mouse only')
p.add_argument('--enable-private-approach-proof', action='store_true', help='After look calibration, replace W/Tab cycle with bounded interior approach only')
for name in ('prefix', 'game-executable', 'profile', 'report'):
p.add_argument('--' + name, type=Path, required=True)
@@ -703,6 +790,8 @@ def arguments(argv=None):
p.add_argument('--nonce', required=True)
p.add_argument('command', nargs=argparse.REMAINDER)
args = p.parse_args(argv)
if args.enable_private_alignment_proof and not (args.enable_private_approach_proof and args.enable_private_look_proof):
p.error("Alignment proof requires explicit approach and look proof")
if args.enable_private_approach_proof and not args.enable_private_look_proof:
p.error("Approach proof requires explicit private look proof")
return args
@@ -761,9 +850,16 @@ APPROACH_MODULES = ('prison_approach_proof.py', 'ordinary_gate_evidence.py',
'ordinary_look_evidence.py', 'prison_approach_evidence.py')
ALIGNMENT_MODULES = ('prison_alignment_proof.py', 'prison_alignment_evidence.py')
def approach_module_hashes():
return module_hashes(APPROACH_MODULES)
def module_hashes(names):
folder = Path(__file__).resolve().parent
return {name: hashlib.sha256((folder / name).read_bytes()).hexdigest() for name in APPROACH_MODULES}
return {name: hashlib.sha256((folder / name).read_bytes()).hexdigest() for name in names}
def report_for_write(report):
@@ -771,14 +867,18 @@ def report_for_write(report):
if report.get('approach_requested') is not True:
return report
result = dict(report)
route = report.get('approach_proof')
if isinstance(route, dict):
result['approach_proof'] = dict(route)
samples = route.get('samples')
if isinstance(samples, list):
result['approach_proof']['samples'] = [
{key: sample.get(key) for key in ('sample', 'real_ms', 'received_monotonic')}
if isinstance(sample, dict) else {'invalid_reference': True} for sample in samples]
names = ['approach_proof']
if report.get('alignment_requested') is True:
names.append('alignment_proof')
for name in names:
route = report.get(name)
if isinstance(route, dict):
result[name] = dict(route)
samples = route.get('samples')
if isinstance(samples, list):
result[name]['samples'] = [
{key: sample.get(key) for key in ('sample', 'real_ms', 'received_monotonic')}
if isinstance(sample, dict) else {'invalid_reference': True} for sample in samples]
return result
@@ -795,6 +895,7 @@ def main(argv=None):
report = {'schema': 1, 'kind': 'headless-private-keyboard-experiment', 'input_sent': False,
'look_requested': args.enable_private_look_proof,
'approach_requested': getattr(args, 'enable_private_approach_proof', False),
'alignment_requested': getattr(args, 'enable_private_alignment_proof', False),
'sequence_completed': False, 'release_qualified': False, 'gameplay_qualified': False,
'hard_supervision': NOTIFY is not None, 'nonce': args.nonce, 'binding': bound, 'command': command, 'report': str(args.report), 'input_attempted': False,
'helper_sha256': hashlib.sha256(Path(__file__).read_bytes()).hexdigest(),
@@ -802,6 +903,8 @@ def main(argv=None):
'prefix': str(args.prefix), 'profile': str(args.profile), 'game_executable': str(args.game_executable)}
if report['approach_requested']:
report['approach_module_sha256'] = approach_module_hashes()
if report['alignment_requested']:
report['alignment_module_sha256'] = module_hashes(ALIGNMENT_MODULES)
fd = os.open(args.report, os.O_WRONLY | os.O_CREAT | os.O_EXCL | os.O_NOFOLLOW, 0o600)
info = os.fstat(fd)
notify({'kind': 'report', 'path': str(args.report), 'device': info.st_dev, 'inode': info.st_ino,
@@ -866,8 +969,8 @@ def main(argv=None):
guard.check()
keyboard = Keyboard(query)
keyboard.require_idle()
mouse = RelativeMouse(query, keyboard, owner, enable_approach=getattr(args, "enable_private_approach_proof", False)) if args.enable_private_look_proof else None
sequence(guard, keyboard, observer, report, mouse=mouse, approach=getattr(args, "enable_private_approach_proof", False))
mouse = RelativeMouse(query, keyboard, owner, enable_approach=getattr(args, "enable_private_approach_proof", False), enable_alignment=getattr(args, "enable_private_alignment_proof", False)) if args.enable_private_look_proof else None
sequence(guard, keyboard, observer, report, mouse=mouse, approach=getattr(args, "enable_private_approach_proof", False), alignment=getattr(args, "enable_private_alignment_proof", False))
except (OSError, ValueError, UnicodeError, subprocess.SubprocessError) as error:
report['error'] = str(error)
finally:
@@ -972,6 +1075,7 @@ def supervise(argv=None):
os.kill(child.pid, signum)
for signum in (signal.SIGINT, signal.SIGTERM, signal.SIGHUP):
previous[signum] = signal.signal(signum, forward)
alignment_deadline = None; alignment_finished = False
approach_deadline = None; approach_finished = False
look_at = None; held_at = None; held_key = None; owned_child = None; report_record = None; proof_finished = False; expired = False
deadline = time.monotonic() + 190
@@ -983,7 +1087,15 @@ def supervise(argv=None):
event = parent.recv()
except EOFError:
event = None
if event and event['kind'] == 'approach-pending':
if event and event['kind'] == 'alignment-pending':
proposed = event.get('deadline_monotonic')
current = time.monotonic()
if type(proposed) not in (int, float) or not math.isfinite(proposed) or proposed > current + 8:
proposed = current
alignment_deadline = proposed if alignment_deadline is None else min(alignment_deadline, proposed)
elif event and event['kind'] == 'alignment-finished':
alignment_finished = bool(event.get('successful'))
elif event and event['kind'] == 'approach-pending':
proposed = event.get('deadline_monotonic')
current = time.monotonic()
if type(proposed) not in (int, float) or not math.isfinite(proposed) or proposed > current + 35:
@@ -1006,7 +1118,7 @@ def supervise(argv=None):
elif event and event['kind'] == 'proof-finished':
proof_finished = event['successful']
now = time.monotonic()
if received or now >= deadline or (approach_deadline is not None and not approach_finished and now >= approach_deadline) or (held_at is not None and now - held_at > 1.5) or (look_at is not None and now - look_at > 4):
if received or now >= deadline or (alignment_deadline is not None and not alignment_finished and now >= alignment_deadline) or (approach_deadline is not None and not approach_finished and now >= approach_deadline) or (held_at is not None and now - held_at > 1.5) or (look_at is not None and now - look_at > 4):
expired = True
child.terminate() # asks worker finally to release if still provable
child.join(1)