P04-01: 同時発生の更新規約

This commit is contained in:
Agent Control Runner
2026-10-16 08:35:40 +09:00
parent 16207d353a
commit 8418560aaf
17 changed files with 2011 additions and 932 deletions
@@ -0,0 +1,109 @@
# S4 finite Root collection — NOT RUN
This adds only the existing `power_stop` evidence obligation. It adds no counted
condition, product change, acceptance or priority approval. Run once after this
source batch is auto-submitted. Do not rerun C/S1/S2a/S2b/S3. Do not run the full
product suite now. Preserve all existing raw and REPAIR17–21 history.
From the clean auto-submitted checkout, stage these three exact reviewed files
in the fresh own-B source directory. Copy the installed engine executable there as an independently pinned native input. This staging is a Root action, not a worker
execution. The wrapper refuses QA outside own B. All input/QA/source footprints
below are within the registered B; external manifest paths are lookup keys and
provenance only. Never open their original production locations.
```sh
export B=/Users/korizatou114/Projects/TheTree-managed-worktrees/p04-01/.runtime/P04-01/build
export E=docs/plans/original-completion-2026-10-02/execution/p04-01
export PYTHONDONTWRITEBYTECODE=1
mkdir "$B/audit-support/p04-s4-source-r22"
cp "$E/s4/run_s4_native.py" "$E/s4/p04_s4_power_probe.gd" "$E/s4/S4_ROOT_RUN.md" "$B/audit-support/p04-s4-source-r22/"
cp /Applications/Godot.app/Contents/MacOS/Godot "$B/audit-support/p04-s4-source-r22/Godot"
python3 "$B/audit-support/p04-s4-source-r22/run_s4_native.py" \
--project-root "$B/audit-support/p04-r17-full-inputs-r01/product" \
--output "$B/p04-s4-native-r22" --plan "$B/p04-s4-plan-r22.json" \
--cache-run "$B/p04-final-product-full-r01" \
--godot "$B/audit-support/p04-s4-source-r22/Godot" --check-only
python3 "$B/audit-support/p04-s4-source-r22/run_s4_native.py" \
--project-root "$B/audit-support/p04-r17-full-inputs-r01/product" \
--output "$B/p04-s4-native-r22" --plan "$B/p04-s4-plan-r22.json" \
--cache-run "$B/p04-final-product-full-r01" \
--godot "$B/audit-support/p04-s4-source-r22/Godot"
```
The first invocation only prepares the pinned plan: zero engine launches. The
second is one collection, exactly four sequential owned processes: import,
parse, Writer, Reader. Each retains the framework's native diagnostics,
ownership, exit/end checks, full stream SHA, default-player protection, private
profiles, source-before/after guards and cleanup. Failure stops the collection;
do not overwrite or silently repeat it. The ordinary framework creates its
normal private user profiles; these are process outputs, not external source
inputs. Reader uses only the existing IO1 formal package mapping.
Exact input footprints:
- `B/audit-support/p04-r17-full-inputs-r01/MANIFEST.json`, SHA256
`a15f29fa12537f6d8aaf7cabea390922a9f16e3eb9c10981e5742aafc8c4c8a8`.
- All 1338 files below that relocation's `product/`, each matched to the
manifest by bytes/SHA. No source is omitted and no old path is followed.
- Its `fixed/6ce6b28c5985e8a4f13b78bfeb1f1e09f6216d874fb01e0656ec1633bfa01cd7/completion_marker_contracts.json`.
- `B/audit-support/p04-review-repair22-r01/ACTUAL_CURRENT_PUBLIC_POWER_FUNCTIONS.json`.
- The three staged S4 files above, pinned by the plan, and the staged native
executable at B/audit-support/p04-s4-source-r22/Godot, pinned before native
admission and checked unchanged after collection. The only stage override
is `tests/s3/test_runtime_staff_assignment.gd` with the S4 probe. Harness and
kitchen fixture are the unchanged product files; inherited sources are
included in the full1338 map. The parse roster is all316 `.gd` inputs.
- `B/p04-final-product-full-r01/{runner-input.json,runner-ended.json,stage/.godot/**}`:
immutable ended cache. Its selected1339 map equals the complete product1338
plus the checker. Cache cloning uses separate inodes; normal import and parse
still run. The checker is not a `.gd` parse input or a product-suite dependency.
Exact output footprints are `B/p04-s4-plan-r22.json` and the fresh
`B/p04-s4-native-r22/**`: private selected source stage, complete phase receipts
and logs, raw JSON/Variant points and all phase observations, full source maps,
PID and cleanup receipts. `formal/{writer,reader}/S4-{boundary,held,recovered}/`
mirrors all four formal members (`journal.log`, `manifest.json`, `thumbnail.png`,
`world.bin`) with a provenance-only `formal-relocation.json`. R reads these
own-B mirrors, never profile paths from old manifests.
Scenario and source-derived timing:
1. Use the existing public cafeteria + physical FLEX kitchen fixture and normal
DAY server commute. Queue using the definition's original price and duration;
advance naturally to duration−1 at959. Derive the cafeteria's sole neighbour
from the actual graph and call public `disconnect_nodes` on exactly that edge.
2. Advance normally to960. The source's modulo10 evaluation finds an isolated
consumer with zero generation/storage. Normal `mana_power_projection.apply`
makes only the cafeteria unpowered; preparation reaches `NO_POWER` with its
dependency operating. Save **boundary** at960, then **held** at961. Mana stays
positive; operator stays open; linked kitchen is Facility `OPEN` and network
consumer `ACTIVE`. No closure, damage, shift or exit triggers accompany outage.
3. At961 reconnect the same edge. Verify the complete original edge set and
graph invariants. Advance each minute, retaining full phase raw. The saved
hold deadline is960+10=970; the next permitted normal evaluation is970.
Progress, reservation and sale stay held through969. Save **recovered** at970
after one completion/payment. Resend connect_nodes on the same edge at970,
comparing the complete Runtime before/after;971 is the duplicate guard. Ordinary guest
redecision/despawn after completed service is retained in raw, never forced
or suppressed; the outage and held service have no exit confound.
4. A distinct Reader copies all four members at each point. Normal SaveService
load and Runtime restore must reproduce the full typed snapshot, including
graph and all unrelated fields. No canonicalization, dropped fields, forced
evaluation or hold bypass. Replay from the960 formal boundary through961,
reconnect,970 and971; compare complete typed points and phase states. No
additional condition is credited for this formal preservation check.
`REPAIR22_SOURCE_FACTS.json` contains full actual source bodies and graph restore
invariants. The source normalizes saved node IDs/reasons, checks valid endpoints,
rebuilds symmetric edges, preserves revision/evaluation time and checks storage
capacity and consumer allocation. Transient `_last_report` is cleared on normal
restore and is not part of Runtime snapshot equality. The derived
`related_facility_operating` flag is measured separately and checked after restore.
After successful S4, Root can perform the eight local suites and fresh read-only
R collection described in EVIDENCE_COLLECTION.md. S4 is missing until the actual
collection succeeds. Subsequent index/cross-gate work is not a source-preparation
blocker. Rebind support53cb through its normal registered task against the new
source. Preserve the21615-check proof. Only if exact final1339 evidence is
required, perform one full run after all fixes, S4, local checks, eight suites
and rebind are bundled. Never splice different-version raw or claim1339 identity.
@@ -0,0 +1,431 @@
extends "res://tests/s3/test_runtime_actor_hardening.gd"
const CASES: Array[String] = ["S4"]
const SEEDS: Dictionary = {"S4": 420440}
const GUEST: StringName = &"p04.s4.guest.a"
const REQUEST: StringName = &"p04.s4.request.a"
const QUEUED_GUEST: StringName = &"p04.s4.guest.b"
const QUEUED_REQUEST: StringName = &"p04.s4.request.b"
const SERVERS: Array[StringName] = [&"p04.order.server.a", &"p04.order.server.b"]
var _evidence_dir: String = ""
var _mode: String = ""
var _case: String = ""
var _serial: int = 0
var _facility_id: StringName = &""
var _service_id: StringName = &""
var _boundary: int = 960
var _price: int = 0
var _duration: int = 0
var _patience: int = 0
var _reserve: int = 0
var _recovery: Dictionary = {}
var _commands: Array = []
var _results: Array = []
var _receipts: Array = []
var _phases: Array = []
var _points: Dictionary = {}
var _branches: Dictionary = {}
var _slots: Dictionary = {}
func _run() -> void:
_evidence_dir = OS.get_environment("THE_TREE_S4_EVIDENCE")
_mode = OS.get_environment("THE_TREE_S4_MODE")
if _evidence_dir.is_empty() or _mode not in ["writer", "reader"]:
quit(2)
return
_expect(DirAccess.make_dir_recursive_absolute(_evidence_dir) == OK, "external evidence directory")
if _mode == "writer":
for branch: String in CASES:
_case = branch
_writer_case()
else:
_reader()
_write(_mode, {&"pid": OS.get_process_id(), &"profile": OS.get_user_data_dir(), &"slots": _slots,
&"branches": _branches, &"checks": _checks, &"failures": _failures,
&"whole_goal_accepted": false, &"unit_accepted": false}, true)
_cleanup()
print("S4 power: %d checks, %d failures" % [_checks, _failures])
quit(1 if _failures else 0)
func _fresh() -> void:
_serial = 0
_facility_id = &""
_service_id = &""
_boundary = 960
_price = 0
_duration = 0
_patience = 0
_reserve = 0
_recovery = {}
_commands = []
_results = []
_receipts = []
_phases = []
_points = {}
_reset_runtime(SEEDS[_case])
_runtime.configure_save_policy({&"enabled": false, &"daily_checkpoint_enabled": false})
_runtime.command_completed.connect(func(result: CommandResult) -> void:
_results.append({&"minute": _clock.absolute_minute, &"result": result.to_dictionary()})
_expect(result.is_success(), "actual public command completion " + str(result.command_id))
)
func _cmd(kind: StringName, payload: Dictionary, at: int = -1) -> CommandResult:
_serial += 1
var key: StringName = StringName("p04.s4.%s.command.%d" % [_case, _serial])
var result: CommandResult = _runtime.submit_command(kind, payload, &"player", &"", 10, at, key)
_commands.append({&"serial": _serial, &"kind": kind, &"payload": payload.duplicate(true),
&"at": at, &"issued_at": _clock.absolute_minute, &"idempotency_key": key, &"result": result.to_dictionary()})
_expect(result.is_success(), "public command " + String(kind))
return result
func _queue(guest_id: StringName, request_id: StringName, exit_at: int) -> void:
_cmd(&"spawn_guest", {&"guest_id": guest_id, &"guest_type_id": &"traveler", &"gold": _price * 2,
&"options": {&"state": GuestRuntime.State.DECIDING, &"current_cell": Vector3i.ZERO, &"planned_exit_at": exit_at}})
_cmd(&"create_service_request", {&"facility_id": _facility_id, &"request_id": request_id, &"guest_ids": [guest_id]})
var guest: GuestRuntime = _runtime.guest_service.get_guest(guest_id)
var request: ServiceRequest = _runtime.facility_service.get_request(request_id)
_expect(guest != null and request != null, "actual Guest and request exist")
if guest == null or request == null: return
_expect(guest.state == GuestRuntime.State.QUEUING and guest.queue_request_id == request_id, "actual queued ownership before advancing clock")
_expect(request.quoted_price == _price and request.service_minutes == _duration and request.reserved_inventory_ids.size() == 1, "definition price/duration and one meal reservation")
for id: StringName in request.reserved_inventory_ids:
var reservation: InventoryReservation = _runtime.inventory_service.get_reservation(id)
_expect(reservation != null and reservation.is_active() and reservation.requester_id == request_id, "public queue actually reserves meal")
func _prepare() -> bool:
_fresh()
var facility: FacilityInstance = _build_cafeteria(true)
if facility == null: return false
_facility_id = facility.instance_id
var definition: FacilityDefinition = _runtime.definition_registry.get_facility(facility.definition_id)
_price = definition.base_price
_duration = definition.base_service_minutes
_patience = _runtime.guest_service.get_definition(&"traveler").base_patience
for id: StringName in SERVERS:
_cmd(&"register_staff", {&"staff_id": id, &"role_id": &"server", &"options": {&"state": StaffRuntime.State.OFF_DUTY, &"current_cell": Vector3i.ZERO}})
_cmd(&"set_staff_work_assignment", {&"staff_id": id, &"template_id": &"DAY", &"facility_id": _facility_id})
_clock.advance_minutes(_boundary - _duration - 1 - _clock.absolute_minute)
_expect(facility.active_staff_ids == SERVERS, "normal public staff attendance")
_expect(_stock(facility.local_inventory_id, &"ingredient/basic_meal", 1, &"p04.s4.stock"), "one actual meal")
_queue(GUEST, REQUEST, _boundary + _duration)
_clock.advance_one_minute()
var active: Array = _runtime.facility_service.get_active_services(_facility_id)
_expect(active.size() == 1, "one started service")
if active.size() != 1: return false
var service: ServiceInstance = active[0]
_service_id = service.service_id
_expect(service.started_at + _duration == _boundary, "source definition duration aligns completion")
_clock.advance_minutes(_boundary - 1 - _clock.absolute_minute)
var graph: Dictionary = _runtime.mana_network_service.create_snapshot()
var adjacent: Array = []
for edge: Array in graph[&"edges"]:
if edge.has(String(_facility_id)):
adjacent.append(edge[1] if edge[0] == String(_facility_id) else edge[0])
_expect(adjacent.size() == 1, "actual sole cafeteria neighbour")
if adjacent.size() != 1: return false
_recovery = {&"neighbour": StringName(adjacent[0]), &"original_graph": graph,
&"interval": ManaNetworkService.EVALUATION_INTERVAL_MINUTES,
&"hold": ManaNetworkService.MINIMUM_OFF_HOLD_MINUTES}
_expect(_boundary % int(_recovery[&"interval"]) == 0, "boundary is normal evaluation")
_expect(_runtime.mana_network_service.disconnect_nodes(_facility_id, _recovery[&"neighbour"]), "public disconnect exact existing edge")
_expect(_runtime.mana_network_service.validate_invariants()[&"valid"], "symmetric cut graph invariants")
_reserve = _state.get_resource(&"mana")
_expect(_reserve > 0 and service.elapsed_minutes == _duration - 1, "positive mana and natural duration-1 progress")
return _clock.absolute_minute == _boundary - 1
func _connect_observations() -> void:
_runtime.economy_service.transaction_posted.connect(func(id: StringName, entries: Array) -> void:
var rows: Array = []
for entry: LedgerEntry in entries: rows.append(entry.to_dictionary())
_receipts.append({&"id": id, &"minute": _clock.absolute_minute, &"entries": rows})
)
_clock.tick_phase_started.connect(func(phase: int, minute: int) -> void:
var label: String = "phase-%d-%s" % [minute, SimulationKernel.TickPhase.keys()[phase]]
var row: Dictionary = _capture()
row[&"phase"] = SimulationKernel.TickPhase.keys()[phase]
_assert_phase(row)
_phases.append(_write(label, row))
)
func _assert_phase(point: Dictionary) -> void:
# Every phase is retained as complete typed raw; semantics are checked at points.
_expect(point[&"snapshot"][&"game_state"][&"resources"][&"mana"] > 0, "no global depletion")
func _capture() -> Dictionary:
var snapshot: Dictionary = _runtime.create_snapshot()
var operations: Dictionary = snapshot[&"systems"][&"runtime"][&"operations"]
var facility: FacilityInstance = _runtime.facility_service.get_instance(_facility_id)
var service: ServiceInstance = _runtime.facility_service.get_service(_service_id)
var event: EventInstance = _runtime.event_service.get_instance(&"p04.destroy.instance")
var kitchen: FacilityInstance = _runtime.facility_service.get_instance(facility.related_facility_id)
var actors: Dictionary = {}
var requests: Dictionary = {}
var wallets: Dictionary = {}
for id: StringName in [GUEST, QUEUED_GUEST]:
var guest: GuestRuntime = _runtime.guest_service.get_guest(id)
actors[String(id)] = guest.create_snapshot() if guest != null else {}
wallets[String(id)] = _runtime.economy_service.get_balance(StringName("GUEST_WALLET:" + String(id)))
for id: StringName in [REQUEST, QUEUED_REQUEST]:
var request: ServiceRequest = _runtime.facility_service.get_request(id)
requests[String(id)] = request.create_snapshot() if request != null else {}
var reservations: Array = []
for reservation: InventoryReservation in _runtime.inventory_service.get_reservations(false): reservations.append(reservation.create_snapshot())
var ledger: Array = []
for entry: LedgerEntry in _runtime.economy_service.get_entries(): ledger.append(entry.to_dictionary())
return {&"snapshot": snapshot, &"minute": _clock.absolute_minute,
&"inputs": {&"case": _case, &"seed": SEEDS[_case], &"serial": _serial, &"facility_id": _facility_id, &"service_id": _service_id,
&"boundary": _boundary, &"price": _price, &"duration": _duration, &"patience": _patience, &"reserve": _reserve, &"recovery": _recovery.duplicate(true),
&"kitchen_premise": "ActorHardening physical FLEX fixture; not natural hire/unlock"},
&"related_facility_operating": facility.related_facility_operating, &"kitchen": kitchen.create_snapshot(), &"graph_invariants": _runtime.mana_network_service.validate_invariants(),
&"facility": facility.create_snapshot(), &"service": service.create_snapshot() if service != null else {},
&"event": event.create_snapshot() if event != null else {}, &"guests": actors, &"requests": requests, &"wallets": wallets,
&"reservations": reservations, &"ledger": ledger, &"receipts": _receipts.duplicate(true),
&"player_cash": _runtime.economy_service.get_balance(_runtime.PLAYER_CASH_ACCOUNT),
&"available_meals": _runtime.inventory_service.available_quantity(facility.local_inventory_id, &"ingredient/basic_meal"),
&"active_services": _runtime.facility_service.get_active_services(_facility_id).size(),
&"operations_enabled": operations[&"enabled"], &"saved_coordinator": operations[&"coordinator"],
&"actual_coordinator_absent": _runtime.operations_coordinator == null, &"unrelated_getters_called": false}
func _labels() -> Array[String]:
return ["boundary", "held", "recovered"]
func _advance(label: String) -> void:
if label == "duplicate_guard":
var before_replay: Dictionary = _runtime.create_snapshot()
var accepted: bool = _runtime.mana_network_service.connect_nodes(_facility_id, _recovery[&"neighbour"])
var after_replay: Dictionary = _runtime.create_snapshot()
_expect(accepted and var_to_bytes(before_replay) == var_to_bytes(after_replay), "public reconnect replay changes no Runtime field")
_write("reconnect-replay", {&"before": before_replay, &"after": after_replay, &"accepted": accepted})
if label == "recovered":
_expect(_clock.absolute_minute == _boundary + 1, "reconnect only after held observation")
_expect(_runtime.mana_network_service.connect_nodes(_facility_id, _recovery[&"neighbour"]), "reconnect exact cut edge")
var graph: Dictionary = _runtime.mana_network_service.create_snapshot()
_expect(var_to_bytes(graph[&"edges"]) == var_to_bytes(_recovery[&"original_graph"][&"edges"]), "all original edges restored")
_expect(_runtime.mana_network_service.validate_invariants()[&"valid"], "reconnected graph invariants")
var node: Dictionary = _runtime.mana_network_service.get_node(_facility_id)
var resume_at: int = int(node[&"hold_until"])
var interval: int = int(_recovery[&"interval"])
while resume_at % interval != 0: resume_at += 1
_expect(resume_at > _clock.absolute_minute and resume_at < _boundary + _duration, "finite normal hold/evaluation recovery before exit")
while _clock.absolute_minute < resume_at:
_clock.advance_one_minute()
if _clock.absolute_minute < resume_at: _assert_stopped(_capture())
else:
_clock.advance_one_minute()
func _writer_case() -> void:
if not _prepare(): return
var before: Dictionary = _capture()
_assert_before(before)
_points["before"] = _write("before", before)
_connect_observations()
for label: String in _labels():
_advance(label)
var actual: Dictionary = _capture()
_assert_outcome(before, actual)
_save_point(label, actual)
_advance("duplicate_guard")
var guard: Dictionary = _capture()
_assert_outcome(before, guard)
_points["duplicate_guard"] = _write("duplicate_guard", guard)
_branches[_case] = _write("case", {&"points": _points.duplicate(true), &"phases": _phases.duplicate(true),
&"commands": _write("commands", {&"submitted": _commands, &"completed_including_fixture": _results})})
func _assert_before(point: Dictionary) -> void:
_assert_unconfounded(point)
_expect(point[&"service"][&"state"] == "ACTIVE" and point[&"facility"][&"power_available"], "before normal projection remains powered")
_expect(point[&"service"][&"elapsed_minutes"] == _duration - 1 and point[&"reservations"].size() == 1, "natural duration-1 and one reservation")
func _assert_unconfounded(point: Dictionary) -> void:
_expect(not point[&"operations_enabled"] and point[&"actual_coordinator_absent"], "existing isolated fixture")
_expect(point[&"facility"][&"operator_open"] and point[&"related_facility_operating"], "open operator and operating dependency")
_expect(point[&"facility"][&"durability"] > 0 and point[&"event"].is_empty(), "no destruction event")
_expect(point[&"kitchen"][&"power_available"] and point[&"kitchen"][&"state"] == "OPEN", "kitchen stays active/powered")
_expect(point[&"snapshot"][&"game_state"][&"resources"][&"mana"] > 0, "positive global reserve")
_expect(point[&"facility"][&"active_staff_ids"] == SERVERS, "no shift confound")
if point[&"service"][&"state"] != "COMPLETED":
_expect(point[&"guests"][String(GUEST)][&"planned_exit_at"] > point[&"minute"] and point[&"guests"][String(GUEST)][&"state"] == "RECEIVING_SERVICE", "no exit at outage/hold")
_expect(point[&"graph_invariants"][&"valid"], "graph allocation/storage/symmetry valid")
_expect(_runtime.mana_network_service.get_consumer_state(StringName(point[&"kitchen"][&"instance_id"])) == ManaNetworkService.ConsumerState.ACTIVE, "kitchen network consumer ACTIVE")
func _assert_stopped(point: Dictionary) -> void:
_assert_unconfounded(point)
_expect(not point[&"facility"][&"power_available"] and point[&"service"][&"state"] == "PAUSED" and point[&"service"][&"interruption_reason"] == "NO_POWER", "actual isolated NO_POWER path")
_expect(point[&"service"][&"elapsed_minutes"] == _duration - 1 and point[&"receipts"].is_empty(), "stopped progress held and no payment")
func _assert_reservation(before: Dictionary, point: Dictionary, request_id: StringName, state: String, terminal: int) -> void:
var prior: Array = []
var current: Array = []
for row: Dictionary in before[&"reservations"]:
if row[&"requester_id"] == String(request_id): prior.append(row)
for row: Dictionary in point[&"reservations"]:
if row[&"requester_id"] == String(request_id): current.append(row)
_expect(prior.size() == 1 and current.size() == 1, "one original reservation per request")
if prior.size() != 1 or current.size() != 1: return
_expect(prior[0][&"reservation_id"] == current[0][&"reservation_id"] and current[0][&"state"] == state and current[0][&"terminal_at"] == terminal, "same reservation ID/state/terminal time; no second release")
if state == "ACTIVE":
_expect(var_to_bytes(prior[0]) == var_to_bytes(current[0]), "entire held reservation unchanged")
var request: Dictionary = point[&"requests"][String(request_id)]
_expect(not request.is_empty() and request[&"reserved_inventory_ids"].has(current[0][&"reservation_id"]), "active reservation has live request owner")
else:
_expect(point[&"requests"][String(request_id)].is_empty(), "terminal reservation has no orphan request")
if state == "RELEASED":
var release_count: int = 0
var inventory: Dictionary = point[&"snapshot"][&"systems"][&"runtime"][&"inventory"]
for operation: Dictionary in inventory[&"idempotency_results"]:
var result: Dictionary = operation[&"result"]
if operation[&"action"] == "release" and String(result.get(&"reservation_id", "")) == current[0][&"reservation_id"]:
release_count += 1
_expect(bool(result.get(&"ok", false)) and result.get(&"quantity", 0) == current[0][&"quantity"], "release returned actual reserved quantity, not an orphan repair/retry")
_expect(release_count == 1, "one raw inventory release operation for original reservation")
func _assert_outcome(before: Dictionary, point: Dictionary) -> void:
_assert_unconfounded(point)
var service: Dictionary = point[&"service"]
_expect(service[&"service_id"] == before[&"service"][&"service_id"], "same service ID")
var paid: bool = service[&"state"] == "COMPLETED"
if not paid:
_assert_stopped(point)
_expect(var_to_bytes(point[&"ledger"]) == var_to_bytes(before[&"ledger"]), "zero stopped ledger delta")
_assert_reservation(before, point, REQUEST, "ACTIVE", -1)
else:
var node: Dictionary = _runtime.mana_network_service.get_node(_facility_id)
_expect(point[&"facility"][&"power_available"] and service[&"elapsed_minutes"] == _duration and service[&"completed_at"] >= int(node[&"hold_until"]), "resume and complete after hold")
_expect(point[&"receipts"].size() == 1, "one payment only")
var expected: Array = before[&"ledger"].duplicate(true)
for receipt: Dictionary in point[&"receipts"]: expected.append_array(receipt[&"entries"])
_expect(var_to_bytes(expected) == var_to_bytes(point[&"ledger"]), "whole ledger exactly one receipt")
_assert_reservation(before, point, REQUEST, "CONSUMED", service[&"completed_at"])
_expect(point[&"wallets"][String(GUEST)] == _price * (1 if paid else 2) and point[&"player_cash"] == before[&"player_cash"] + (_price if paid else 0), "exact one price or zero")
func _save_point(label: String, point: Dictionary) -> void:
_points[label] = _write(label, point)
var path: String = "user://saves/p04_s4/" + _case + "-" + label
var saved: Dictionary = SaveService.save_snapshot(path, point[&"snapshot"])
var inspected: Dictionary = SaveService.inspect_snapshot(path)
_expect(bool(saved.get(&"ok", false)) and bool(inspected.get(&"ok", false)) and bool(inspected.get(&"read_only", false)), "public formal save/inspect")
_expect(var_to_bytes(inspected.get(&"snapshot", {})) == var_to_bytes(point[&"snapshot"]), "all typed fields preserved by formal package")
_expect(var_to_bytes(_runtime.create_snapshot()) == var_to_bytes(point[&"snapshot"]), "formal saving leaves Runtime unchanged")
_slots[_case + "-" + label] = ProjectSettings.globalize_path(path)
# Snapshot bytes exist once in point raw and once in the required formal
# package; indexes do not embed inspection/load copies of those bytes.
func _write(label: String, value: Variant, global_name: bool = false) -> Dictionary:
var name: String = label if global_name else _mode + "-" + _case + "-" + label
var reference: Dictionary = {}
for extension: String in ["var", "json"]:
var path: String = _evidence_dir.path_join(name + "." + extension)
_expect(not FileAccess.file_exists(path), "raw point written exactly once " + name)
var file: FileAccess = FileAccess.open(path, FileAccess.WRITE)
_expect(file != null, "raw file " + name)
if file == null: continue
if extension == "var": file.store_buffer(var_to_bytes(value))
else: file.store_string(JSON.stringify(value, "\t"))
file.flush()
file.close()
reference[extension] = {&"path": path, &"sha256": FileAccess.get_sha256(path)}
return reference
func _read(reference: Dictionary) -> Dictionary:
var item: Dictionary = reference["var"]
var path: String = item["path"]
_expect(FileAccess.get_sha256(path) == item["sha256"], "raw typed SHA")
var json_ref: Dictionary = reference["json"]
_expect(FileAccess.get_sha256(json_ref["path"]) == json_ref["sha256"], "raw JSON SHA")
var file: FileAccess = FileAccess.open(path, FileAccess.READ)
_expect(file != null, "typed input readable")
if file == null: return {}
var value: Variant = bytes_to_var(file.get_buffer(file.get_length()))
file.close()
_expect(value is Dictionary, "typed input dictionary")
return value if value is Dictionary else {}
func _restore_inputs(point: Dictionary) -> void:
var inputs: Dictionary = point[&"inputs"]
_expect(_serial == 0, "Reader starts with serial zero before importing external inputs")
_serial = inputs[&"serial"]
_facility_id = inputs[&"facility_id"]
_service_id = inputs[&"service_id"]
_boundary = inputs[&"boundary"]
_price = inputs[&"price"]
_duration = inputs[&"duration"]
_patience = inputs[&"patience"]
_reserve = inputs[&"reserve"]
_recovery = inputs[&"recovery"].duplicate(true)
func _load_point(path: String, expected: Dictionary) -> bool:
_fresh()
_restore_inputs(expected)
var loaded: Dictionary = SaveService.load_snapshot(path)
_expect(bool(loaded.get(&"ok", false)), "public formal load")
if not bool(loaded.get(&"ok", false)): return false
_expect(var_to_bytes(loaded[&"snapshot"]) == var_to_bytes(expected[&"snapshot"]), "all saved typed fields equal Writer")
var restored: bool = _runtime.restore_snapshot(loaded[&"snapshot"])
_expect(restored, "public Runtime restore")
_expect(var_to_bytes(_runtime.create_snapshot()) == var_to_bytes(expected[&"snapshot"]), "all restored typed fields equal Writer")
return restored
func _reader() -> void:
var file: FileAccess = FileAccess.open(_evidence_dir.path_join("writer.json"), FileAccess.READ)
_expect(file != null, "Writer receipt")
if file == null: return
var writer: Dictionary = JSON.parse_string(file.get_as_text())
file.close()
_expect(int(writer["pid"]) != OS.get_process_id() and writer["profile"] != OS.get_user_data_dir(), "independent Reader PID/profile")
for branch: String in CASES:
_case = branch
var writer_case: Dictionary = _read(writer["branches"][branch])
var hashes: Dictionary = {}
var restored_refs: Dictionary = {}
for label: String in _labels():
var key: String = branch + "-" + label
var original: String = writer["slots"][key]
var target: String = ProjectSettings.globalize_path("user://saves/p04_s4/" + key)
_expect(DirAccess.make_dir_recursive_absolute(target) == OK, "private Reader formal slot")
var directory: DirAccess = DirAccess.open(original)
_expect(directory != null, "Writer formal slot readable")
if directory == null: return
var members: PackedStringArray = directory.get_files()
members.sort()
_expect(directory.get_directories().is_empty() and members == PackedStringArray(["journal.log", "manifest.json", "thumbnail.png", "world.bin"]), "all formal members enumerated")
var member_hashes: Dictionary = {}
for member: String in members:
var sha: String = FileAccess.get_sha256(original.path_join(member))
_expect(DirAccess.copy_absolute(original.path_join(member), target.path_join(member)) == OK, "copy every formal member")
var copied_sha: String = FileAccess.get_sha256(target.path_join(member))
_expect(not sha.is_empty() and sha == copied_sha, "original/copy member SHA equality")
member_hashes[member] = {&"original_before": sha, &"copy_before": copied_sha}
hashes[key] = {&"original": original, &"copy": target, &"members": member_hashes}
_slots[key] = target
var expected: Dictionary = _read(writer_case[&"points"][label])
if not _load_point(target, expected): return
restored_refs[label] = _write(label + "-restored", _capture())
var before: Dictionary = _read(writer_case[&"points"]["before"])
var boundary: Dictionary = _read(writer_case[&"points"]["boundary"])
if not _load_point(_slots[branch + "-boundary"], boundary): return
_assert_stopped(_capture())
_connect_observations()
var replay_refs: Dictionary = {}
for label: String in ["held", "recovered", "duplicate_guard"]:
_advance(label)
var actual: Dictionary = _capture()
var expected: Dictionary = _read(writer_case[&"points"][label])
_expect(var_to_bytes(actual) == var_to_bytes(expected), "replay complete typed point including Runtime, inputs, receipts " + label)
_assert_outcome(before, actual)
replay_refs[label] = _write(label + "-replay", actual)
var expected_phases: Array = []
for reference: Dictionary in writer_case[&"phases"]:
if int(_read(reference)[&"minute"]) > _boundary: expected_phases.append(reference)
_expect(_phases.size() == expected_phases.size(), "complete phase count")
if _phases.size() == expected_phases.size():
for index: int in range(_phases.size()):
_expect(var_to_bytes(_read(_phases[index])) == var_to_bytes(_read(expected_phases[index])), "same phase order and all typed phase state")
for key: String in hashes:
var row: Dictionary = hashes[key]
for member: String in row[&"members"]:
var item: Dictionary = row[&"members"][member]
item[&"original_after"] = FileAccess.get_sha256(String(row[&"original"]).path_join(member))
item[&"copy_after"] = FileAccess.get_sha256(String(row[&"copy"]).path_join(member))
_expect(item[&"original_before"] == item[&"original_after"] and item[&"copy_before"] == item[&"copy_after"], "original/copy unchanged after all loads and replay")
_branches[branch] = _write("case", {&"restored": restored_refs, &"replay": replay_refs, &"phases": _phases,
&"member_hashes": hashes, &"recovery_commands": _commands, &"replay_completed_commands": _results})
@@ -0,0 +1,317 @@
#!/usr/bin/env python3
"""S4-only native entry. --check-only never loads ownership helpers or starts Godot."""
from pathlib import Path
import argparse
import hashlib
import importlib.util
import json
import os
import shutil
import subprocess
import sys
sys.dont_write_bytecode = True
HERE = Path(__file__).resolve().parent
BUILD = Path("/Users/korizatou114/Projects/TheTree-managed-worktrees/p04-01/.runtime/P04-01/build")
PRODUCT = BUILD / "audit-support/p04-r17-full-inputs-r01/product"
CACHE_RUN = BUILD / "p04-final-product-full-r01"
TEST = "tests/s3/test_runtime_staff_assignment.gd"
QA = HERE / "p04_s4_power_probe.gd"
ENGINE = HERE / "Godot"
MANIFEST = BUILD / "audit-support/p04-r17-full-inputs-r01/MANIFEST.json"
MANIFEST_SHA = "a15f29fa12537f6d8aaf7cabea390922a9f16e3eb9c10981e5742aafc8c4c8a8"
CONTRACTS = BUILD / "audit-support/p04-r17-full-inputs-r01/fixed/6ce6b28c5985e8a4f13b78bfeb1f1e09f6216d874fb01e0656ec1633bfa01cd7/completion_marker_contracts.json"
CASES = ["S4"]
LABELS = {"S4": ["boundary", "held", "recovered"]}
SLOTS = ["S4-" + label for label in sorted(LABELS["S4"])]
MATERIALS = [MANIFEST, BUILD / "audit-support/p04-review-repair22-r01/ACTUAL_CURRENT_PUBLIC_POWER_FUNCTIONS.json"]
def template_guard(root):
if root != PRODUCT.resolve() or not HERE.is_relative_to(BUILD.resolve()):
raise ValueError("Root must stage this exact QA/wrapper/plan inside own B")
if pin(MANIFEST)["sha256"] != MANIFEST_SHA:
raise ValueError("immutable full R17 relocation manifest")
manifest = json.loads(MANIFEST.read_text())
for name, source in manifest["product_source"].items():
# Old absolute paths are lookup keys only. Never open them.
row = manifest["original_to_relocated"][source["path"]]
actual = pin(root / name)
if Path(row["relocated"]).resolve() != root / name or any(
actual[k] != source[k] or actual[k] != row[k] for k in ("bytes", "sha256")):
raise ValueError("relocated product differs: " + name)
probe = QA.read_text()
import re
if ":=" in probe or re.search(r"\bvar\s+\w+\s*=", probe) or re.search(r"\bfor\s+\w+\s+in\b", probe):
raise ValueError("explicit typed declarations/loops required")
if re.search(r"\.(?:patience_remaining|elapsed_minutes|power_available|capacity|hold_until)\s*=(?!=)", probe):
raise ValueError("forbidden direct state mutation")
for forbidden in ["evaluate_now(", "configure_operations(", "._update_shift_states(", ".record_duty_minute("]:
if forbidden in probe:
raise ValueError("forbidden bypass: " + forbidden)
# Constants and public bodies are read from the actual selected source, not a memo.
mana = (root / "scripts/mana/mana_network_service.gd").read_text()
for key in ("EVALUATION_INTERVAL_MINUTES", "MINIMUM_OFF_HOLD_MINUTES"):
if not re.search(r"const " + key + r" := 10\b", mana):
raise ValueError("source timing changed: " + key)
def module(name, path):
spec = importlib.util.spec_from_file_location(name, path)
value = importlib.util.module_from_spec(spec)
spec.loader.exec_module(value)
return value
def pin(path):
path = Path(path)
if path.is_symlink() or not path.is_file():
raise ValueError("missing exact input: " + str(path))
data = path.read_bytes()
return dict(path=str(path), bytes=len(data), sha256=hashlib.sha256(data).hexdigest())
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--project-root", type=Path, default=PRODUCT)
parser.add_argument("--output", type=Path, default=BUILD / "p04-s4-native-r22")
parser.add_argument("--plan", type=Path, default=BUILD / "p04-s4-plan-r22.json")
parser.add_argument("--cache-run", type=Path, default=CACHE_RUN)
parser.add_argument("--godot", type=Path, default=ENGINE)
parser.add_argument("--check-only", action="store_true")
args = parser.parse_args()
if args.godot.resolve() != ENGINE.resolve():
raise ValueError("use the exact own-B staged engine")
if args.cache_run.resolve() != CACHE_RUN.resolve():
raise ValueError("only registered own-B full-run cache")
root = args.project_root.resolve()
out = args.output.resolve()
if not out.is_relative_to(BUILD.resolve()) or not args.plan.resolve().is_relative_to(BUILD.resolve()):
raise ValueError("plan/output must stay in task build")
r = module("s4_standard_runner", root / "tools/run_all_tests.py")
# Preserve the reviewed source_refs -> exactly one QA override order.
source_before = r.source_refs(root)
refs = dict(source_before)
refs[TEST] = pin(QA)
template_guard(root)
expected = {n: dict(bytes=p["bytes"], sha256=p["sha256"]) for n, p in refs.items()}
fixed = [pin(p) for p in MATERIALS + [
QA, Path(__file__), HERE / "S4_ROOT_RUN.md",
root / "tests/transport/test_runtime_transport.gd",
root / "tests/staff/test_rescue_elevator.gd",
CONTRACTS,
]]
selected = dict(root_before=source_before, map={n: p["sha256"] for n, p in refs.items()}, timeout=1200)
cache_note = None
cache_receipts = []
for leaf in ["runner-ended.json", "runner-input.json"]:
path = args.cache_run / leaf
try:
cache_receipts.append(pin(path))
except OSError as caught:
cache_note = dict(path=str(path), error=type(caught).__name__ + ": " + str(caught),
required_before_native=True)
break
plan = dict(protocol="p04-s4-preparation-v1", project_root=str(root), output=str(out), engine_path=str(args.godot.resolve()),
source_before=source_before, selected_source_map=selected["map"],
QA_overrides=[TEST], fixed_input_pins=fixed, cache_run=str(args.cache_run),
cache_receipts=cache_receipts, cache_access=cache_note,
phases=["import", "parse", "writer", "reader"],
native_engine_launches=0, whole_goal_accepted=False, unit_accepted=False)
# Check-only only reads source/materials and writes the plan in task build.
# It deliberately does not read default-player data, inspect processes or stat Godot.
if args.check_only:
args.plan.parent.mkdir(parents=True, exist_ok=True)
if args.plan.exists():
r.require(r.read(args.plan) == plan, "existing plan differs; use a fresh plan path")
else:
r.write(args.plan, plan)
print(json.dumps(dict(check_only=True, engines=0, source_files=len(source_before),
QA_overrides=[TEST], fixed_pins=len(fixed), plan=str(args.plan),
plan_sha256=r.sha(args.plan), cache_access=cache_note), ensure_ascii=False))
return 0
r.require(root == PRODUCT.resolve() and len(source_before) == 1338, "Root native requires product 1338; worker plan is not native evidence")
r.require(args.plan.is_file() and r.read(args.plan) == plan, "run exact check-only plan first")
engine_descriptor = None
def fixed_guard():
template_guard(root)
if engine_descriptor is not None:
r.require(pin(engine_descriptor["path"]) == engine_descriptor, "engine changed during collection")
for value in fixed + cache_receipts:
r.require(pin(value["path"]) == value, "fixed input changed: " + value["path"])
fixed_guard()
# Native admission is Root-only and fails closed on unreadable cache.
r.require(cache_note is None, "Root must read exact cache receipts: " + str(cache_note))
old = args.cache_run
r.require(r.read(old / "runner-ended.json")["passed"], "ended cache run must have passed")
old_map = r.read(old / "runner-input.json")["selected_source_map"]
current_map = {n: p["sha256"] for n, p in source_before.items()}
r.require({k: v for k, v in old_map.items() if k != "tools/check_p04_order_evidence.py"} == current_map and len(old_map) == 1339, "immutable full cache: all 1338 product inputs equal; checker is not parsed/executed by product suites")
protected = r.default_player(root)
outer = module("s4_owned_outer", r.SUPPORT / "owned_outer.py")
r.require(not outer.native.helper["live_engines"](), "another Godot owns CPU")
engine = args.godot.resolve()
r.require(engine.is_file(), "Root native Godot path")
engine_descriptor = pin(engine)
r.require(engine_descriptor["sha256"] == r.read(old / "runner-input.json")["engine_sha256"],
"staged engine matches preserved full proof; no environment substitution")
r.require(shutil.disk_usage(out.parent).free >= 1024**3 + engine.stat().st_size + 136*1024**2,
"reviewed native disk admission")
r.require(not out.exists(), "fresh output required; preserve previous attempts")
out.mkdir()
stage = out / "stage"
stage.mkdir()
(out / "starters").mkdir()
(out / "bootstrap").mkdir()
phases = []
previous = {}
error = None
guards = []
cleanup = []
cleanup_errors = []
cache_before = None
formal_before = None
formal_after = None
original_phase = outer.run_phase
def run_phase(argv, folder, timeout, env, **kwargs):
env = dict(env)
if folder.name in ["writer", "reader"]:
env.update(THE_TREE_S4_EVIDENCE=str(out / "raw"), THE_TREE_S4_MODE=folder.name)
return original_phase(argv, folder, timeout, env, **kwargs)
outer.run_phase = run_phase
r.write(out / "runner-started.json", dict(started=r.now(), owner_runner_pid=os.getpid(),
plan=pin(args.plan), engines_launched=0))
try:
r.write(out / "source_before.json", source_before)
r.write(out / "selected-source-map.json", refs)
# Private source stage first, then literal reviewed bootstrap.
for name, value in refs.items():
target = stage / name
target.parent.mkdir(parents=True, exist_ok=True)
r.copy_source(Path(value["path"]), target)
r.require(r.inventory(stage, stage=True) == expected, "whole private source")
for name, value in r.read(r.SUPPORT / "bootstrap-scripts.json").items():
data = value["text"].encode()
r.require(hashlib.sha256(data).hexdigest() == value["sha256"], "bootstrap pin")
(out / "bootstrap" / name).write_bytes(data)
cache = old / "stage/.godot"
cache_before = r.inventory(cache)
r.require(bool(cache_before), "nonempty ended cache")
for name in cache_before:
src = cache / name
dst = stage / ".godot" / name
dst.parent.mkdir(parents=True, exist_ok=True)
subprocess.run(["/bin/cp", "-c", str(src), str(dst)], check=True)
r.require((src.stat().st_dev, src.stat().st_ino) != (dst.stat().st_dev, dst.stat().st_ino),
"cache clone is a separate inode")
r.require(r.inventory(stage / ".godot") == cache_before, "whole cache clone hashes")
r.write(out / "cache-reuse.json", dict(ended_run=str(old), receipts=cache_receipts,
whole_cache=cache_before, independent_inode=True,
ordinary_import_required=True, ordinary_parse_required=True))
paths = ["res://" + n for n in sorted(refs) if n.endswith(".gd")]
r.write(out / "parse-paths.json", paths)
markers = module("s4_markers", root / "tools/strict_test_markers.py")
contracts = markers.load_contracts(CONTRACTS)
contracts[TEST] = dict(test=TEST, template="S4 power: <checks> checks, <failures> failures", required=True)
policy = r.read(r.SUPPORT / "suite-policy.json")
r.require(TEST not in policy["suite_policies"], "no normal suite watchdog/policy override")
r.write(out / "contracts.json", contracts)
r.write(out / "runner-input.json", dict(plan, owner_runner_pid=os.getpid(),
wrapper_sha256=r.sha(__file__), QA=pin(QA), engine=engine_descriptor, runner_sha256=r.sha(root / "tools/run_all_tests.py"),
default_player_before=protected, fixed_input_pins=fixed,
functional_watchdog_seconds=1200, source_adoption=False))
diagnostics = module("s4_diagnostics", r.SUPPORT / "diagnostic_gate.py")
owners = module("s4_owners", r.SUPPORT / "ordinary_proof_gate.py")
audio = module("s4_audio", r.SUPPORT / "audio_evidence_gate.py")
public = (stage / "project.godot").read_bytes()
for phase, test in [("import", None), ("parse", None), ("writer", TEST), ("reader", TEST)]:
if phase == "reader":
writer = r.read(out / "raw/writer.json")
formal_before = {n: r.inventory(Path(p)) for n, p in writer["slots"].items()}
r.require(sorted(formal_before) == SLOTS, "exact three formal points for isolated power case")
row = r.execute_phase(str(engine), stage, out, phase, test, selected, expected,
previous, policy, contracts, paths, public, protected, root,
outer, markers, diagnostics, owners, audio)
phases.append(row)
# execute_phase uses the same test log name; retain each phase's exact log too.
if test and row.get("log"):
(out / (phase + "-combined-raw.log")).write_bytes((out / row["log"]).read_bytes())
row["strict_runner_log_path"] = row["log"]
row["log"] = phase + "-combined-raw.log"
r.write(out / (phase + "-actual-phase.json"), row)
print(json.dumps(dict(phase=phase, passed=row["passed"], error=row.get("validation_error"))), flush=True)
r.require(row["passed"], "first strict phase failure: " + phase)
if phase in ["writer", "reader"]:
receipt = r.read(out / ("raw/" + phase + ".json"))
r.require(receipt["checks"] > 0 and receipt["failures"] == 0,
"S4 actual outcome receipt")
r.require(sorted(receipt["branches"]) == CASES,
"exact one independent Writer/Reader case")
r.require(sorted(receipt["slots"]) == SLOTS, "all formal point slots")
r.require(receipt["pid"] == row["actual_pid"], "actual native PID/receipt")
writer = r.read(out / "raw/writer.json")
reader = r.read(out / "raw/reader.json")
r.require(writer["pid"] != reader["pid"] and writer["profile"] != reader["profile"], "distinct formal processes")
formal_after = {n: r.inventory(Path(p)) for n, p in writer["slots"].items()}
r.require(formal_before == formal_after, "Reader leaves Writer packages unchanged")
except BaseException as caught:
error = type(caught).__name__ + ": " + str(caught)
finally:
cleanup, cleanup_errors = outer.finish_captured(outer.OWNED)
try:
r.write(out / "outer-owned-finally.json", dict(cleanup=cleanup, errors=cleanup_errors))
except BaseException as caught:
guards.append("finally receipt: " + type(caught).__name__ + ": " + str(caught))
# Fixed guard always follows ownership cleanup, even if setup or an engine failed.
try:
fixed_guard()
after = r.source_refs(root)
r.write(out / "source_after.json", after)
r.require(after == source_before, "whole product source unchanged")
r.require(r.default_player(root) == protected, "default player unchanged")
if (stage / "project.godot").exists():
r.require(r.inventory(stage, stage=True) == dict(expected, **previous), "whole private source unchanged")
if cache_before is not None:
r.require(r.inventory(old / "stage/.godot") == cache_before, "original cache unchanged")
if formal_before is not None:
writer = r.read(out / "raw/writer.json")
formal_after = {n: r.inventory(Path(p)) for n, p in writer["slots"].items()}
r.require(formal_before == formal_after, "Writer formal packages unchanged after Reader, including failure")
except BaseException as caught:
guards.append(type(caught).__name__ + ": " + str(caught))
pids = [p.pid for p in outer.OWNED]
passed = (error is None and not guards and not cleanup_errors and len(phases) == 4
and all(p["passed"] for p in phases) and len(pids) == len(set(pids)) == 4
and all(p["engine_ended"] for p in cleanup))
execution = dict(passed=passed, error=error, guard_errors=guards, phases=phases,
actual_captured_PIDs=pids, cleanup=cleanup, cleanup_errors=cleanup_errors,
formal_before=formal_before, formal_after=formal_after,
whole_goal_accepted=False, unit_accepted=False)
# IO1 has already copied all four members into a separate Reader profile.
# Mirror those exact complete directories to own B for read-only R verification.
# Original profile paths are provenance after this step, never verifier inputs.
if passed:
relocated = {}
for role in ("writer", "reader"):
receipt = r.read(out / ("raw/" + role + ".json"))
relocated[role] = {}
for slot, original in receipt["slots"].items():
target = out / "formal" / role / slot
target.mkdir(parents=True)
members = r.inventory(Path(original))
r.require(set(members) == {"journal.log", "manifest.json", "thumbnail.png", "world.bin"}, "all formal members")
for member in members:
shutil.copyfile(Path(original) / member, target / member)
r.require(r.inventory(target) == members == formal_before[slot], "whole formal mirror")
relocated[role][slot] = dict(provenance=original, relative=str(target.relative_to(out)), members=members)
r.write(out / "formal-relocation.json", relocated)
r.write(out / "raw-inventory.json", r.inventory(out / "raw"))
r.write(out / "PID-set.json", dict(owner_runner_pid=os.getpid(), engine_PIDs=pids))
r.write(out / "execution.json", execution)
r.write(out / "runner-ended.json", dict(execution, ended=r.now()))
print(json.dumps(dict(passed=passed, error=error, guard_errors=guards, output=str(out))))
return 0 if passed else 1
if __name__ == "__main__":
try:
raise SystemExit(main())
except Exception as caught:
print(type(caught).__name__ + ": " + str(caught), file=sys.stderr)
raise SystemExit(2)