Compare commits
3
Commits
bb34f443c4
...
d1c7ce27d2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d1c7ce27d2 | ||
|
|
1545bd42d6 | ||
|
|
592809bfda |
+107
-19
File diff suppressed because one or more lines are too long
@@ -5,7 +5,7 @@
|
||||
"base_commit": "e196578c1a4ad2fdf84642565bd102e524be386f",
|
||||
"spec_sha256_stripped_utf8": "19f02bb7912a4d7354e565186f02366fc1d4dd698468d26142e8204fd14ebfb0",
|
||||
"spec_sha256_raw": "01b3600e8815b514ffd5c4d341d77a6a3e8341064796c1dae706062b746ba6aa",
|
||||
"scope": "Repair r04: definition-derived diagnostic deficit, three construction stages in one batch, minimal checker mutations and compact verified history references; no production/native/Runner/git writes.",
|
||||
"scope": "Metadata-only synchronization against Git 1545bd42; no code, fixture, runtime, marker or acceptance changes.",
|
||||
"outputs": [
|
||||
{
|
||||
"path": "docs/plans/original-completion-2026-10-02/execution/completion_marker_contracts.json",
|
||||
@@ -27,13 +27,13 @@
|
||||
},
|
||||
{
|
||||
"path": "tests/fixtures/p08-02/README.md",
|
||||
"sha256": "8a6b8dcde00be86e1ff96b29b255b586366e759a561b03e060b28e8713e210e0",
|
||||
"sha256": "e88ca33fdb4e96602d1fb108d71a9c35b6fa054619ecf6f1396e9a6c5e0ebd98",
|
||||
"hash_scope": "raw bytes",
|
||||
"disposition": "r04 current source; retained paths and UID/marker entries"
|
||||
},
|
||||
{
|
||||
"path": "tests/fixtures/p08-02/mixed_world.gd",
|
||||
"sha256": "e084d7aa6baf122089bd6f717af7e1e62de3c9930288439e6f51cc524bb55dde",
|
||||
"sha256": "a23f8a686be1d77e4874b9956eef89be4756f6e5eb01cc62454c3845acc62c25",
|
||||
"hash_scope": "raw bytes",
|
||||
"disposition": "r04 current source; retained paths and UID/marker entries"
|
||||
},
|
||||
@@ -57,13 +57,13 @@
|
||||
},
|
||||
{
|
||||
"path": "tools/check_p08_migration_evidence.py",
|
||||
"sha256": "c6edc43b114a64f5dedaafbfde47516baa265978c7aa836296c6bf5fe7a26857",
|
||||
"sha256": "928353233bf85436a5aaf8c4f77f3f82472d94b8ad2646cb1b030baf92b3b5dc",
|
||||
"hash_scope": "raw bytes",
|
||||
"disposition": "r04 current source; retained paths and UID/marker entries"
|
||||
},
|
||||
{
|
||||
"path": "tools/test_p08_migration_evidence.py",
|
||||
"sha256": "22e75b2d4e79e7b6f8d7990c1eb14917565ca0e60197435716588ebaef3e09ea",
|
||||
"sha256": "fd9ef7ec63f53dfd5e04e261606c9303d01a2e6590e22a35fef2e35df1429280",
|
||||
"hash_scope": "raw bytes",
|
||||
"disposition": "r04 current source; retained paths and UID/marker entries"
|
||||
},
|
||||
@@ -75,7 +75,7 @@
|
||||
},
|
||||
{
|
||||
"path": "docs/plans/original-completion-2026-10-02/execution/p08-02/implementation.json",
|
||||
"sha256": "7783f882e49c86432ea286293fd691d7d0abbec3847a7759c814e67436645e62",
|
||||
"sha256": "99bf42766caec72f3fdeb13fbfac02bad59c518be3bda7b8c8eb08287ba7cd05",
|
||||
"hash_scope": "canonical normalized self record: replace this sha256 with null, UTF-8 json.dumps(ensure_ascii=False,sort_keys=True,separators=(comma,colon)), no newline",
|
||||
"disposition": "r04 current source; retained paths and UID/marker entries"
|
||||
},
|
||||
@@ -101,24 +101,25 @@
|
||||
"self_hash_note": "A raw self-containing SHA cannot be embedded in itself. This explicit normalized hash covers implementation.json; Root must separately measure its raw SHA and the final full packet on actual submitted SHA. No final packet measurement claimed.",
|
||||
"commands_actually_run": [
|
||||
{
|
||||
"command": "python3 -B AST parse of tools/run_p08_mixed_migration.py, tools/check_p08_migration_evidence.py, tools/test_p08_migration_evidence.py",
|
||||
"command": "python3 -B AST parse of three P08 Python tools",
|
||||
"exit_code": 0,
|
||||
"result": "3 AST parses passed"
|
||||
"result": "3 parses passed"
|
||||
},
|
||||
{
|
||||
"command": "python3 -B tools/test_p08_migration_evidence.py",
|
||||
"exit_code": 0,
|
||||
"result": "2 tests in 34.514s, OK, synthetic only"
|
||||
"command": "python3 -B tools/test_p08_migration_evidence.py (first run)",
|
||||
"exit_code": 1,
|
||||
"result": "2 tests; positive stale source map because worker changed README during test"
|
||||
},
|
||||
{
|
||||
"command": "git show fixed historical references (stdout SHA only; no Git writes)",
|
||||
"command": "python3 -B tools/test_p08_migration_evidence.py (stable-source rerun)",
|
||||
"exit_code": 0,
|
||||
"result": "both references matched original raw SHA and 1082602 bytes"
|
||||
"result": "2 tests in 33.523s, OK; synthetic only",
|
||||
"classification": "Historical worker run; superseded as current local-check metadata, preserved without relabeling."
|
||||
}
|
||||
],
|
||||
"local_checks": {
|
||||
"ast_parse": "passed, three Python sources",
|
||||
"checker_tests": "2 tests passed in 34.514s; synthetic only, includes missing_start_stage and resource_delta_mismatch",
|
||||
"checker_tests": "Recorded Astra run 7cd74f004a72415fb6f4503792d297b4: 4 tests in 61.547s, PASS; source fixed before/after. Scope: synthetic local checker tests only, not native or final acceptance; not rerun by this worker.",
|
||||
"native_or_full_regression_run": false
|
||||
},
|
||||
"pending_root_conditions": [
|
||||
@@ -181,7 +182,7 @@
|
||||
"construction": "tools/test_p08_migration_evidence.py Proof creates all linked bytes and reachable whole-check positive. Embedded documents illustrate receipts/lifecycle/OS/regression/typed records. Digests belong to the disposable synthetic fixture, not delivered acceptance artifacts; reproduce with Proof in a worktree-local disposable directory.",
|
||||
"complete_example_generator": {
|
||||
"source": "tools/test_p08_migration_evidence.py",
|
||||
"source_raw_sha256": "22e75b2d4e79e7b6f8d7990c1eb14917565ca0e60197435716588ebaef3e09ea",
|
||||
"source_raw_sha256": "fd9ef7ec63f53dfd5e04e261606c9303d01a2e6590e22a35fef2e35df1429280",
|
||||
"class": "Proof",
|
||||
"constructor": "Proof(Path(fresh_temporary_directory))",
|
||||
"project_root_binding": "module ROOT resolved from unchanged generator inside this exact candidate source tree",
|
||||
@@ -224,13 +225,13 @@
|
||||
"fixture_sources": {
|
||||
"tests/s1/test_p08_combined_migration.gd": "9284a8cfb478b4c8d69f6d8166eec6c41107e3902029289b3c0e9ba48c370855",
|
||||
"tests/s1/test_p08_combined_migration.gd.uid": "8c3a70d0ce619b8cb98dc003eb6c53a31f167a686179a3ffc105f7cd4d856da5",
|
||||
"tests/fixtures/p08-02/README.md": "8a6b8dcde00be86e1ff96b29b255b586366e759a561b03e060b28e8713e210e0",
|
||||
"tests/fixtures/p08-02/mixed_world.gd": "e084d7aa6baf122089bd6f717af7e1e62de3c9930288439e6f51cc524bb55dde",
|
||||
"tests/fixtures/p08-02/README.md": "e88ca33fdb4e96602d1fb108d71a9c35b6fa054619ecf6f1396e9a6c5e0ebd98",
|
||||
"tests/fixtures/p08-02/mixed_world.gd": "a23f8a686be1d77e4874b9956eef89be4756f6e5eb01cc62454c3845acc62c25",
|
||||
"tests/fixtures/p08-02/reader.gd": "a760d0e45979d747dbd1f8710033bdce13287b00eb879d947d8378ff920ed1be",
|
||||
"tests/fixtures/p08-02/writer.gd": "1a918b5509773b4b07a27d47ee44583b0eb5288138f7bf896088a7913773c260",
|
||||
"tools/run_p08_mixed_migration.py": "7e86737e6354a4734726ba8d3171ad5f58d6c0380aba5a0fa3c57879d580e579",
|
||||
"tools/check_p08_migration_evidence.py": "c6edc43b114a64f5dedaafbfde47516baa265978c7aa836296c6bf5fe7a26857",
|
||||
"tools/test_p08_migration_evidence.py": "22e75b2d4e79e7b6f8d7990c1eb14917565ca0e60197435716588ebaef3e09ea"
|
||||
"tools/check_p08_migration_evidence.py": "928353233bf85436a5aaf8c4f77f3f82472d94b8ad2646cb1b030baf92b3b5dc",
|
||||
"tools/test_p08_migration_evidence.py": "fd9ef7ec63f53dfd5e04e261606c9303d01a2e6590e22a35fef2e35df1429280"
|
||||
},
|
||||
"native_runner_input_shape": {
|
||||
"protocol": "p08-mixed-v1",
|
||||
@@ -250,7 +251,15 @@
|
||||
"timeouts": "unchanged: collection 900s, phase maximum 180s",
|
||||
"formal_validation": "Root copies actual evidence into allocated build directory; --verify-only reads BUILD_OUTPUT_DIR/p08-evidence/index.json"
|
||||
},
|
||||
"purpose": "Synthetic Proof generator documents current schema. Historical reconstruction measurements are preserved in Git, not asserted for changed fixture."
|
||||
"purpose": "Synthetic Proof generator documents current schema. Historical reconstruction measurements are preserved in Git, not asserted for changed fixture.",
|
||||
"previous_full_inline_record": {
|
||||
"commit": "1545bd42d689a9a6382e2b003ee0550fa197f6e4",
|
||||
"path": "docs/plans/original-completion-2026-10-02/execution/p08-02/implementation.json",
|
||||
"sha256": "ae4a9f11c4e10a2be31d8fc6429577ff5f14f6097fae4f2d5a6a321937fefdac",
|
||||
"bytes": 16946,
|
||||
"hash_scope": "Git raw bytes",
|
||||
"preservation": "Full previous metadata/example remains byte-preserved in Git; historical links and SHA values unchanged."
|
||||
}
|
||||
},
|
||||
"historical_record": {
|
||||
"path": "docs/plans/original-completion-2026-10-02/execution/p08-02/implementation-historical-6c7c724.json",
|
||||
@@ -279,5 +288,25 @@
|
||||
"mismatch": "Previous zero added resources insufficient; no acceptance narrowed.",
|
||||
"git_history_verification": "Both fixed Git references raw SHA matched ab1fcf46ef73f2157f16f69394b4039a562c031fae8d407adca013a452226e2f, 1082602 bytes",
|
||||
"archive_verification": "Reuse Root supplied roundtrip evidence; archive not opened or executed."
|
||||
},
|
||||
"landing_repair": {
|
||||
"input": "ROOT_REPAIR_INPUT-r04.json",
|
||||
"ground": "initialized world paths within rank 1 bounds",
|
||||
"upper": "complete each stair pair before ordering next floor, using maximum registered build_minutes",
|
||||
"nearby_preflight": "rank bounds, registered stair definitions and existing completed logistics endpoints checked locally",
|
||||
"acceptance_sources": "all 18 retained; no omissions",
|
||||
"packet_limit_bytes": null,
|
||||
"packet_measurement": "Final actual submitted Runner packet measurement remains Root responsibility; obsolete byte cap removed; no source omission or compression.",
|
||||
"collection_timeout_seconds": 900
|
||||
},
|
||||
"metadata_sync": {
|
||||
"source_commit": "1545bd42d689a9a6382e2b003ee0550fa197f6e4",
|
||||
"current_tool_raw_sha256": {
|
||||
"tools/check_p08_migration_evidence.py": "928353233bf85436a5aaf8c4f77f3f82472d94b8ad2646cb1b030baf92b3b5dc",
|
||||
"tools/test_p08_migration_evidence.py": "fd9ef7ec63f53dfd5e04e261606c9303d01a2e6590e22a35fef2e35df1429280"
|
||||
},
|
||||
"source_map": "run_all_tests.py source_refs enumerates autoload/scripts/scenes/data/tests/assets/tools plus project.godot/export_presets.cfg/Makefile/AGENTS.md, excluding docs. Only docs changed, so source refs unchanged.",
|
||||
"worker_validation": "JSON parse, Git raw SHA equality, generator Proof reference existence; no checker/native/Godot/full regression execution.",
|
||||
"review_material_context_gap": "No .review-material or explicit complete 18-source Runner packet manifest provided in this checkout. Exact packet bytes cannot be claimed without Runner operation (prohibited); Root must measure complete actual submitted packet. No required source was removed."
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+6
@@ -11,6 +11,12 @@ The fixture derives and records this deficit from the actual registry/catalogs
|
||||
in identity.diagnostic_budget before using the existing change_resources command.
|
||||
No price or production coefficient is changed.
|
||||
|
||||
Stair preparation proceeds from floor 1 through floor 3. Each pair finishes
|
||||
using the maximum registered stair build_minutes before the next floor is
|
||||
ordered. Ground landings use initialized world paths; upper landings are
|
||||
created by completed stairs through build_facility, with a local path check
|
||||
before each pair. Failed commands include kind, payload and result details.
|
||||
|
||||
The first legacy premium job progresses while the six actor/transport facets
|
||||
are prepared. The second is paid and reserved immediately before saving, with
|
||||
zero progress; the dormitory is already complete. All three stages share the
|
||||
|
||||
Vendored
+8
-2
@@ -21,7 +21,9 @@ func expect(ok: bool, label: String) -> bool:
|
||||
func command(kind: StringName, payload: Dictionary) -> bool:
|
||||
serial += 1
|
||||
var result: CommandResult = runtime.submit_command(kind, payload, &"player", &"", 0, -1, StringName("p08.%d" % serial))
|
||||
return expect(result.is_success(), String(kind) + ": " + result.message)
|
||||
var label := String(kind) + ": " + result.message
|
||||
if not result.is_success(): label += " payload=" + var_to_str(payload) + " details=" + var_to_str(result.details)
|
||||
return expect(result.is_success(), label)
|
||||
func registry(old: bool) -> void:
|
||||
var paths: Array[String] = FacilityCatalog.FACILITY_PATHS + FacilityCatalog.INFRASTRUCTURE_FACILITY_PATHS + FacilityCatalog.RESCUE_FACILITY_PATHS
|
||||
paths.append_array(["res://tests/fixtures/legacy_premium_room.tres", "res://tests/fixtures/legacy_staff_dormitory.tres"] if old else ["res://data/facilities/expansion/premium_room.tres", "res://data/facilities/expansion/staff_dormitory.tres"])
|
||||
@@ -102,9 +104,13 @@ func setup(operations: bool) -> bool:
|
||||
for row: Array in [[&"warehouse",Vector3i(-8,0,1)],[&"kitchen",Vector3i(2,0,1)],[&"infirmary",Vector3i(6,0,1)],[&"staff_door",Vector3i(0,0,1)],[&"reception",Vector3i(-6,0,0)],[&"reception",Vector3i(5,0,0)],[&"guest_elevator",Vector3i.ZERO]]:
|
||||
if not build(row[0],row[1],false): return false
|
||||
clock.advance_minutes(180)
|
||||
# Completed stairs supply the next floor's landing through the domain projector.
|
||||
# Ground-floor landings come from the initialized world; never inject path cells.
|
||||
var stair_minutes: int = maxi(runtime.definition_registry.get_facility(&"service_stairs").build_minutes,runtime.definition_registry.get_facility(&"guest_stairs").build_minutes)
|
||||
for y: int in [1,2,3]:
|
||||
if not expect(runtime.path_service.has_cell(Vector3i(-9,y-1,1)) and runtime.path_service.has_cell(Vector3i(4,y-1,0)),"completed lower stair landings for floor %d" % y): return false
|
||||
if not build(&"service_stairs",Vector3i(-9,y,1),false) or not build(&"guest_stairs",Vector3i(4,y,0),false): return false
|
||||
clock.advance_minutes(runtime.definition_registry.get_facility(&"guest_stairs").build_minutes)
|
||||
clock.advance_minutes(stair_minutes)
|
||||
if not build(&"staff_dormitory",Vector3i(-8,2,1)): return false
|
||||
identity[&"dorm"] = instance(&"staff_dormitory").instance_id
|
||||
if not build(&"premium_room",Vector3i(-6,2,0),false): return false
|
||||
|
||||
@@ -4,6 +4,7 @@ import argparse
|
||||
import hashlib
|
||||
import importlib.util
|
||||
import json
|
||||
import math
|
||||
import os
|
||||
from pathlib import Path
|
||||
import re
|
||||
@@ -43,7 +44,16 @@ def decode(node):
|
||||
return text == 'true'
|
||||
if t == 'Nil': return None
|
||||
if t in ('Vector2i','Vector3i','Vector2','Vector3'):
|
||||
return [float(x.strip()) if '.' in x else int(x.strip()) for x in text.strip('()').split(',')]
|
||||
match = re.fullmatch(re.escape(t)+r'\(([^()]*)\)',text)
|
||||
parts = match.group(1).split(',') if match else []
|
||||
integer = t.endswith('i')
|
||||
number = r'[+-]?[0-9]+' if integer else r'[+-]?(?:[0-9]+(?:\.[0-9]*)?|\.[0-9]+)(?:[eE][+-]?[0-9]+)?'
|
||||
if len(parts) != int(t[6]) or any(not re.fullmatch(number,x.strip()) for x in parts):
|
||||
raise ValueError('invalid typed vector '+t)
|
||||
values = [(int if integer else float)(x.strip()) for x in parts]
|
||||
if not integer and not all(math.isfinite(x) for x in values):
|
||||
raise ValueError('invalid typed vector '+t)
|
||||
return values
|
||||
# Preserve unfamiliar scalar types verbatim; never silently drop them.
|
||||
return (t,text)
|
||||
|
||||
@@ -101,13 +111,44 @@ def semantics(snapshot, identity, initial=False):
|
||||
if not identity['logistics_requests']: raise ValueError('missing original food request')
|
||||
for rid in identity['logistics_requests']:
|
||||
req = requests[rid]
|
||||
if req['source_inventory_id'] not in inventories or req['destination_inventory_id'] not in inventories:
|
||||
raise ValueError('orphan cargo endpoint')
|
||||
if initial:
|
||||
carrier = staff[req['assigned_carrier_id']]
|
||||
cargo = inventories[carrier['carried_inventory_id']]
|
||||
if not cargo['stacks'] or not req['reservation_ids'] or any(x not in reservations for x in req['reservation_ids']):
|
||||
raise ValueError('lost cargo/reservation')
|
||||
for endpoint in ('source_inventory_id','destination_inventory_id'):
|
||||
value = req[endpoint]
|
||||
if not value.startswith('facility.inventory:') or value.removeprefix('facility.inventory:') not in operational:
|
||||
raise ValueError('orphan cargo endpoint')
|
||||
state = req['state']
|
||||
carrier_id = req['assigned_carrier_id']
|
||||
if state == 'RESERVED' and carrier_id:
|
||||
raise ValueError('RESERVED carrier contradiction')
|
||||
if state in ('RESERVED','DELIVERING'):
|
||||
ids = req['reservation_ids']
|
||||
if not ids or len(ids) != len(set(ids)) or any(x not in reservations for x in ids):
|
||||
raise ValueError(state+' missing/duplicate reservation')
|
||||
totals = {}
|
||||
for reservation_id in ids:
|
||||
row = reservations[reservation_id]
|
||||
if (row['requester_id'] != rid or row['owner_inventory_id'] != req['source_inventory_id']
|
||||
or row['stack_id'] not in req['reserved_stack_ids']
|
||||
or row['state'] != ('ACTIVE' if state == 'RESERVED' else 'CONSUMED')
|
||||
or type(row['quantity']) is not int or row['quantity'] <= 0):
|
||||
raise ValueError(state+' reservation crosslink mismatch')
|
||||
totals[row['item_id']] = totals.get(row['item_id'],0)+row['quantity']
|
||||
if totals != req['item_requirements'] or set(req['reserved_stack_ids']) != {reservations[x]['stack_id'] for x in ids}:
|
||||
raise ValueError(state+' reservation quantity mismatch')
|
||||
if state == 'RESERVED':
|
||||
source_stacks = indexed(inventories.get(req['source_inventory_id'],{}).get('stacks',[]),'stack_id')
|
||||
for reservation_id in ids:
|
||||
row = reservations[reservation_id]; stack = source_stacks.get(row['stack_id'],{})
|
||||
if (stack.get('item_id') != row['item_id'] or stack.get('quantity',0) < row['quantity']
|
||||
or stack.get('reserved_quantity',0) < row['quantity']):
|
||||
raise ValueError('RESERVED cargo/reservation mismatch')
|
||||
else:
|
||||
carrier = staff.get(carrier_id,{})
|
||||
cargo = inventories.get(carrier.get('carried_inventory_id'),{})
|
||||
cargo_totals = {}
|
||||
for stack in cargo.get('stacks',[]):
|
||||
cargo_totals[stack['item_id']] = cargo_totals.get(stack['item_id'],0)+stack['quantity']
|
||||
if not carrier or cargo_totals != totals:
|
||||
raise ValueError('DELIVERING carrier/cargo mismatch')
|
||||
trips = indexed(r['elevators']['trips'],'request_id')
|
||||
actors = set(guests) | set(staff)
|
||||
for trip in trips.values():
|
||||
@@ -136,6 +177,27 @@ def typed_differences(a, b, path=()):
|
||||
return [p for i,(x,y) in enumerate(zip(a['items'],b['items'])) for p in typed_differences(x,y,path+(str(i),))]
|
||||
return [path]
|
||||
|
||||
def migration_revisions(before, after, refusal):
|
||||
"""WorldService._mark_dirty assigns domain counters the global epoch.
|
||||
|
||||
r04: (world, occupancy, support) (144,144,142) -> (150,150,150).
|
||||
Only an actual occupancy rewrite can justify the support epoch delta.
|
||||
Whole typed comparison still rejects every other unapproved field.
|
||||
"""
|
||||
old, new = before['world']['revisions'], after['world']['revisions']
|
||||
keys = ('world','occupancy','support')
|
||||
if any(type(row[k]) is not int or row[k] < 0 for row in (old,new) for k in keys):
|
||||
raise ValueError('invalid revision epoch')
|
||||
if refusal:
|
||||
if any(old[k] != new[k] for k in keys):
|
||||
raise ValueError('refusal revision changed')
|
||||
return
|
||||
changed = before['world']['occupancies'] != after['world']['occupancies']
|
||||
if (not changed or not old['support'] <= old['world'] or not old['occupancy'] <= old['world']
|
||||
or new['world'] <= old['world'] or new['occupancy'] != new['world']
|
||||
or new['support'] != new['world'] or new['support'] < old['support']):
|
||||
raise ValueError('support epoch/occupancy mismatch')
|
||||
|
||||
# Explicit geometry changes only. No actor, economy, request, event or route wildcard.
|
||||
# This fixture deliberately places actors away from removed cells: NO route changes approved.
|
||||
GEOMETRY = [
|
||||
@@ -235,10 +297,13 @@ def check(project_root, evidence_dir):
|
||||
proc = decode(typed(role+'-process'))
|
||||
need(proc['pid'] == receipt['actual_pid'] and proc['failures'] == 0 and proc['operations'] == (case['operations']=='on') and proc['refusal'] == refusal,'fixture/native binding '+name+'/'+role)
|
||||
checkpoint = typed('writer-checkpoint')
|
||||
migrated = decode(checkpoint)['systems']['runtime']
|
||||
migration_revisions(source,migrated,refusal)
|
||||
if refusal: need(original == checkpoint,'partial earlier candidate '+name)
|
||||
else:
|
||||
for path in typed_differences(original,checkpoint):
|
||||
need(any(re.fullmatch(pattern,'/'.join(path)) for pattern in GEOMETRY),'unapproved typed migration delta '+name+'/'+ '/'.join(path))
|
||||
support_epoch = path == ('systems','runtime','world','revisions','support')
|
||||
need(support_epoch or any(re.fullmatch(pattern,'/'.join(path)) for pattern in GEOMETRY),'unapproved typed migration delta '+name+'/'+ '/'.join(path))
|
||||
migrated = decode(checkpoint)['systems']['runtime']
|
||||
jobs = indexed(migrated['construction']['jobs'],'job_id')
|
||||
need(all(len(jobs[j]['reserved_cells']) == 4 for j in identity['jobs']),'positive batch not applied '+name)
|
||||
|
||||
@@ -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