P08-02: 旧配置の安全な変更
This commit is contained in:
@@ -14,8 +14,13 @@ import check_p08_migration_evidence as c
|
||||
|
||||
ROOT = Path(__file__).resolve().parent.parent
|
||||
BANNER = 'Godot Engine v4.7.2.stable.official.ed1daf0bf - https://godotengine.org\n'
|
||||
R04 = json.loads((ROOT/'docs/plans/original-completion-2026-10-02/execution/p08-02/ROOT_REPAIR_INPUT-r04.json').read_text())
|
||||
ACTUAL = R04['actual_readonly_diagnostics']
|
||||
REQUESTS = ACTUAL['CARGO_ENDPOINT_READONLY_DIAGNOSTIC.json'][0]['requests']
|
||||
REVISIONS = ACTUAL['ACTUAL_WORLD_REVISION_DELTAS.json'][0]
|
||||
|
||||
def typed(value):
|
||||
if isinstance(value,tuple): return {'type':'Vector3i','text':'Vector3i('+', '.join(map(str,value))+')'}
|
||||
if isinstance(value,dict): return {'type':'Dictionary','entries':[[typed(k),typed(v)] for k,v in value.items()]}
|
||||
if isinstance(value,list): return {'type':'Array','element_type':0,'element_class':'','items':[typed(x) for x in value]}
|
||||
kind = 'Nil' if value is None else 'bool' if type(value) is bool else 'int' if type(value) is int else 'String'
|
||||
@@ -46,19 +51,24 @@ class Proof:
|
||||
self.put(prefix+name+'.var',json.dumps(node).encode())
|
||||
def save(self): (self.path/'index.json').write_text(json.dumps(self.index))
|
||||
def state(self,operations):
|
||||
jobs = [{'job_id':j,'state':'PLANNED' if j=='j2' else 'BUILDING','progress':0 if j=='j2' else 466,'reserved_cells':[[x,2,0] for x in range(offset,offset+6)],'construction_cost':{'gold':1}}
|
||||
jobs = [{'job_id':j,'state':'PLANNED' if j=='j2' else 'BUILDING','progress':0 if j=='j2' else 1266,'reserved_cells':[(x,2,0) for x in range(offset,offset+6)],'construction_cost':{'gold':1}}
|
||||
for j,offset in [('j1',0),('j2',10)]]
|
||||
facilities = [{'instance_id':'dorm','occupied_cells':[[30,0,1]]},{'instance_id':'clinic','occupied_cells':[[31,0,1]]}]
|
||||
facilities += [{'instance_id':'facility_00000005','occupied_cells':[(32,0,1)]},
|
||||
{'instance_id':'facility_00000006','occupied_cells':[(33,0,1)]}]
|
||||
guests = [{'guest_id':g} for g in ['p08.walker']+['g'+str(n) for n in range(5)]]
|
||||
staff = [{'staff_id':'patient','health':0},{'staff_id':'carrier'},{'staff_id':'staff_gina','carried_inventory_id':'cargo'}]
|
||||
staff = [{'staff_id':'patient','health':0},{'staff_id':'carrier'},{'staff_id':'staff_gina','carried_inventory_id':'cargo:staff_gina'}]
|
||||
r = {'operations':{'enabled':operations},'guests':{'guests':guests},'staff':{'staff':staff,'tasks':[
|
||||
{'task_id':'rescue','target_entity_id':'patient','rescue_facility_id':'clinic','state':'MOVING','assigned_staff_id':'carrier'}]},
|
||||
'construction':{'jobs':jobs,'facilities':facilities},'facilities':{'instances':copy.deepcopy(facilities)},
|
||||
'world':{'occupancies':[{'entity_id':j['job_id'],'cells':copy.deepcopy(j['reserved_cells'])} for j in jobs]+
|
||||
'world':{'revisions':copy.deepcopy(REVISIONS['original_world_revisions']),'occupancies':[{'entity_id':j['job_id'],'cells':copy.deepcopy(j['reserved_cells'])} for j in jobs]+
|
||||
[{'entity_id':f['instance_id'],'cells':copy.deepcopy(f['occupied_cells'])} for f in facilities]},
|
||||
'inventory':{'inventories':[{'owner_inventory_id':x,'stacks':[{'item_id':'ingredient/raw_food','quantity':4}]} for x in ('source','dest','cargo')],
|
||||
'reservations':[{'reservation_id':'reserve'}]},
|
||||
'logistics_requests':[{'request_id':'food','source_inventory_id':'source','destination_inventory_id':'dest','assigned_carrier_id':'staff_gina','reservation_ids':['reserve'],'state':'IN_TRANSIT'}],
|
||||
'inventory':{'inventories':[{'owner_inventory_id':owner,'stacks':[{'stack_id':stack,'item_id':'ingredient/raw_food','quantity':4,'reserved_quantity':reserved}]} for owner,stack,reserved in
|
||||
[('facility.inventory:facility_00000005','stack_00000001',4),('cargo:staff_gina','stack_00000002',0)]],
|
||||
'reservations':[{'reservation_id':req['reservation_ids'][0],'requester_id':req['request_id'],
|
||||
'owner_inventory_id':req['source_inventory_id'],'stack_id':'stack_00000001','item_id':'ingredient/raw_food',
|
||||
'quantity':4,'state':'CONSUMED' if req['state']=='DELIVERING' else 'ACTIVE'} for req in REQUESTS]},
|
||||
'logistics_requests':copy.deepcopy(REQUESTS),
|
||||
'movement_credits':[{'credit':1000}],
|
||||
'elevators':{'trips':[{'request_id':'ev'+str(n),'actor_id':'g'+str(n)} for n in range(5)],'shafts':[{'boarded':['g0','g1','g2','g3']}]}}
|
||||
return {'systems':{'runtime':r}}
|
||||
@@ -82,6 +92,7 @@ class Proof:
|
||||
self.index['cases'].append({'id':name,'operations':'on' if operations else 'off','kind':'refusal' if refusal else 'positive','directory':prefix[:-1],'phases':phases})
|
||||
self.index['phases'] += phases
|
||||
identity = {'starting_job':'j2','diagnostic_budget':{'seed':42588,'food_quantity':8,'food_cost':24,'signing':1,'costs':[{'cost':{'gold':2,'materials':1}}],'initial':{'gold':0,'materials':0},'needed':{'gold':27,'materials':1},'deltas':{'gold':27,'materials':1}},'jobs':['j1','j2'],'dorm':'dorm','rescue_task':'rescue','logistics_requests':['food'],'ev_requests':['ev'+str(n) for n in range(5)]}
|
||||
identity['logistics_requests'] = [req['request_id'] for req in REQUESTS]
|
||||
original = self.state(operations)
|
||||
self.variant(prefix,'identity',identity); self.variant(prefix,'original',original); self.variant(prefix,'after-live',original)
|
||||
before = {}; after = {}
|
||||
@@ -94,6 +105,7 @@ class Proof:
|
||||
checkpoint = copy.deepcopy(original)
|
||||
if not refusal:
|
||||
r = checkpoint['systems']['runtime']
|
||||
r['world']['revisions'] = copy.deepcopy(REVISIONS['checkpoint_world_revisions'])
|
||||
for job in r['construction']['jobs']: job['reserved_cells'] = job['reserved_cells'][:4]
|
||||
for row in r['world']['occupancies'][:2]: row['cells'] = row['cells'][:4]
|
||||
events = [{'event_type':'ELEVATOR_ARRIVED','payload':{'request_id':rid}} for rid in identity['ev_requests']]
|
||||
@@ -103,7 +115,7 @@ class Proof:
|
||||
state = copy.deepcopy(checkpoint)
|
||||
if label == 'minute-160':
|
||||
state['systems']['runtime']['staff']['staff'][0]['health'] = 100
|
||||
state['systems']['runtime']['logistics_requests'][0]['state'] = 'COMPLETED'
|
||||
for req in state['systems']['runtime']['logistics_requests']: req['state'] = 'COMPLETED'
|
||||
self.variant(prefix,role+'-'+label,events if label=='events' else state)
|
||||
def os(self):
|
||||
proof = {'commit':self.index['commit'],'source_map':self.index['source_map'],'input_method':'native_os','setup':'controlled_old_save','actions':[]}
|
||||
@@ -143,6 +155,64 @@ class EvidenceTests(unittest.TestCase):
|
||||
def tearDownClass(cls): cls.temp.cleanup()
|
||||
def test_positive_reaches_whole_check(self):
|
||||
self.assertEqual(c.check(ROOT,self.proof.path),[])
|
||||
def test_strict_native_vectors(self):
|
||||
for kind,good,expected in (
|
||||
('Vector3i','Vector3i(0, -12, 6)',[0,-12,6]),
|
||||
('Vector2i','Vector2i(-2, 3)',[-2,3]),
|
||||
('Vector3','Vector3(1.5, -2e1, .25)',[1.5,-20,.25]),
|
||||
('Vector2','Vector2(0, 1e-3)',[0,.001])):
|
||||
with self.subTest(kind=kind):
|
||||
self.assertEqual(c.decode({'type':kind,'text':good}),expected)
|
||||
invalid = [good.replace(kind,'WrongVector'),good+'tail',good.replace(',',',,',1),
|
||||
good[:good.rfind(',')]+')',good.replace(')',', 4)')]
|
||||
if kind.endswith('i'): invalid += [good.replace('(', '(1.0,',1), kind+'(1.0, '+', '.join(['2']*(len(expected)-1))+')']
|
||||
for text in invalid:
|
||||
with self.subTest(text=text), self.assertRaisesRegex(ValueError,'invalid typed vector'):
|
||||
c.decode({'type':kind,'text':text})
|
||||
|
||||
def test_r04_local_deficits_through_whole_check(self):
|
||||
p = self.proof
|
||||
def mutate_state(node, change):
|
||||
state = c.decode(node); change(state['systems']['runtime']); return typed(state)
|
||||
def bad_vector(node):
|
||||
if node['type'] == 'Vector3i':
|
||||
node['text'] = 'Vector3i(0, , 1)'; return True
|
||||
children = ([n for pair in node['entries'] for n in pair] if node['type']=='Dictionary'
|
||||
else node.get('items',[]))
|
||||
return any(bad_vector(child) for child in children)
|
||||
corruptions = [
|
||||
('original','invalid typed vector',None),
|
||||
('original','orphan cargo endpoint',lambda r:r['logistics_requests'][0].update(destination_inventory_id='facility.inventory:absent')),
|
||||
('original','RESERVED missing/duplicate reservation',lambda r:r['inventory']['reservations'].pop()),
|
||||
('original','DELIVERING carrier/cargo mismatch',lambda r:r['logistics_requests'][0].update(assigned_carrier_id='patient')),
|
||||
('original','RESERVED carrier contradiction',lambda r:r['logistics_requests'][1].update(assigned_carrier_id='staff_gina')),
|
||||
('original','DELIVERING reservation quantity mismatch',lambda r:r['inventory']['reservations'][0].update(quantity=3)),
|
||||
('writer-checkpoint','support epoch/occupancy mismatch',lambda r:r['world']['revisions'].update(support=143)),
|
||||
]
|
||||
for label,message,change in corruptions:
|
||||
path = 'pairs/off-positive/'+label+'.json'
|
||||
original = (p.path/path).read_bytes()
|
||||
try:
|
||||
node = json.loads(original)
|
||||
if change: node = mutate_state(node,change)
|
||||
else: self.assertTrue(bad_vector(node))
|
||||
p.put(path,node); p.save()
|
||||
with self.subTest(message=message):
|
||||
self.assertTrue(any(message in deficit for deficit in c.check(ROOT,p.path)),message)
|
||||
finally:
|
||||
p.put(path,original); p.index = copy.deepcopy(self.baseline); p.save()
|
||||
for key in ('world','occupancy','support'):
|
||||
path = 'pairs/off-refusal/writer-checkpoint.json'
|
||||
original = (p.path/path).read_bytes()
|
||||
try:
|
||||
node = c.decode(json.loads(original)); revisions = node['systems']['runtime']['world']['revisions']
|
||||
revisions[key] += 1
|
||||
p.put(path,typed(node)); p.save()
|
||||
with self.subTest(refusal_revision=key):
|
||||
self.assertTrue(any('refusal revision changed' in deficit for deficit in c.check(ROOT,p.path)))
|
||||
finally:
|
||||
p.put(path,original); p.index = copy.deepcopy(self.baseline); p.save()
|
||||
|
||||
def test_corruptions_cannot_be_accepted(self):
|
||||
p = self.proof
|
||||
mutations = {
|
||||
|
||||
Reference in New Issue
Block a user