P08-03: 配置の通常操作受入
This commit is contained in:
@@ -901,6 +901,18 @@ def check(project_root, evidence_dir):
|
||||
require(os_proof['input_method'] == 'native_os' and os_proof['observer_mutations'] == [], 'fake OS provenance')
|
||||
os_pids = [os_phase(n) for n in os_proof['phases']]
|
||||
require(len(os_pids) == 2, 'OS separate PID pair')
|
||||
role_actions = {}
|
||||
for role, pid in zip(('writer', 'reader'), os_pids):
|
||||
actions = [document(name) for name in index['artifacts']
|
||||
if name.startswith('os-pair/') and name.endswith('/request.json')]
|
||||
actions = sorted((a for a in actions if a.get('target_pid') == pid), key=lambda a: a['sequence'])
|
||||
terminal = 'os-pair/'+role+'-terminal.json'
|
||||
if terminal in index['artifacts']:
|
||||
stopped = document(terminal)
|
||||
require(not any(a['sequence'] > stopped['sequence'] for a in actions), 'terminal OS continuation')
|
||||
require(False, 'terminal OS failure')
|
||||
require([a['sequence'] for a in actions] == list(range(1, len(actions)+1)), 'OS request sequence gap')
|
||||
role_actions[pid] = actions
|
||||
os_rows = pair_gate(os_proof['directory'], os_pids, True)
|
||||
lifecycle = []
|
||||
for role, pid in zip(('writer','reader'),os_pids):
|
||||
@@ -928,6 +940,30 @@ def check(project_root, evidence_dir):
|
||||
require(ack['target_pid'] == request['target_pid'] == observed['pid'] == expected_pid
|
||||
and ack['sequence'] == request['sequence'] == observed['sequence'], 'fake OS provenance')
|
||||
require(ack['request_sha256'] == sha(artifact(folder+'request.json')), 'OS ack request mismatch')
|
||||
if request['action'] == 'reveal_palette':
|
||||
def finite(values, size):
|
||||
return isinstance(values, list) and len(values) == size and all(
|
||||
isinstance(v, (int, float)) and not isinstance(v, bool) and math.isfinite(v) for v in values)
|
||||
def geometry(data):
|
||||
bar = data.get('bar', {})
|
||||
return (all(finite(data.get(k), 4) for k in ('target_rect', 'target_screen_rect', 'palette_rect', 'thumb_rect'))
|
||||
and finite(data.get('viewport_to_screen'), 6) and isinstance(bar, dict)
|
||||
and finite(bar.get('rect'), 4) and isinstance(bar.get('visible'), bool)
|
||||
and finite([bar.get(k) for k in ('min_value', 'max_value', 'page', 'value')], 4))
|
||||
require(geometry(request) and finite([request.get('scroll_before')], 1)
|
||||
and request.get('direction') in ('left', 'right'), 'unverified palette reveal')
|
||||
require(folder+'reveal.json' in index['artifacts'], 'unverified palette reveal')
|
||||
revealed = document(folder+'reveal.json')
|
||||
require(geometry(revealed) and revealed.get('visible_after') is True
|
||||
and finite([revealed.get('scroll_after')], 1), 'unverified palette reveal')
|
||||
x,y,w,h = revealed['target_rect']; px,py,pw,ph = revealed['palette_rect']
|
||||
require(w >= 0 and h >= 0 and px <= x and py <= y and x+w <= px+pw and y+h <= py+ph,
|
||||
'unverified palette reveal')
|
||||
delta = revealed['scroll_after'] - request['scroll_before']
|
||||
require(delta > 0 if request['direction'] == 'right' else delta < 0, 'palette reveal direction mismatch')
|
||||
following = [a for a in role_actions[expected_pid] if a['sequence'] == request['sequence']+1]
|
||||
require(len(following) == 1 and following[0]['action'] == 'button'
|
||||
and following[0].get('node') == request.get('node') and request.get('node'), 'unverified palette reveal')
|
||||
raw = document(ack['input_receipt'])
|
||||
require(raw['method'] == 'native_os' and raw['target_pid'] == expected_pid and raw['timestamp_ns'] > 0
|
||||
and raw['tool_name'] and raw['tool_call_id'], 'fake OS provenance')
|
||||
|
||||
@@ -98,6 +98,60 @@ class EvidenceTests(unittest.TestCase):
|
||||
def test_positive_whole_checker(self):
|
||||
self.assertEqual([], C.check(self.root, self.out))
|
||||
|
||||
def edit_os_request(self, folder, callback):
|
||||
self.edit(folder+'/request.json', callback)
|
||||
self.edit(folder+'/ack.json', lambda d: d.update(request_sha256=C.sha(self.out/(folder+'/request.json'))))
|
||||
|
||||
def test_reveal_missing_thumb(self):
|
||||
self.edit_os_request('os-pair/supported_connected-1', lambda d: d.pop('thumb_rect'))
|
||||
self.reject('unverified palette reveal')
|
||||
|
||||
def test_reveal_missing_bar(self):
|
||||
self.edit_os_request('os-pair/supported_connected-1', lambda d: d.pop('bar'))
|
||||
self.reject('unverified palette reveal')
|
||||
|
||||
def test_reveal_not_visible(self):
|
||||
self.edit('os-pair/supported_connected-1/reveal.json', lambda d: d.update(visible_after=False))
|
||||
self.reject('unverified palette reveal')
|
||||
|
||||
def test_reveal_target_outside(self):
|
||||
self.edit('os-pair/supported_connected-1/reveal.json', lambda d: d.update(target_rect=[110,0,20,20]))
|
||||
self.reject('unverified palette reveal')
|
||||
|
||||
def test_reveal_unchanged_scroll(self):
|
||||
self.edit('os-pair/supported_connected-1/reveal.json', lambda d: d.update(scroll_after=0))
|
||||
self.reject('palette reveal direction mismatch')
|
||||
|
||||
def test_reveal_opposite_scroll(self):
|
||||
self.edit('os-pair/supported_connected-1/reveal.json', lambda d: d.update(scroll_after=-1))
|
||||
self.reject('palette reveal direction mismatch')
|
||||
|
||||
def test_reveal_next_wrong_node(self):
|
||||
self.edit_os_request('os-pair/supported_connected-2', lambda d: d.update(node='/other'))
|
||||
self.reject('unverified palette reveal')
|
||||
|
||||
def test_reveal_next_not_button(self):
|
||||
self.edit_os_request('os-pair/supported_connected-2', lambda d: d.update(action='framing'))
|
||||
self.reject('unverified palette reveal')
|
||||
|
||||
def terminal(self, sequence):
|
||||
F.put(self.out/'os-pair/writer-terminal.json', {'pid':303, 'role':'writer',
|
||||
'sequence':sequence, 'stem':'writer-request', 'reason':'timeout'})
|
||||
F.repin(self.out)
|
||||
|
||||
def test_terminal_failure(self):
|
||||
sequences = [C.load(p)['sequence'] for p in (self.out/'os-pair').glob('*/request.json')]
|
||||
self.terminal(max(sequences))
|
||||
self.reject('terminal OS failure')
|
||||
|
||||
def test_terminal_continuation(self):
|
||||
self.terminal(1)
|
||||
self.reject('terminal OS continuation')
|
||||
|
||||
def test_os_sequence_gap(self):
|
||||
self.edit_os_request('os-pair/supported_connected-2', lambda d: d.update(sequence=999))
|
||||
self.reject('OS request sequence gap')
|
||||
|
||||
def action_row(self, label):
|
||||
rows = C.load(self.out/'pair/writer-actions.json')['actions']
|
||||
return copy.deepcopy(next(row for row in rows if row['id'] == label))
|
||||
|
||||
Reference in New Issue
Block a user