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

This commit is contained in:
Agent Control Runner
2026-10-16 09:19:25 +09:00
parent 7af6fafaf0
commit 4ab7ab8712
5 changed files with 447 additions and 49 deletions
@@ -17,7 +17,7 @@ MANIFEST_SHA256 = 'a15f29fa12537f6d8aaf7cabea390922a9f16e3eb9c10981e5742aafc8c4c
MANIFEST_BYTES = 1244135
PRODUCT = '5f6bc11d6bb6e92d22d787989b219fef3a435bc1'
RUNS = {'s1': 'p04-s1-native-r02', 's2a': 'p04-s2a-native-r03',
's2b': 'p04-s2b-native-r02', 's3': 'p04-s3-native-r01', 's4': 'p04-s4-native-r24'}
's2b': 'p04-s2b-native-r02', 's3': 'p04-s3-native-r01', 's4': 'p04-s4-native-r26'}
# Native descriptors pinned from literal files, never ROOT result passed fields.
NATIVE_PINS = {
's1': ('1e26d9585396da9415747d87d77b70f23425a3ee0714e4fca114748f9479fd4e', '01372beb0ffec181c892d3b4e88bce32001e08fb2007938d7bf19135511a7acc', '0b6d11eb34d8af64b22cb806caeb06eed4ec521fa9ee24d1204ea185345e655c'),
@@ -1023,11 +1023,29 @@ class S4Series(Series):
require(path.is_relative_to(self.build), 'S4 material outside own B')
self.read_build(path.relative_to(self.build).as_posix(), pin)
verify_s4_engine(inputs['engine'], execution['engine_identity'])
require(inputs['engine_identity'] == execution['engine_identity'], 'S4 input/execution engine identity')
initial = inputs['engine_identity_initial']
require(inputs['engine_identity_before'] == inputs['engine'] == initial['before'] ==
execution['engine_identity']['before'], 'S4 immutable initial engine descriptor')
require(set(initial) == {'before', 'during', 'after'} and initial['after'] is None and
isinstance(initial['during'], list) and
initial['during'] == execution['engine_identity']['during'][:len(initial['during'])],
'S4 initial observations are an immutable prefix')
observations = execution['engine_observations']
require(any(row['stage'] == 'after-cleanup' for row in observations) and
all(row.get('descriptor') == inputs['engine'] for row in observations),
'S4 runtime engine observations')
stages = [row['stage'] for row in observations]
required = ['before-import', 'before-parse', 'before-writer', 'before-reader',
'after-collection', 'after-cleanup', 'after-output-collection']
require(all(stages.count(stage) == 1 for stage in required) and
[stages.index(stage) for stage in required] == sorted(stages.index(stage) for stage in required) and
all(row.get('descriptor') == inputs['engine'] and 'error' not in row for row in observations),
'S4 ordered runtime engine observations')
for phase in ('import', 'parse', 'writer', 'reader'):
index = stages.index('before-' + phase)
require(loads(self.read('engine-identity-before-' + phase + '.json')) ==
dict(observations[index], observation_index=index), 'S4 immutable per-phase engine receipt')
require(loads(self.read('engine-identity.json')) ==
dict(observations=observations, identity=execution['engine_identity']), 'S4 final engine receipt')
require(observations[stages.index('after-cleanup')]['descriptor'] == inputs['engine'],
'S4 exact after-cleanup engine descriptor')
require(execution['error'] is None and execution['guard_errors'] == [] and execution['cleanup_errors'] == [] and
[p['phase'] for p in execution['phases']] == ['import', 'parse', 'writer', 'reader'], 'S4 native phase/guard failure')
for phase in execution['phases']:
@@ -14,20 +14,24 @@ provenance only. Never open their original production locations.
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-r24"
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-r24/"
python3 "$B/audit-support/p04-s4-source-r24/run_s4_native.py" \
python3 "$E/s4/engine_identity_negative_fixtures.py"
python3 "$E/s4/receipt_lifecycle_fixtures.py"
mkdir "$B/audit-support/p04-s4-source-r26"
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-r26/"
python3 "$B/audit-support/p04-s4-source-r26/run_s4_native.py" \
--project-root "$B/audit-support/p04-r17-full-inputs-r01/product" \
--output "$B/p04-s4-native-r24" --plan "$B/p04-s4-plan-r24.json" \
--output "$B/p04-s4-native-r26" --plan "$B/p04-s4-plan-r26.json" \
--godot /Applications/Godot.app/Contents/MacOS/Godot --check-only
python3 "$B/audit-support/p04-s4-source-r24/run_s4_native.py" \
python3 "$B/audit-support/p04-s4-source-r26/run_s4_native.py" \
--project-root "$B/audit-support/p04-r17-full-inputs-r01/product" \
--output "$B/p04-s4-native-r24" --plan "$B/p04-s4-plan-r24.json" \
--output "$B/p04-s4-native-r26" --plan "$B/p04-s4-plan-r26.json" \
--godot /Applications/Godot.app/Contents/MacOS/Godot
```
The first invocation only prepares the pinned plan: zero engine launches. The
second is one collection, exactly four sequential owned processes: import,
Root must stop if either fixture or check-only fails; the commands are not an
unconditional batch. Both fixtures start zero engines. The check-only invocation
only prepares the pinned plan: zero engine launches. The final invocation
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;
@@ -37,7 +41,7 @@ inputs. Reader uses only the existing IO1 formal package mapping.
Exact input footprints:
- Current wrapper SHA256 is recorded in `../REPAIR24_STATUS.md`. The plan pins
- Historical wrapper hashes do not pin this repair. The fresh plan pins
its exact bytes and Stage R checks them against the current source checkout.
- `B/audit-support/p04-r17-full-inputs-r01/MANIFEST.json`, SHA256
`a15f29fa12537f6d8aaf7cabea390922a9f16e3eb9c10981e5742aafc8c4c8a8`.
@@ -48,7 +52,8 @@ Exact input footprints:
- The three staged S4 files above, pinned by the plan, and the installed executable
`/Applications/Godot.app/Contents/MacOS/Godot`, pinned directly against the
constant SHA before launch, during collection, and explicitly after cleanup.
Before/during/after descriptors are retained in runner-input and execution. The only stage override
Runner-input retains an immutable initial descriptor and observation prefix;
execution and engine-identity retain the final before/during/after identity. 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.
@@ -64,8 +69,8 @@ Exact input footprints:
proof and remains ineligible. Its diagnostics are at
`audit-support/p04-root-d393-s4-r01/ENGINE_STAGING_DIAGNOSTICS.json`.
Exact output footprints are `B/p04-s4-plan-r24.json` and the fresh
`B/p04-s4-native-r24/**`: private selected source stage, complete phase receipts
Exact output footprints are `B/p04-s4-plan-r26.json` and the fresh
`B/p04-s4-native-r26/**`: 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`,
@@ -107,8 +112,115 @@ restore and is not part of Runtime snapshot equality. The derived
`related_facility_operating` flag is measured separately and checked after restore.
This source-only dispatch authorizes no worker execution. Root stages the three
files, creates the r24 plan with --check-only, then collects once to the fresh
r24 output. Do not rerun S1-S3, C or full178. No engine copy, signing, xattr,
files, creates the r26 plan with --check-only, then collects once to the fresh
r26 output. Do not read or write r24 outputs, cleanup, stages or plans.
Do not rerun S1-S3, C or full178. No engine copy, signing, xattr,
codesign call or security-setting change is part of this procedure.
Native evidence, registered validation, independent review, support53cb rebind
and formal acceptance remain pending. D05 remains pending.
## REPAIR26 receipt lifecycle audit (static; NOT RUN)
All names below are relative to the fresh output unless marked otherwise.
`r.write` is the unchanged exclusive `open('x')` writer in
`tools/run_all_tests.py`. No retry, overwrite, replace or atomic rename is used
for a JSON proof. A write attempt may fail or leave a partial file; it is never
retried. “At most once” describes wrapper control flow, not a durability claim.
The initial input is serialized once with `engine=engine_descriptor`,
`engine_identity_before=engine_descriptor` and a deep-copied initial identity.
`repin()` itself is unchanged (it returns None); each per-phase receipt uses
the descriptor it just appended and the index of that exact observation.
| Writer / destination | Maximum attempts per run / uniqueness | Success and failure reachability |
| --- | --- | --- |
| `r.write(args.plan)` outside output | 1, check-only branch returns immediately | Native branch only reads the exact plan; existing plan rejected in check-only |
| `runner-started.json` | 1 | First write inside protected setup; exception enters cleanup |
| `source_before.json` | 1 | Setup before source copy; no fallback write |
| `selected-source-map.json` | 1 | Setup before source copy; no fallback write |
| `r.copy_source` to `stage/<source name>` | 1 per unique source-map key | Fresh stage; loop stops on setup failure; source files are not proof receipts |
| `write_bytes` to `bootstrap/<name>` | 1 per pinned bootstrap key | The reviewed map names `profile_guard.gd` and `parse_full_dependencies.gd`; setup only |
| `/bin/cp -c` to `stage/.godot/<name>` | 1 per cache inventory key | Optional eligible-cache branch only; fresh mode never accesses cache |
| `cache-reuse.json` | 0 or 1 | Only after all optional cache clones and guards |
| `parse-paths.json` | 1 | Setup after 316-script guard |
| `contracts.json` | 1 | Setup after unchanged suite-policy guard |
| `runner-input.json` | 1 | Setup; no final read/rewrite; absent if earlier setup fails |
| `engine-identity-before-<phase>.json` | 1 each for import, parse, writer, reader | Immediately after that phase's repin; failure stops loop, never resumes it |
| `write_bytes` to `writer-combined-raw.log`, `reader-combined-raw.log` | 1 each | Binary copy immediately after returned phase row, even a failed row if it has a log; later phases never reuse names |
| `<phase>-actual-phase.json` | 1 each for four unique phases | Before `require(row['passed'])`; mid-loop failure retains its row and stops later phases |
| `outer-owned-finally.json` | 1 | Single finally after setup/loop success or failure; cleanup called once; cleanup exception recorded; receipt write exception becomes guard error |
| `source_after.json` | 0 or 1 | Single post-cleanup guarded block, after fixed_guard/source_refs succeed; later guard failure does not re-enter or retry the write |
| `shutil.copyfile` to `formal/<role>/<slot>/<member>` | 1 per tuple | Only preliminary pass; two unique roles, unique slot map keys, four unique member keys, fresh target directory; exception stops collection |
| `formal-relocation.json` | 0 or 1 | Only after successful complete formal mirror |
| `raw-inventory.json` | 0 or 1 | Once after mirror branch, including setup/phase/cleanup failure; earlier output-collection exception can skip it |
| `PID-set.json` | 0 or 1 | Once after raw inventory; earlier output-collection exception can skip it |
| `engine-identity.json` | 1 | One final attempt after cleanup and both after-cleanup/after-output-collection repin attempts, including caught setup/phase/cleanup/collection failures; own write failure records guard error and makes both passed fields false |
| `execution.json` | 1 | After identity attempt, with final observations, identity and guard errors |
| `runner-ended.json` | 1 | After execution; no retry if final receipt storage itself fails |
The wrapper has no `write_text` call. The unchanged `execute_phase` dependency
also writes the following. Its shared text log is an explicit exception to
write-once naming, and is not presented as two independent phase proofs.
| Dependency destination | Writer and lifecycle |
| --- | --- |
| `<phase>-startup-profile-before.json` | Exclusive `write`, once per distinct phase before engine start |
| `<phase>-profile.json` | Guard report path for parse/writer/reader, one per phase; actual Guard checks file absence before writing |
| `starters/<phase>.gd` | `write_text`, once each for writer/reader |
| `<phase>/combined-raw.log` | `write_text`, once each for import/parse |
| `tests__s3__test_runtime_staff_assignment.gd.log` (general `out/<test>.log`) | Both writer and reader call `write_text` on this same per-test name; reader replaces the shared text. Wrapper preserves separate binary copies as `writer-combined-raw.log` and `reader-combined-raw.log`, retaining `strict_runner_log_path` and the original log hash |
| `<phase>-receipt.json` | Exclusive `write`, once per phase after its finally and guards |
| `stage/project.godot` | `write_bytes` for private startup settings and restoration in each phase finally; mutable private source, not immutable proof |
| `<phase>/**`, private profiles, raw QA outputs | Delegated owned-native capture/engine/QA outputs; not wrapper `r.write`/`write_bytes`/`write_text` calls; their existing ownership/diagnostic/formal guards remain unchanged |
Path coverage: success traverses each distinct phase once. A failed parse row is
recorded, then `require(row['passed'])` jumps to the single finally; no writer or
reader names are used. A protected setup failure also enters that finally, then
attempts post-cleanup guards and final identity/execution/end receipts. Cleanup
exceptions are caught separately, set `cleanup_errors`, and do not prevent final
receipts. A post-cleanup fixed/source guard failure is caught without retrying
`source_after`; formal/output-collection exceptions now become guard errors and
still reach the final repin and identity receipt. A final identity write failure
is caught without touching existing bytes and forces failure in execution/end.
Admission failures before the protected setup (invalid plan/product/engine,
live engine, insufficient disk, existing output, directory creation failure)
remain fail-closed and do not promise final receipts. They have no repeated
wrapper proof write. Persistent storage failure writing execution/end can still
prevent a complete terminal receipt pair; no code can guarantee receipt storage
when that storage fails. Such runs cannot provide accepted evidence.
## Fixture scope and remaining gaps
`receipt_lifecycle_fixtures.py` imports the real wrapper and standard writer.
It builds 1338 synthetic source entries including 316 scripts in resolved fresh
temporary paths; engine identity, template_guard, disk/default-player, helpers
and phase execution are mocked. Four distinct fake PIDs and complete synthetic
formal slot members exercise wrapper guards and copying without starting an
engine. The actual identity section of `S4Series.__init__` is compiled from its
AST, and actual `verify_s4_engine` checks a non-executable temporary data file
under fixture-only patched constants. Other verifier sections are not claimed
as covered by this synthetic fixture.
Cases cover success after check-only, parse failure, cleanup exception, setup
copy failure, post-cleanup collection failure and final identity write failure.
Every exclusive write attempt is counted. Input bytes are compared with their
creation bytes before negative mutation. Fresh-output refusal, each missing or
tampered phase receipt and mismatched initial-before are negative checks. The
fixture's deliberate proof mutations are confined to its disposable temporary
files. No registered plan/output/stage is read or changed.
Reasoning only about the 7af6 wrapper: import writes `engine-identity.json` via
exclusive create. Before parse, the same name raises FileExistsError, so the
four-phase success assertion cannot pass. After cleanup the old final write
tries that same name again and escapes main, preventing final execution/end.
Even if that collision alone were removed, the old runner-input final rewrite
would hit the exclusive writer, and the required initial/per-phase receipts
are absent. No 7af6 execution was performed to establish this reasoning.
Worker performed source inspection and file edits only: no fixtures, check-only,
tests, Git, engines, Runner, agents or commits. Root still must run both fixtures
and check-only before any native r26 collection, then exact-version verification
and independent review. The registered validation gate remains unchanged at
7af6, exit 2, fresh MANIFEST missing; this is the supplied gate record, not a new
worker run. Native/product proof, full formal checks, D05 and original P04-01
acceptance remain pending. This repair is submitted preparation, not completion.
@@ -33,7 +33,7 @@ def run():
# Same bytes/SHA cannot admit an own-B bare copy or another outside-B path.
for label, path in [
("copied bare engine in B with same SHA", s4.BUILD / "audit-support/p04-s4-source-r24/Godot"),
("copied bare engine in B with same SHA", s4.BUILD / "audit-support/p04-s4-source-r26/Godot"),
("other outside-B path", Path("/Applications/OtherGodot.app/Contents/MacOS/Godot")),
]:
other = dict(pin, path=str(path))
@@ -0,0 +1,255 @@
#!/usr/bin/env python3
"""Root-only synthetic receipt lifecycle fixtures. No engine/process is started.
Uses the real main, exclusive JSON writer, source_refs and inventories. The
identity section is compiled directly from S4Series.__init__, not reimplemented.
This is not native/product/ownership/formal acceptance evidence.
"""
import ast
from collections import Counter
from contextlib import ExitStack, redirect_stdout
import copy
import importlib.util
import io
import json
from pathlib import Path
import shutil
import sys
import tempfile
from types import SimpleNamespace
from unittest.mock import patch
sys.dont_write_bytecode = True
HERE = Path(__file__).resolve().parent
ROOT = next(p for p in HERE.parents if (p / 'tools/run_all_tests.py').is_file())
def load(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
s4 = load('receipt_s4', HERE / 'run_s4_native.py')
r = load('receipt_standard', ROOT / 'tools/run_all_tests.py')
series = load('receipt_series', HERE.parent / 'p04_native_series.py')
def identity_code():
tree = ast.parse((HERE.parent / 'p04_native_series.py').read_text())
cls = next(n for n in tree.body if isinstance(n, ast.ClassDef) and n.name == 'S4Series')
init = next(n for n in cls.body if isinstance(n, ast.FunctionDef) and n.name == '__init__')
start = next(i for i, n in enumerate(init.body) if isinstance(n, ast.Expr) and
isinstance(n.value, ast.Call) and isinstance(n.value.func, ast.Name) and
n.value.func.id == 'verify_s4_engine')
end = next(i for i in range(start + 1, len(init.body))
if "execution['error']" in ast.unparse(init.body[i]))
return compile(ast.Module(body=init.body[start:end], type_ignores=[]), '<real S4 identity section>', 'exec')
def verify_identity(out, descriptor):
# The real engine verifier reads only the synthetic ordinary file here.
# Production constants/functions on disk are never changed.
with patch.object(series, 'S4_ENGINE_PATH', descriptor['path']), \
patch.object(series, 'S4_ENGINE_SHA256', descriptor['sha256']):
namespace = dict(vars(series), inputs=r.read(out / 'runner-input.json'),
execution=r.read(out / 'execution.json'),
self=SimpleNamespace(read=lambda name: (out / name).read_bytes()))
exec(identity_code(), namespace)
def rejected(action):
try:
action()
except (ValueError, AssertionError, OSError, KeyError, TypeError):
return
raise AssertionError('negative fixture was accepted')
def case(base, failure=None):
product = base / 'product'
support = base / 'support'
staged = base / 'source'
for path in (product, support, staged):
path.mkdir(parents=True)
def put(path, data):
path.parent.mkdir(parents=True, exist_ok=True)
path.write_bytes(data)
# Real 1338-entry source_refs and 316-entry script roster, synthetic bytes.
scripts = [s4.TEST, 'tests/transport/test_runtime_transport.gd',
'tests/staff/test_rescue_elevator.gd']
scripts += ['scripts/fixture_%03d.gd' % i for i in range(313)]
for name in scripts:
put(product / name, b'extends Node\n')
for name in ('project.godot', 'export_presets.cfg', 'Makefile', 'AGENTS.md'):
put(product / name, b'fixture\n')
put(product / 'tools/run_all_tests.py', (ROOT / 'tools/run_all_tests.py').read_bytes())
for i in range(1017):
put(product / ('data/fixture_%04d.txt' % i), b'fixture\n')
assert len(r.source_refs(product)) == 1338
qa = staged / 'p04_s4_power_probe.gd'
put(qa, b'extends Node\n# synthetic override\n')
put(staged / 'run_s4_native.py', (HERE / 'run_s4_native.py').read_bytes())
put(staged / 'S4_ROOT_RUN.md', b'synthetic fixture material\n')
contracts = base / 'contracts.json'
r.write(contracts, {})
material = base / 'material.json'
r.write(material, {})
r.write(support / 'suite-policy.json', {'suite_policies': {}})
r.write(support / 'bootstrap-scripts.json', {
'parse_full_dependencies.gd': {'text': 'fixture\n', 'sha256': r.sha(product / 'Makefile')}})
engine = base / 'synthetic-engine-data'
put(engine, b'not an executable; never launched\n')
st = engine.stat()
descriptor = dict(path=str(engine), resolved_path=str(engine), bytes=st.st_size,
sha256=r.sha(engine), mtime_ns=st.st_mtime_ns, mode=st.st_mode,
device=st.st_dev, inode=st.st_ino, ctime_ns=st.st_ctime_ns)
out = base / 'p04-s4-native-r26'
plan = base / 'p04-s4-plan-r26.json'
attempts, created, phases = Counter(), {}, []
cleanup_calls = []
real_write, real_inventory = r.write, r.inventory
protected = dict(path=str(base / 'default-player'), existed=False, files={}, logs_and_locks_included=True)
def write(path, value):
path = Path(path)
attempts[path] += 1
if failure == 'identity-write' and path == out / 'engine-identity.json':
raise OSError('injected final identity write error')
real_write(path, value) # unchanged open('x'), including collisions
created[path] = path.read_bytes()
def finish(owned):
cleanup_calls.append(list(owned))
if failure == 'cleanup':
raise RuntimeError('injected cleanup error')
return [dict(pid=p.pid, engine_ended=True) for p in owned], []
outer = SimpleNamespace(native=SimpleNamespace(helper={'live_engines': lambda: []}),
OWNED=[SimpleNamespace(pid=91001+i) for i in range(4)],
finish_captured=finish, run_phase=lambda *a, **k: None)
def execute(engine_arg, stage, output, phase, test, selected, expected, previous,
policy, contracts_arg, paths, public, player, root, *helpers):
phases.append(phase)
assert engine_arg == str(engine) and output == out and player == protected
assert r.source_refs(root) == selected['root_before']
assert r.inventory(stage, stage=True) == dict(expected, **previous)
pid = outer.OWNED[len(phases)-1].pid
row = dict(phase=phase, passed=not (failure == 'parse' and phase == 'parse'), actual_pid=pid)
if test:
slots = {}
for slot in s4.SLOTS:
directory = base / 'profiles' / phase / slot
for member in ('journal.log', 'manifest.json', 'thumbnail.png', 'world.bin'):
put(directory / member, (slot + '/' + member).encode())
slots[slot] = str(directory)
(out / 'raw').mkdir(exist_ok=True)
write(out / ('raw/' + phase + '.json'), dict(checks=1, failures=0, branches=['S4'],
slots=slots, pid=pid, profile=str(base / 'profiles' / phase)))
# Reproduce the runner's shared text log and let main copy its bytes.
log = test.replace('/', '__') + '.log'
(out / log).write_text(phase + '\n')
row['log'] = log
return row
def inventory(path, **kwargs):
if failure == 'output' and Path(path) == out / 'raw':
raise OSError('injected post-cleanup inventory error')
return real_inventory(path, **kwargs)
def copy_source(source, target):
if failure == 'setup':
raise OSError('injected setup copy error')
shutil.copyfile(source, target)
def modules(name, path):
if name == 's4_standard_runner':
return r
if name == 's4_owned_outer':
return outer
if name == 's4_markers':
return SimpleNamespace(load_contracts=lambda path: {})
if name in ('s4_diagnostics', 's4_owners', 's4_audio'):
return SimpleNamespace()
raise AssertionError('unexpected helper import: ' + name)
with ExitStack() as stack:
for obj, values in [(s4, dict(BUILD=base, PRODUCT=product, HERE=staged, QA=qa,
ENGINE=engine, CONTRACTS=contracts, MATERIALS=[material],
__file__=str(staged / 'run_s4_native.py'), module=modules)),
(r, dict(SUPPORT=support, write=write, execute_phase=execute,
copy_source=copy_source, inventory=inventory))]:
for key, value in values.items():
stack.enter_context(patch.object(obj, key, value))
pin_mock = stack.enter_context(patch.object(s4, 'engine_pin', return_value=descriptor))
guard_mock = stack.enter_context(patch.object(s4, 'template_guard', return_value=None))
player_mock = stack.enter_context(patch.object(r, 'default_player', return_value=protected))
disk_mock = stack.enter_context(patch.object(s4.shutil, 'disk_usage',
return_value=SimpleNamespace(free=10 * 1024**3)))
stack.enter_context(redirect_stdout(io.StringIO()))
argv = ['run_s4_native.py', '--project-root', str(product), '--output', str(out),
'--plan', str(plan), '--godot', str(engine)]
with patch.object(sys, 'argv', argv + ['--check-only']):
assert s4.main() == 0
assert not phases and not cleanup_calls and not out.exists()
with patch.object(sys, 'argv', argv):
result = s4.main()
rejected(s4.main) # same output must be rejected before any new writes
assert pin_mock.called and guard_mock.called and player_mock.called and disk_mock.called
assert result == (0 if failure is None else 1)
assert len(cleanup_calls) == 1
assert all(count == 1 for count in attempts.values()), attempts
for leaf in ('outer-owned-finally.json', 'engine-identity.json', 'execution.json', 'runner-ended.json'):
assert attempts[out / leaf] == 1
if failure != 'identity-write' or leaf != 'engine-identity.json':
assert (out / leaf).is_file()
if failure != 'setup':
assert (out / 'runner-input.json').read_bytes() == created[out / 'runner-input.json']
assert r.read(out / 'runner-input.json')['engine_identity_initial']['after'] is None
execution = r.read(out / 'execution.json')
assert execution['passed'] is (failure is None)
assert 'FileExistsError' not in json.dumps(execution)
assert phases == ([] if failure == 'setup' else ['import', 'parse'] if failure == 'parse'
else ['import', 'parse', 'writer', 'reader'])
if failure == 'cleanup':
assert execution['cleanup_errors'] == ['injected cleanup error']
if failure in ('output', 'identity-write'):
assert execution['guard_errors']
if failure is None:
assert len(list(out.glob('engine-identity*.json'))) == 5
verify_identity(out, descriptor)
assert (out / 'writer-combined-raw.log').read_bytes() == b'writer\n'
assert (out / 'reader-combined-raw.log').read_bytes() == b'reader\n'
# Mutations below affect only this disposable fixture's synthetic proofs.
for phase in ('import', 'parse', 'writer', 'reader'):
path = out / ('engine-identity-before-' + phase + '.json')
original = path.read_bytes()
path.write_text('{}')
rejected(lambda: verify_identity(out, descriptor))
path.unlink()
rejected(lambda: verify_identity(out, descriptor))
path.write_bytes(original)
path = out / 'runner-input.json'
initial = r.read(path)
changed = copy.deepcopy(initial)
changed['engine_identity_initial']['before']['inode'] += 1
path.write_text(json.dumps(changed))
rejected(lambda: verify_identity(out, descriptor))
path.write_bytes(created[path])
verify_identity(out, descriptor)
return dict(case=failure or 'success', passed=True, native_engine_launches=0)
def run():
with tempfile.TemporaryDirectory(prefix='s4-receipt-fixtures-') as temporary:
return [case(Path(temporary).resolve() / (failure or 'success'), failure)
for failure in (None, 'parse', 'cleanup', 'setup', 'output', 'identity-write')]
if __name__ == '__main__':
print(json.dumps(dict(results=run(), formal_acceptance=False), indent=2))
@@ -2,6 +2,7 @@
"""S4-only native entry. --check-only never loads ownership helpers or starts Godot."""
from pathlib import Path
import argparse
import copy
import hashlib
import importlib.util
import json
@@ -98,8 +99,8 @@ def engine_pin(path, expected=None):
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-r24")
parser.add_argument("--plan", type=Path, default=BUILD / "p04-s4-plan-r24.json")
parser.add_argument("--output", type=Path, default=BUILD / "p04-s4-native-r26")
parser.add_argument("--plan", type=Path, default=BUILD / "p04-s4-plan-r26.json")
parser.add_argument("--cache-run", type=Path, default=None,
help="optional passed registered cache; default is fresh, without cache access")
parser.add_argument("--godot", type=Path, default=ENGINE)
@@ -222,9 +223,9 @@ def main():
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 / "runner-started.json", dict(started=r.now(), owner_runner_pid=os.getpid(),
plan=pin(args.plan), engines_launched=0))
r.write(out / "source_before.json", source_before)
r.write(out / "selected-source-map.json", refs)
# Private source stage first, then literal reviewed bootstrap.
@@ -262,7 +263,9 @@ def main():
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, engine_identity=engine_identity, runner_sha256=r.sha(root / "tools/run_all_tests.py"),
wrapper_sha256=r.sha(__file__), QA=pin(QA), engine=engine_descriptor,
engine_identity_before=engine_descriptor, engine_identity_initial=copy.deepcopy(engine_identity),
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")
@@ -278,12 +281,15 @@ def main():
r.require(not (stage / ".godot").exists() and not (stage / ".godot").is_symlink(),
"fresh private .godot absent at actual import")
repin("before-" + phase)
r.write(out / "engine-identity.json", engine_observations)
r.write(out / ("engine-identity-before-" + phase + ".json"),
dict(engine_observations[-1], observation_index=len(engine_observations)-1))
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.
# execute_phase writer/reader share out/<test>.log via write_text.
# Keep binary per-phase copies under these existing unique names.
# The shared runner log is not write-once; see S4_ROOT_RUN.md.
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"]
@@ -349,36 +355,43 @@ def main():
# 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))
try:
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))
except BaseException as caught:
guards.append("output collection: " + type(caught).__name__ + ": " + str(caught))
passed = False
execution["passed"] = False
try:
repin("after-output-collection")
except BaseException as caught:
guards.append(str(caught))
passed = False
execution["passed"] = False
r.write(out / "engine-identity.json", engine_observations)
try:
r.write(out / "engine-identity.json",
dict(observations=engine_observations, identity=engine_identity))
except BaseException as caught:
guards.append("engine identity receipt: " + type(caught).__name__ + ": " + str(caught))
passed = False
execution["passed"] = False
execution["engine_observations"] = engine_observations
execution["engine_identity"] = engine_identity
if (out / "runner-input.json").is_file():
final_inputs = r.read(out / "runner-input.json")
final_inputs["engine_identity"] = engine_identity
r.write(out / "runner-input.json", final_inputs)
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))))