P08-03: 配置の通常操作受入

This commit is contained in:
Agent Control Runner
2026-10-14 03:52:45 +09:00
parent 7dd450c64c
commit c2b59f04f9
13 changed files with 409 additions and 53 deletions
@@ -316,6 +316,20 @@ def check(project_root, evidence_dir):
process = document(directory+'/'+role+'-process.json')
require(process['pid'] == pid and process['failures'] == 0 and process['checks'] > 0, 'process attribution')
require(bool(process['engine']['hash']) and bool(process['engine']['string']), 'engine version')
save_log = document(directory+'/'+role+'-save-results.json')
require(save_log['pid'] == pid and save_log['records'], 'missing actual save results')
operations = []
for stem in save_log['records']:
record = document(directory+'/'+stem+'.json')
raw = artifact(directory+'/'+stem+'.var')
require(record['pid'] == pid and record['raw_sha256'] == sha(raw), 'save result attribution/SHA')
path = record['requested_path']
require(path.startswith('user://') and record['absolute_path'] == str(Path(record['user_data_dir'])/path[7:]), 'save outside private user profile')
result = decode(record['result'])
require(isinstance(result, dict) and result.get('ok') is True, 'actual save/load failed')
if record['operation'] == 'runtime_operation':
operations.append(result.get('operation'))
require(('save' in operations) if role == 'writer' else any(op in ('load','recovery','recovery_candidate') for op in operations), 'missing actual normal save/load result')
doc = document(directory+'/'+role+'-actions.json')
require(doc['setup'] == 'diagnostic', 'unlabelled diagnostic')
expected = WRITER if role == 'writer' else ['resume_overlap']
@@ -361,6 +375,30 @@ def check(project_root, evidence_dir):
require(set(saved['members']) == set(MEMBERS) and saved['pid'] == expected_pids[0], 'missing formal members/writer')
for member in MEMBERS:
require(saved['members'][member] == sha(artifact(directory+'/formal/'+member)), 'formal member changed')
exported = document(directory+'/formal-export.json')
require(exported['pid'] == expected_pids[0] and exported['requested_path'] == 'user://saves/manual_ui', 'formal export attribution')
absolute = str(Path(exported['user_data_dir'])/'saves/manual_ui')
require(exported['absolute_path'] == absolute, 'save outside private user profile')
for name in ('formal','formal.bak'):
package = exported['packages'][name]
require(package['present'] and package['source_path'] == absolute+('.bak' if name.endswith('.bak') else '') and set(package['members']) == set(MEMBERS), 'missing formal backup export')
for member, pin in package['members'].items():
file = artifact(directory+'/'+name+'/'+member)
require(pin == {'sha256':sha(file),'bytes':file.stat().st_size}, 'formal export SHA')
metadata = exported['slot_metadata']
require(metadata['file'] == 'formal-slot-metadata.json' and metadata['sha256'] == sha(artifact(directory+'/'+metadata['file'])) == saved['members']['manifest.json'], 'slot metadata SHA')
if not native_os:
initial = document(directory+'/initial-formal-export.json')
initial_absolute = str(Path(initial['user_data_dir'])/'saves/p08_initial')
require(initial['pid'] == expected_pids[0] and initial['requested_path'] == 'user://saves/p08_initial' and initial['absolute_path'] == initial_absolute, 'initial formal private path')
package = initial['packages']['initial-formal']
require(package['present'] and package['source_path'] == initial_absolute and set(package['members']) == set(MEMBERS), 'initial formal members')
for member, pin in package['members'].items():
file = artifact(directory+'/initial-formal/'+member)
require(pin == {'sha256':sha(file),'bytes':file.stat().st_size}, 'initial formal SHA')
require(initial['packages']['initial-formal.bak'] == {'present':False,'source_path':initial_absolute+'.bak','members':{}}, 'unexpected initial backup')
metadata = initial['slot_metadata']
require(metadata['file'] == 'initial-formal-slot-metadata.json' and metadata['sha256'] == sha(artifact(directory+'/'+metadata['file'])) == package['members']['manifest.json']['sha256'], 'initial slot metadata SHA')
facilities = keyed(decode(checkpoint)['systems']['runtime']['construction']['facilities'], 'instance_id')
require(saved['instance_id'] in facilities and facilities[saved['instance_id']]['origin'] == saved['cell'], 'wrong resume instance')
by_id = {r['id']: r for r in rows}
+18 -4
View File
@@ -124,15 +124,27 @@ class EvidenceTests(unittest.TestCase):
def test_wrong_resume(self):
path = 'pair/reader-checkpoint.json'
original = (self.out/path).read_bytes()
model = C.decode(json.loads((self.out/path).read_text()))
model['clock']['absolute_minute'] += 1
F.put(self.out/path, F.typed(model)); F.repin(self.out)
self.reject('wrong resume model')
(self.out/path).write_bytes(original); F.repin(self.out)
self.edit('pair/reader-save-result-0001.json', lambda d: d.update(result=F.typed({'ok':False,'operation':'load','code':'UNSAFE_PATH'})))
self.reject('actual save/load failed')
self.edit('pair/reader-save-result-0001.json', lambda d: d.update(result=F.typed({'ok':True,'operation':'load'}), requested_path='/outside/formal'))
self.reject('save outside private user profile')
def test_missing_formal_member(self):
(self.out/'pair/formal/journal.log').unlink()
F.repin(self.out)
self.reject('journal.log')
for name in ('formal','formal.bak'):
with self.subTest(package=name):
file = self.out/('pair/'+name+'/journal.log')
original = file.read_bytes()
file.unlink(); F.repin(self.out)
self.reject('journal.log')
file.write_bytes(original); F.repin(self.out)
self.edit('pair/formal-export.json', lambda d: d['slot_metadata'].update(sha256='0'*64))
self.reject('slot metadata SHA')
def test_missing_model_member(self):
path = 'pair/writer-unsupported-before.json'
@@ -238,7 +250,9 @@ class EvidenceTests(unittest.TestCase):
private_project=C.runner.private_project, fresh_environment=C.runner.fresh_environment,
SUPPORT=ROOT/'tools/regression', module=lambda *a:SimpleNamespace(ordinary_gate=lambda *a:{'mock':True}))
outer = SimpleNamespace(native=SimpleNamespace(helper={'live_engines':lambda:[]}),finish_captured=cleanup)
with patch.object(D.subprocess,'Popen',side_effect=spawn), contextlib.redirect_stdout(io.StringIO()):
# Disk admission is an environmental input to this fully mocked collector.
# Preserve the real collector's gate; make this fixture independent of host free space.
with patch.object(D.shutil,'disk_usage',return_value=SimpleNamespace(free=1024**3)), patch.object(D.subprocess,'Popen',side_effect=spawn), contextlib.redirect_stdout(io.StringIO()):
if fail_wait:
with self.assertRaisesRegex(AssertionError,'native OS collection failed'):
D.collect_os(r,self.root,output,stage,str(Path(self.temp.name)/'synthetic-engine-not-executable'),'1'*40,refs,protected,outer)