Merge commit '53cb52da9ce87f15d56f9443790da0f9de29be58' into HEAD
This commit is contained in:
+2
-2
@@ -78,7 +78,7 @@
|
||||
"template": "Required kitchen: <checks> checks, <failures> failures",
|
||||
"required": true,
|
||||
"fixed_check_count_used": false,
|
||||
"source_sha256_at_schema_audit": "90e7639f5aa76e34b9ee14769f4ec1ae1317a647900b27877075dbf21ef1dfcf",
|
||||
"source_sha256_at_schema_audit": "3a9c92f80c24064ef0858fba10bd7caab63203932942837f7e142349578f6b2f",
|
||||
"authority": {
|
||||
"path": "/Users/korizatou114/Projects/とりにく工房ゲーム制作部/artifacts/original-completion-2026-10-02/p09-kitchen-single-link-candidate-r09/candidates/tests/business/test_required_kitchen.gd",
|
||||
"sha256": "90e7639f5aa76e34b9ee14769f4ec1ae1317a647900b27877075dbf21ef1dfcf",
|
||||
@@ -86,7 +86,7 @@
|
||||
},
|
||||
"source_marker_lines": [
|
||||
{
|
||||
"line": 281,
|
||||
"line": 481,
|
||||
"text": "\tprint(\"Required kitchen: %d checks, %d failures\" % [checks, failures])"
|
||||
}
|
||||
]
|
||||
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
{
|
||||
"task_id": "P18-REG-KITCHEN-LEGACY14",
|
||||
"base_commit": "9b1a18c1eb25ca1300dcd59da78423c0dda2f498",
|
||||
"state": "code-ready; tests NOT RUN; Root native proof pending",
|
||||
"canonical_source_delta": {
|
||||
"authority_file": "MAIN9B_COMPLETE_SOURCE_REFS.json",
|
||||
"authority_bytes": 376039,
|
||||
"authority_sha256": "dc43e665be3f02d14513f0b8c4248d410175dcfb118171d8cbe0e62ef764a2e4",
|
||||
"canonical_count": 1338,
|
||||
"docs_in_canonical_map": false,
|
||||
"path": "tests/business/test_required_kitchen.gd",
|
||||
"old": {"bytes": 31533, "sha256": "5dcc86d66bdc47003472d6c30d6889eb514e016003265965bdd8496d123d5ca5"},
|
||||
"new": {"bytes": 38276, "sha256": "3a9c92f80c24064ef0858fba10bd7caab63203932942837f7e142349578f6b2f"},
|
||||
"other_entries_same_keys_and_hashes": 1337,
|
||||
"added_keys": [],
|
||||
"dropped_keys": []
|
||||
},
|
||||
"marker_delta": {
|
||||
"path": "docs/plans/original-completion-2026-10-02/execution/completion_marker_contracts.json",
|
||||
"separate_fixed_authority": true,
|
||||
"old": {"bytes": 188089, "sha256": "6ce6b28c5985e8a4f13b78bfeb1f1e09f6216d874fb01e0656ec1633bfa01cd7"},
|
||||
"new": {"bytes": 188089, "sha256": "9c095df16eb6848e5b3b34cd78aef1c57a7724e875eb8371d3ff4c2c8cc349a1"},
|
||||
"changed_rows": [{
|
||||
"test": "tests/business/test_required_kitchen.gd",
|
||||
"source_sha256_at_schema_audit": {
|
||||
"old": "90e7639f5aa76e34b9ee14769f4ec1ae1317a647900b27877075dbf21ef1dfcf",
|
||||
"new": "3a9c92f80c24064ef0858fba10bd7caab63203932942837f7e142349578f6b2f"
|
||||
},
|
||||
"source_marker_lines": {
|
||||
"old": [{"line": 281, "text": "\tprint(\"Required kitchen: %d checks, %d failures\" % [checks, failures])"}],
|
||||
"new": [{"line": 481, "text": "\tprint(\"Required kitchen: %d checks, %d failures\" % [checks, failures])"}]
|
||||
}
|
||||
}],
|
||||
"authority_records_changed": false,
|
||||
"other_rows_changed": false,
|
||||
"fixed_check_count_used": false,
|
||||
"predicted_native_checks": 163,
|
||||
"actual_native_checks": null
|
||||
},
|
||||
"new_support_path_group": {
|
||||
"path": "docs/plans/original-completion-2026-10-02/execution/p18-kitchen-legacy-support/",
|
||||
"files": ["plan.md", "delta-manifest.json", "verify_support.py", "root_diagnostic.py"]
|
||||
},
|
||||
"full_selected_map": {
|
||||
"required_count": 1339,
|
||||
"composition": "current canonical 1338 plus exactly the fixed reviewed P04 extra",
|
||||
"extra_path": "tools/check_p04_order_evidence.py",
|
||||
"extra_bytes": 78235,
|
||||
"extra_sha256": "f5b44827958f2ae352fe5c4bc3a1ffd4211ec7a41fc4c0759c8b954891662324"
|
||||
},
|
||||
"zero_changes": ["autoload", "scripts", "scenes", "resources", "project.godot", "export", "policy", "engine", "legacy_fixture"],
|
||||
"grep": {
|
||||
"pattern": "test_required_kitchen\\.gd",
|
||||
"canonical_source_reference_search_exit": 1,
|
||||
"canonical_source_reference_matches": [],
|
||||
"repository_reference_matches": "docs only (including archived manifests); edited definition is the kitchen test itself",
|
||||
"source_consumers_outside_the_test": 0
|
||||
},
|
||||
"existing_source_manifest_rewritten": false,
|
||||
"generic_exclusions_added": false,
|
||||
"tests_run": false,
|
||||
"independent_review": "pending",
|
||||
"integration": "pending",
|
||||
"native_Windows": "not claimed"
|
||||
}
|
||||
+120
@@ -0,0 +1,120 @@
|
||||
# P18-REG-KITCHEN-LEGACY14 source submission
|
||||
|
||||
Requirement: `legacy_preservation`. Base remains
|
||||
`9b1a18c1eb25ca1300dcd59da78423c0dda2f498`. This is uncounted support;
|
||||
original30/88 and the acceptance denominator do not change. Product code, P04,
|
||||
settings, thresholds, suite policy, fixture bytes and fixture authority are unchanged.
|
||||
|
||||
## Diagnosis and exact migration
|
||||
|
||||
The old14 writer is v1, while the approved P18 relationship service saves a v2
|
||||
container. The five typed differences are all below
|
||||
`/systems/runtime/relationships`: version 1 becomes 2, `legacy` is added,
|
||||
`original_relationships` is added, and `threshold_candidates` and
|
||||
`next_candidate_sequence` move into `legacy`. The retained full native typed
|
||||
inputs are 159552 and 159680 bytes, an increase of 128 bytes. Their SHA-256 values
|
||||
are respectively `a2f8992853ba9497539cdfcb74d22c83c76011f2acf73f9e8e3e7241620ddca0`
|
||||
and `a2f3e75181071f94946369df6f3a6256998dd7d776441766bccc9b9c3ccf5d8d`.
|
||||
They diagnose the earlier failure; they are never substituted into a new run.
|
||||
|
||||
The genuine local `write-checkpoint.var` contains the old v1 dictionary at byte
|
||||
offsets [143668,143816): **148 bytes**, SHA-256
|
||||
`ed2cafb629f4d08249f8cd147c7ff9fc715ff641c24809a93e66a1229d4105c4`.
|
||||
This was obtained by reading the original fixture bytes, including its dictionary
|
||||
header, StringName keys, typed empty Dictionary arrays, and int values. The new
|
||||
native proof independently hashes `var_to_bytes(old)` and the whole fixture file.
|
||||
|
||||
The pure validator requires the exact old field set/key encoding, int version 1,
|
||||
byte-identical nested legacy, int version 2, exact v2 field set, unchanged sequence,
|
||||
byte-identical compatibility projection and typed empty original records. It builds
|
||||
the container in the service's insertion order with StringName keys and typed
|
||||
`Array[Dictionary]`. Original values stay unconfigured: no conversion from old
|
||||
five-axis values and no guessed original initialization.
|
||||
|
||||
After the existing identity/saveID-bound IO1 replacement, a reader copy with just
|
||||
relationships reversed to v1 must equal the entire expected writer byte stream.
|
||||
Only that subtree is then replaced. Whole typed bytes, Dictionary equality,
|
||||
SimulationDigest, IO validity, envelope, replaced==1 and published IDs remain
|
||||
required. Only legacy14 opts in. new14 has the same behavior and proof shape.
|
||||
|
||||
Seventeen in-memory controls cover an outside change, each legacy field deleted
|
||||
and changed, sequence +1, nonempty originals, actual versions 0/3/string, equal
|
||||
reordered/reencoded dictionaries, and a byte-identical deep-copy positive control.
|
||||
Every attempt checks both caller inputs and both attempted copies for mutation.
|
||||
There are 17 new assertions plus one fresh proof-file assertion, so the native
|
||||
success path is expected to report 163 checks (the existing 145 remain). This
|
||||
arithmetic is a gate expectation, not an observed native result. The final print
|
||||
wording and schema are unchanged; the marker contract continues to derive counts
|
||||
from raw output (`fixed_check_count_used: false`).
|
||||
|
||||
## Source map and authority
|
||||
|
||||
`MAIN9B_COMPLETE_SOURCE_REFS.json` was read locally after its 376039-byte SHA-256
|
||||
`dc43e665be3f02d14513f0b8c4248d410175dcfb118171d8cbe0e62ef764a2e4`
|
||||
was checked. Its canonical 1338 keys exclude docs. The only changed selected entry
|
||||
is `tests/business/test_required_kitchen.gd`; all other 1337 keys and hashes match.
|
||||
The selected map hash before this change is
|
||||
`533a4177918a9da4d4982aa9862cb7420c25d30c2458e36aab9c071c4d3a7139`.
|
||||
Absolute production paths in those records are provenance, never input paths.
|
||||
|
||||
The completion-marker contract is a separately pinned, second changed existing
|
||||
file. Its pre-change bytes were 188089 with SHA-256
|
||||
`6ce6b28c5985e8a4f13b78bfeb1f1e09f6216d874fb01e0656ec1633bfa01cd7`.
|
||||
Only the kitchen source SHA and actual marker line position change; authority,
|
||||
template, count policy and other entries remain byte-for-byte unchanged.
|
||||
The newly added support directory is the third allowed path group. The tree as a
|
||||
whole is not described as exactly two paths. See `delta-manifest.json`.
|
||||
|
||||
Root's selected full-run map must be exactly these current 1338 files plus
|
||||
`tools/check_p04_order_evidence.py`, 78235 bytes, SHA-256
|
||||
`f5b44827958f2ae352fe5c4bc3a1ffd4211ec7a41fc4c0759c8b954891662324`.
|
||||
This is the fixed supplied reviewed P04 QA source, not permission to edit P04 or
|
||||
read its build. The verifier compares local relative keys and full file pins,
|
||||
rejecting additions, drops and stale sources. It introduces no selector exclusions.
|
||||
|
||||
Static reference search:
|
||||
|
||||
```sh
|
||||
rg -l 'test_required_kitchen\.gd' autoload scripts scenes data tests assets tools project.godot export_presets.cfg Makefile AGENTS.md
|
||||
rg -l 'test_required_kitchen\.gd' docs
|
||||
```
|
||||
|
||||
The first command returned no matches (exit 1). The second finds documentation
|
||||
and archived manifests only; there are no other source consumers of the test.
|
||||
The test itself is the edited definition. This search does not rewrite manifests.
|
||||
|
||||
## Root-only finite local diagnostic procedure
|
||||
|
||||
Worker has run **no tests, engine, Runner, Git or agents**. Python support files
|
||||
were compiled in memory only. GDScript import/parse and native results are pending.
|
||||
Root must use the existing `godot` guarded launcher supported by the current tools;
|
||||
do not point any command directly at a player profile. Run from this checkout in
|
||||
one shell, stop on the first nonzero exit, and do not retry into an existing output.
|
||||
If an output already exists, retain it and obtain a newly registered build before
|
||||
a new collection. The helper uses the existing `execute_phase`, Guard, owned outer
|
||||
launcher and `run_p18_relationship_tests.py` roster/archive APIs. It changes no
|
||||
production collector, policy or threshold. All source copying is private.
|
||||
|
||||
```sh
|
||||
export BUILD_OUTPUT_DIR=/Users/korizatou114/Projects/TheTree-managed-worktrees/p18-kitchen-regression-r02/.runtime/P18-REG-KITCHEN-LEGACY14/build
|
||||
set -e
|
||||
python3 docs/plans/original-completion-2026-10-02/execution/p18-kitchen-legacy-support/root_diagnostic.py --phase parse
|
||||
python3 docs/plans/original-completion-2026-10-02/execution/p18-kitchen-legacy-support/root_diagnostic.py --phase kitchen
|
||||
python3 docs/plans/original-completion-2026-10-02/execution/p18-kitchen-legacy-support/root_diagnostic.py --phase affected
|
||||
python3 docs/plans/original-completion-2026-10-02/execution/p18-kitchen-legacy-support/root_diagnostic.py --phase full
|
||||
python3 docs/plans/original-completion-2026-10-02/execution/p18-kitchen-legacy-support/verify_support.py --verify-only --marker-contracts docs/plans/original-completion-2026-10-02/execution/completion_marker_contracts.json
|
||||
```
|
||||
|
||||
Order is import/all-script parse, required_kitchen alone, all business plus all
|
||||
s4 relationship tests/all s6/P18 affected set, then the new standard full178.
|
||||
Every phase retains import/parse and exact-source receipts; the full phase invokes
|
||||
the unchanged `run_all_tests.main` with the explicit 1339 source input and marker
|
||||
contract. `archive_profiles` retains all actual private profiles under the build,
|
||||
including the kitchen typed proofs, checked against native inventories. The
|
||||
read-only verifier reads those receipts, raw stdout/stderr/native logger streams,
|
||||
and PID lifecycle evidence; it launches nothing and emits no PASS of its own.
|
||||
Missing future evidence correctly exits nonzero and does not block writing code.
|
||||
|
||||
Root will formally verify P04 separately in P04A repair. Independent Opus review,
|
||||
integration and final verification remain required before any original-unit credit.
|
||||
No native Windows behavior or whole-unit completion is claimed.
|
||||
+126
@@ -0,0 +1,126 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Root-only finite diagnostics, using existing guarded native phase APIs.
|
||||
|
||||
This is an execution procedure, not worker evidence or an acceptance gate.
|
||||
"""
|
||||
import argparse
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
import shutil
|
||||
import sys
|
||||
import uuid
|
||||
|
||||
sys.dont_write_bytecode = True
|
||||
ROOT = Path(__file__).resolve().parents[5]
|
||||
sys.path.insert(0, str(ROOT / 'tools'))
|
||||
import run_p18_relationship_tests as p18
|
||||
|
||||
s = p18.shared
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(allow_abbrev=False)
|
||||
parser.add_argument('--phase', required=True, choices=['parse', 'kitchen', 'affected', 'full'])
|
||||
args = parser.parse_args()
|
||||
build = Path(os.environ['BUILD_OUTPUT_DIR']).resolve(strict=True)
|
||||
out = build / {'parse': 'native-parse', 'kitchen': 'native-kitchen',
|
||||
'affected': 'native-affected', 'full': 'native-full178'}[args.phase]
|
||||
s.require(not out.exists(), 'fresh output; no automatic retry or evidence splice')
|
||||
prior = {'kitchen': 'native-parse', 'affected': 'native-kitchen', 'full': 'native-affected'}
|
||||
if args.phase in prior:
|
||||
previous = s.read(build / prior[args.phase] / 'runner-ended.json')
|
||||
s.require(previous['error'] is None and previous['cleanup_errors'] == []
|
||||
and previous['guard_errors'] == [], 'preceding diagnostic must finish successfully')
|
||||
refs = s.source_refs(ROOT)
|
||||
checker = build / 'audit-support/root-input-r01/P04_REVIEWED_CANDIDATE_CHECKER.py'
|
||||
s.require(checker.stat().st_size == 78235 and s.sha(checker)
|
||||
== 'f5b44827958f2ae352fe5c4bc3a1ffd4211ec7a41fc4c0759c8b954891662324', 'fixed local P04 extra')
|
||||
extra = {'path': str(checker), 'bytes': 78235, 'sha256': s.sha(checker)}
|
||||
s.require('tools/check_p04_order_evidence.py' not in refs
|
||||
or {k: refs['tools/check_p04_order_evidence.py'][k] for k in ('bytes', 'sha256')}
|
||||
== {k: extra[k] for k in ('bytes', 'sha256')}, 'no replacement of a different P04 source')
|
||||
refs['tools/check_p04_order_evidence.py'] = extra
|
||||
s.require(len(refs) == 1339, 'prospective full source set')
|
||||
marker_path = ROOT / 'docs/plans/original-completion-2026-10-02/execution/completion_marker_contracts.json'
|
||||
if args.phase == 'full':
|
||||
selected = build / 'kitchen-selected-1339.json'
|
||||
s.write(selected, refs)
|
||||
code = s.main(['--project-root', str(ROOT), '--output', str(out), '--marker-contracts', str(marker_path),
|
||||
'--source-input-refs', str(selected)])
|
||||
# Retain the actual profiles under BUILD_OUTPUT_DIR while they still
|
||||
# exist. Archive helper checks every byte against native phase inventory.
|
||||
for row in s.read(out / 'runner-ended.json')['phases']:
|
||||
p18.archive_profiles(out, row)
|
||||
return code
|
||||
root_before = s.source_refs(ROOT)
|
||||
roster = [] if args.phase == 'parse' else ['tests/business/test_required_kitchen.gd']
|
||||
if args.phase == 'affected':
|
||||
roster = sorted(set(n for n in refs if n.startswith('tests/business/test') and n.endswith('.gd'))
|
||||
| set(p18.ROSTER)
|
||||
| set(n for n in refs if n.startswith('tests/s6/test') and n.endswith('.gd'))
|
||||
| set(n for n in refs if n.startswith('tests/s4/test') and n.endswith('.gd') and 'relationship' in n))
|
||||
markers = s.module('kitchen_diagnostic_markers', ROOT / 'tools/strict_test_markers.py')
|
||||
contracts = markers.load_contracts(marker_path)
|
||||
policy = s.read(s.SUPPORT / 'suite-policy.json')
|
||||
s.policy_coverage(roster, refs, policy)
|
||||
engine = shutil.which('godot')
|
||||
s.require(engine, 'existing guarded native launcher required')
|
||||
engine = str(Path(engine).resolve())
|
||||
outer = s.module('kitchen_diagnostic_outer', s.SUPPORT / 'owned_outer.py')
|
||||
s.require(not outer.native.helper['live_engines'](), 'one owned engine; no concurrent run')
|
||||
s.require(shutil.disk_usage(build).free >= sum(p['bytes'] for p in refs.values()) + s.MIN_SPACE, 'unchanged disk admission')
|
||||
out.mkdir()
|
||||
stage = out / 'stage'; stage.mkdir()
|
||||
(out / 'starters').mkdir(); (out / 'bootstrap').mkdir()
|
||||
for name, entry in s.read(s.SUPPORT / 'bootstrap-scripts.json').items():
|
||||
data = entry['text'].encode()
|
||||
s.require(hashlib.sha256(data).hexdigest() == entry['sha256'], 'exact existing bootstrap')
|
||||
(out / 'bootstrap' / name).write_bytes(data)
|
||||
for name, entry in refs.items():
|
||||
target = stage / name; target.parent.mkdir(parents=True, exist_ok=True)
|
||||
s.copy_source(Path(entry['path']), target)
|
||||
expected = {n: {k: p[k] for k in ('bytes', 'sha256')} for n, p in refs.items()}
|
||||
s.require(s.inventory(stage, stage=True) == expected, 'whole private source copy')
|
||||
public = (ROOT / 'project.godot').read_bytes()
|
||||
protected = s.default_player(ROOT)
|
||||
paths = ['res://' + n for n in sorted(refs) if n.endswith('.gd')]
|
||||
s.write(out / 'parse-paths.json', paths)
|
||||
selected = {'root_before': root_before, 'map': {n: p['sha256'] for n, p in refs.items()}, 'timeout': 1200}
|
||||
run_id = uuid.uuid4().hex
|
||||
s.write(out / 'runner-input.json', {'protocol': 'kitchen-diagnostic-v1', 'run_id': run_id,
|
||||
'roster': roster, 'selected_source_map': selected['map'], 'default_player_before': protected})
|
||||
s.write(out / 'source-start.json', root_before)
|
||||
diagnostic = s.module('kitchen_diag_gate', s.SUPPORT / 'diagnostic_gate.py')
|
||||
owners = s.module('kitchen_diag_owners', s.SUPPORT / 'ordinary_proof_gate.py')
|
||||
audio = s.module('kitchen_diag_audio', s.SUPPORT / 'audio_evidence_gate.py')
|
||||
phases = []; rows = []; generated = {}; error = None; guard_errors = []
|
||||
try:
|
||||
for phase, test in [('import', None), ('parse', None)] + [(f'{i:03}', n) for i, n in enumerate(roster)]:
|
||||
row = s.execute_phase(engine, stage, out, phase, test, selected, expected, generated,
|
||||
policy, contracts, paths, public, protected, ROOT, outer, markers, diagnostic, owners, audio)
|
||||
p18.archive_profiles(out, row)
|
||||
phases.append(row)
|
||||
if test:
|
||||
row.update(sequence=len(rows), godot=engine); rows.append(row)
|
||||
(out / 'all-tests.json').write_text(json.dumps(rows, ensure_ascii=False, indent=2) + '\n')
|
||||
s.require(row['passed'], 'first strict native failure stops: ' + phase)
|
||||
except BaseException as caught:
|
||||
error = type(caught).__name__ + ': ' + str(caught)
|
||||
finally:
|
||||
cleanup, cleanup_errors = outer.finish_captured(outer.OWNED)
|
||||
try:
|
||||
s.require(s.source_refs(ROOT) == root_before and s.default_player(ROOT) == protected, 'whole source/default unchanged')
|
||||
s.require(s.inventory(stage, stage=True) == dict(expected, **generated), 'whole stage unchanged')
|
||||
s.require(all(r['engine_ended'] for r in cleanup), 'all native PIDs ended')
|
||||
except BaseException as caught:
|
||||
guard_errors.append(str(caught))
|
||||
s.write(out / 'runner-ended.json', {'protocol': 'kitchen-diagnostic-v1', 'run_id': run_id, 'error': error,
|
||||
'cleanup_errors': cleanup_errors, 'guard_errors': guard_errors, 'final_owned_cleanup': cleanup,
|
||||
'phases': phases, 'default_player_after': s.default_player(ROOT)})
|
||||
return int(error is not None or bool(cleanup_errors) or bool(guard_errors))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
raise SystemExit(main())
|
||||
+284
@@ -0,0 +1,284 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Read-only Root evidence gate. No collection, engine launch, or PASS emission."""
|
||||
import argparse
|
||||
import hashlib
|
||||
import importlib.util
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
import sys
|
||||
|
||||
sys.dont_write_bytecode = True
|
||||
ROOT = Path(__file__).resolve().parents[5]
|
||||
KITCHEN = 'tests/business/test_required_kitchen.gd'
|
||||
MARKER = 'docs/plans/original-completion-2026-10-02/execution/completion_marker_contracts.json'
|
||||
EXTRA = 'tools/check_p04_order_evidence.py'
|
||||
EXTRA_PIN = {'bytes': 78235, 'sha256': 'f5b44827958f2ae352fe5c4bc3a1ffd4211ec7a41fc4c0759c8b954891662324'}
|
||||
BASE_PIN = {'bytes': 376039, 'sha256': 'dc43e665be3f02d14513f0b8c4248d410175dcfb118171d8cbe0e62ef764a2e4'}
|
||||
MARKER_PIN = {'bytes': 188089, 'sha256': '6ce6b28c5985e8a4f13b78bfeb1f1e09f6216d874fb01e0656ec1633bfa01cd7'}
|
||||
|
||||
|
||||
def require(ok, reason):
|
||||
if not ok:
|
||||
raise ValueError(reason)
|
||||
|
||||
|
||||
def pin(path):
|
||||
data = path.read_bytes()
|
||||
return {'bytes': len(data), 'sha256': hashlib.sha256(data).hexdigest()}
|
||||
|
||||
|
||||
def unique(pairs):
|
||||
result = {}
|
||||
for key, value in pairs:
|
||||
require(key not in result, 'duplicate JSON key: ' + key)
|
||||
result[key] = value
|
||||
return result
|
||||
|
||||
|
||||
def read(path):
|
||||
return json.loads(path.read_bytes(), object_pairs_hook=unique)
|
||||
|
||||
|
||||
def local(base, name):
|
||||
name = Path(name)
|
||||
require(not name.is_absolute() and '..' not in name.parts, 'relative evidence key required')
|
||||
path = base / name
|
||||
require(path.resolve().is_relative_to(base.resolve()), 'evidence escapes build directory')
|
||||
require(not any(p.is_symlink() for p in [path, *path.parents] if p != base.parent), 'evidence alias')
|
||||
return path
|
||||
|
||||
|
||||
def load(name, path):
|
||||
spec = importlib.util.spec_from_file_location(name, path)
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(module)
|
||||
return module
|
||||
|
||||
|
||||
def pins(refs):
|
||||
# Absolute production paths are provenance only. Never open them.
|
||||
return {name: {key: value[key] for key in ('bytes', 'sha256')} for name, value in refs.items()}
|
||||
|
||||
|
||||
def exited(pid):
|
||||
require(type(pid) is int and pid > 0, 'native PID invalid')
|
||||
try:
|
||||
os.kill(pid, 0)
|
||||
except ProcessLookupError:
|
||||
return
|
||||
raise ValueError('native PID still exists or cannot be confirmed exited: ' + str(pid))
|
||||
|
||||
|
||||
def cleanup(rows, expected):
|
||||
require([r['pid'] for r in rows] == expected, 'captured cleanup PID set/order')
|
||||
for row in rows:
|
||||
require(row['engine_ended'] is True and row['outer_cleanup_errors'] == []
|
||||
and row['foreign_process_or_group_signals'] == 0
|
||||
and row['cleanup']['engine_ended'] is True and row['cleanup']['errors'] == [],
|
||||
'owned native cleanup incomplete')
|
||||
exited(row['pid'])
|
||||
|
||||
|
||||
def verify(args):
|
||||
build = Path(os.environ['BUILD_OUTPUT_DIR']).resolve(strict=True)
|
||||
out = local(build, args.full_output)
|
||||
authority = local(build, 'audit-support/root-input-r01')
|
||||
base_path = local(authority, 'MAIN9B_COMPLETE_SOURCE_REFS.json')
|
||||
marker_base = local(authority, 'MAIN9B_COMPLETION_MARKER_CONTRACTS.json')
|
||||
require(pin(base_path) == BASE_PIN and pin(marker_base) == MARKER_PIN, 'fixed Root authority bytes')
|
||||
baseline = pins(read(base_path))
|
||||
require(len(baseline) == 1338, 'canonical map size')
|
||||
require(hashlib.sha256(json.dumps({n: p['sha256'] for n, p in baseline.items()}, sort_keys=True).encode()).hexdigest()
|
||||
== '533a4177918a9da4d4982aa9862cb7420c25d30c2458e36aab9c071c4d3a7139', 'canonical selected map hash')
|
||||
delta = read(Path(__file__).with_name('delta-manifest.json'))
|
||||
require(pin(ROOT / KITCHEN) == delta['canonical_source_delta']['new'], 'submitted kitchen source stale')
|
||||
require(baseline[KITCHEN] == delta['canonical_source_delta']['old'], 'old kitchen pin')
|
||||
expected = dict(baseline)
|
||||
expected[KITCHEN] = pin(ROOT / KITCHEN)
|
||||
expected[EXTRA] = EXTRA_PIN
|
||||
require(len(expected) == 1339, 'exact fixed additional P04 QA source')
|
||||
shared = load('kitchen_shared', ROOT / 'tools/run_all_tests.py')
|
||||
submitted = pins(shared.source_refs(ROOT))
|
||||
require(set(submitted) in (set(baseline), set(expected)), 'submitted additions/drops')
|
||||
require(all(p == expected[n] for n, p in submitted.items()), 'submitted source byte drift')
|
||||
marker_path = Path(args.marker_contracts).resolve(strict=True)
|
||||
require(pin(marker_path) == pin(ROOT / MARKER) == delta['marker_delta']['new'], 'explicit marker input stale')
|
||||
old_contract = read(marker_base)
|
||||
new_contract = read(marker_path)
|
||||
require(set(old_contract) == set(new_contract), 'marker structure changed')
|
||||
changes = []
|
||||
for old, new in zip(old_contract['rows'], new_contract['rows']):
|
||||
if old != new:
|
||||
require(old['test'] == new['test'] == KITCHEN, 'other marker row changed')
|
||||
require({k for k in set(old) | set(new) if old.get(k) != new.get(k)}
|
||||
== {'source_sha256_at_schema_audit', 'source_marker_lines'}, 'marker authority/count changed')
|
||||
changes.append(new)
|
||||
require(len(changes) == 1 and len(old_contract['rows']) == len(new_contract['rows'])
|
||||
and all(old_contract[k] == new_contract[k] for k in old_contract if k != 'rows'), 'marker contract delta')
|
||||
text = (ROOT / KITCHEN).read_text().splitlines()
|
||||
require(changes[0]['source_sha256_at_schema_audit'] == expected[KITCHEN]['sha256'], 'marker source SHA')
|
||||
require(changes[0]['source_marker_lines'] == [{'line': i, 'text': line} for i, line in enumerate(text, 1)
|
||||
if 'print("Required kitchen:' in line], 'actual source marker line/text')
|
||||
markers = load('kitchen_markers', ROOT / 'tools/strict_test_markers.py')
|
||||
contracts = markers.load_contracts(marker_path)
|
||||
start = read(local(out, 'runner-input.json'))
|
||||
end = read(local(out, 'runner-ended.json'))
|
||||
require(start['protocol'] == end['protocol'] == 'guarded-standard-v1'
|
||||
and start['run_id'] == end['run_id'] and start['owner_runner_pid'] == end['owner_runner_pid'], 'native standard run identity')
|
||||
require(Path(start['stage']) == out / 'stage', 'native build output identity')
|
||||
require(end['passed'] is True and end['engine_ended'] is True and end['failed_suites'] == 0, 'native full run did not pass')
|
||||
require(start['selected_source_map'] == {n: p['sha256'] for n, p in expected.items()}, 'exact 1339 selected map')
|
||||
require(pins(read(local(out, 'source-start.json'))) == {n: p for n, p in expected.items() if n != EXTRA}
|
||||
or pins(read(local(out, 'source-start.json'))) == expected, 'native submitted map stale')
|
||||
require(start['marker_contract_sha256'] == pin(marker_path)['sha256']
|
||||
and start['runner_sha256'] == expected['tools/run_all_tests.py']['sha256']
|
||||
and start['marker_module_sha256'] == expected['tools/strict_test_markers.py']['sha256'], 'native authority pins')
|
||||
roster = ['tests/run_tests.gd'] + sorted(n for n in expected if n.startswith('tests/') and n.endswith('.gd')
|
||||
and Path(n).name.startswith('test') and 'export' not in Path(n).parts and n != 'tests/s0/test_kernel_and_save.gd')
|
||||
require(len(roster) == 178 and start['roster'] == roster, 'new standard full 178 required')
|
||||
rows = read(local(out, 'all-tests.json'))
|
||||
require(markers.review_roster(roster, rows)['passed'], 'full exact ordered native roster')
|
||||
require(pin(local(out, 'all-tests.json'))['sha256'] == end['all_tests_sha256'], 'full rows receipt hash')
|
||||
phases = end['phases']
|
||||
require(len(phases) == 180 and [p['phase'] for p in phases] == ['import', 'parse'] + [f'{i:03}' for i in range(178)], 'import/parse/full finite phases')
|
||||
paths = ['res://' + n for n in sorted(expected) if n.endswith('.gd')]
|
||||
require(read(local(out, 'parse-paths.json')) == paths, 'all submitted scripts parsed')
|
||||
diagnostics = load('kitchen_diagnostics', ROOT / 'tools/regression/diagnostic_gate.py')
|
||||
native = load('kitchen_native', ROOT / 'tools/regression/owned_diagnostic_process.py')
|
||||
policy = read(ROOT / 'tools/regression/suite-policy.json')
|
||||
pids = []
|
||||
kitchen_raw = None
|
||||
generated = {}
|
||||
for source, source_pin in expected.items():
|
||||
require(pin(local(out, 'stage/' + source)) == source_pin, 'native selected bytes stale: ' + source)
|
||||
owners = load('kitchen_owners', ROOT / 'tools/regression/ordinary_proof_gate.py')
|
||||
audio = load('kitchen_audio', ROOT / 'tools/regression/audio_evidence_gate.py')
|
||||
bootstraps = read(ROOT / 'tools/regression/bootstrap-scripts.json')
|
||||
for name, entry in bootstraps.items():
|
||||
require(local(out, 'bootstrap/' + name).read_bytes() == entry['text'].encode()
|
||||
and pin(local(out, 'bootstrap/' + name))['sha256'] == entry['sha256'], 'existing Guard/bootstrap bytes')
|
||||
for index, phase in enumerate(phases):
|
||||
name = phase['phase']
|
||||
recorded = read(local(out, name + '-receipt.json'))
|
||||
# all-tests adds only sequence and godot after the immutable phase receipt.
|
||||
require(all(phase[k] == v for k, v in recorded.items()), 'phase receipt binding')
|
||||
test = roster[index - 2] if index >= 2 else None
|
||||
if test:
|
||||
require(phase == rows[index - 2] and phase['test'] == test, 'full row/phase identity')
|
||||
require(phase['validation_error'] is None and phase['passed'] is True and phase['failed'] is False, 'native phase failed')
|
||||
command = [start['engine'], '--headless', '--path', str(out / 'stage')]
|
||||
if name == 'import':
|
||||
command += ['--editor', '--import', '--quit', '--verbose']
|
||||
else:
|
||||
script = out / ('bootstrap/parse_full_dependencies.gd' if name == 'parse' else 'starters/' + name + '.gd')
|
||||
command += ['--script', str(script), '--verbose']
|
||||
require(pin(script)['sha256'] == phase['starter_sha256'], 'actual guarded starter pin')
|
||||
if test:
|
||||
expected_starter = ('extends ' + json.dumps('res://' + test)
|
||||
+ '\nconst Guard := preload(' + json.dumps(str(out / 'bootstrap/profile_guard.gd'))
|
||||
+ ')\nfunc _initialize() -> void:\n\tif not Guard.activate():\n\t\tquit(97)\n\t\treturn\n\tsuper._initialize()\n')
|
||||
require(script.read_text() == expected_starter, 'unchanged actual guarded suite entry')
|
||||
command += ['--log-file', str(out / name / 'engine-native.log')]
|
||||
require(phase['command'] == command, 'actual guarded launcher command')
|
||||
stdout = local(out, name + '/stdout.log').read_text()
|
||||
stderr = local(out, name + '/stderr.log').read_text()
|
||||
raw = stdout + stderr
|
||||
require(local(out, phase['log']).read_text() == raw and pin(local(out, phase['log']))['sha256'] == phase['log_sha256'], 'literal raw native transports')
|
||||
logger = local(out, name + '/engine-native.log')
|
||||
require(pin(logger) == {k: phase['raw_native_log'][k] for k in ('bytes', 'sha256')}, 'native logger hash')
|
||||
review = diagnostics.review_streams(native, {'stdout': stdout, 'stderr': stderr, 'native_file': logger.read_text()}, test,
|
||||
local(out, 'stage/' + test) if test else None)
|
||||
process = read(local(out, name + '/process.json'))
|
||||
require(process == phase['native_process'] and shared.process_gate(process, review['allowed_e31_error_lines'])
|
||||
and diagnostics.diagnostic_result_gate(process, review), 'native process/strict diagnostics')
|
||||
began = read(local(out, name + '/started.json'))
|
||||
ended = read(local(out, name + '/ended.json'))
|
||||
pid = phase['actual_pid']; pids.append(pid)
|
||||
require(began['actual_pid'] == ended['actual_pid'] == process['pid'] == pid
|
||||
and began['command'] == ended['command'] == phase['command']
|
||||
and began['owner_pid'] == ended['owner_pid'] == start['owner_runner_pid']
|
||||
and ended['engine_ended'] is True and ended['exit_code'] == 0, 'native START/END PID/command binding')
|
||||
require(began['live_identity_exact'] is True or began['tracked_child_ended_before_initial_probe'] is True, 'owned native identity')
|
||||
require(began == process['actual_start_binding'] and ended == process['actual_end_binding']
|
||||
and began['event'] == 'ENGINE_STARTED' and ended['event'] == 'ENGINE_ENDED'
|
||||
and ended['monotonic_ns'] >= began['monotonic_ns']
|
||||
and ended['actual_process_exit_code'] == 0 and ended['timed_out'] is False
|
||||
and ended['lifecycle_error'] is None, 'raw complete lifecycle binding')
|
||||
require(read(local(out, name + '/adapter-owned-cleanup.json')) == process['adapter_owned_cleanup'], 'raw adapter cleanup binding')
|
||||
capture = phase['outer_actual_capture']
|
||||
outer = read(local(out, name + '/outer-owned-finally.json'))
|
||||
require(all(capture[k] == v for k, v in outer.items()) and capture['all_captured_ended'] is True
|
||||
and capture['outer_receipt_written'] is True and capture['outer_receipt_error'] is None
|
||||
and capture['adapter_exception'] is None and capture['outer_cleanup_errors'] == []
|
||||
and capture['foreign_process_or_group_signals'] == 0 and capture['actual_captured_pids'] == [pid], 'outer native ownership')
|
||||
cleanup(capture['cleanup'], [pid])
|
||||
require(phase.get('guard_error') is None and phase['default_player_entire_profile_unchanged'] is True
|
||||
and phase['private_project_fields'] == 2, 'unchanged guards/default profile')
|
||||
public = (ROOT / 'project.godot').read_bytes()
|
||||
require(hashlib.sha256(shared.private_project(public, phase['profile_startup_name'])).hexdigest()
|
||||
== phase['private_project_sha256'], 'only the two existing private profile settings')
|
||||
require(read(local(out, name + '-startup-profile-before.json'))['existed'] is False, 'fresh startup profile')
|
||||
for kind in ('startup', 'runtime') if index else ('startup',):
|
||||
whole = phase['whole_' + kind + '_profile_after']
|
||||
archive = local(out, 'profiles/' + name + '/' + kind)
|
||||
require(archive.is_dir() and shared.inventory(archive) == whole['files'], 'retained actual whole private profile')
|
||||
if index:
|
||||
report = read(local(out, name + '-profile.json'))
|
||||
require(report == phase['actual_private_profile']['report'] and report['verified'] is True
|
||||
and report['conditions'] == [True] * 5 and report['conditions_checked'] == 5
|
||||
and report['fresh_before_parent_initialize'] is True and report['profile_writes_before_guard'] == 0
|
||||
and report['project_settings_persisted'] is False
|
||||
and report['actual_user_data_dir'] == report['expected_user_data_dir'], 'unchanged five Guard conditions')
|
||||
require(pin(local(out, name + '-profile.json'))['sha256'] == phase['actual_private_profile']['report_sha256']
|
||||
and phase['actual_private_profile']['whole_files'] == phase['whole_runtime_profile_after']['files']
|
||||
and report['globalized_probe'] == report['actual_user_data_dir'] + '/guard-probe'
|
||||
and Path(report['actual_user_data_dir']).name == report['custom_user_dir_name'], 'whole actual profile/Guard binding')
|
||||
if test:
|
||||
require(phase['source_sha256'] == phase['source_sha256_end'] == expected[test]['sha256'], 'suite source stale')
|
||||
parsed = markers.parse_raw(test, raw, contracts, expected[test]['sha256'])
|
||||
require(parsed['passed'] and parsed['checks'] == phase['reported_checks']
|
||||
and parsed['marker'] == phase['completion_marker'], 'strict actual marker/count')
|
||||
if test in policy['suite_policies']:
|
||||
require(phase['policy'] == shared.phase_policy(test, local(out, 'stage/' + test), policy['suite_policies']), 'unchanged suite cleanup/threshold policy')
|
||||
proof_policy = phase['policy']
|
||||
if proof_policy['ordinary_cleanup_prepared']:
|
||||
require(owners.ordinary_gate(local(out, name + '-teardown'), pid, out,
|
||||
proof_policy['ordinary_control_labels']) == phase['ordinary_owner_pairs'], 'actual ordinary cleanup proof')
|
||||
if proof_policy['old_bounded_teardown'] or test == 'tests/ui/test_ui_audio_feedback.gd':
|
||||
require(audio.teardown_gate(local(out, name + '-teardown'), pid, out) == phase['old_bounded_proof'], 'actual bounded cleanup proof')
|
||||
if test == KITCHEN:
|
||||
kitchen_raw = raw
|
||||
require(parsed['checks'] == 163, '145 existing checks plus 17 controls and one fresh proof')
|
||||
elif name == 'parse':
|
||||
require(raw.count('FULL_DEPENDENCY_PARSE checks=%d failures=0\n' % len(paths)) == 1, 'strict all-script parse')
|
||||
generated.update({n: {k: p[k] for k in ('bytes', 'sha256')} for n, p in phase['generated_reports'].items()})
|
||||
require(len(set(pids)) == 180 and end['actual_captured_PIDs'] == pids, 'all native PID bindings')
|
||||
cleanup(end['final_owned_cleanup'], pids)
|
||||
require(end['error'] is None and end['cleanup_errors'] == [] and end['guard_errors'] == [], 'full cleanup/guards')
|
||||
require(start['default_player_before'] == end['default_player_after']
|
||||
and start['default_player_before']['logs_and_locks_included'] is True, 'whole default profile including locks/logs preserved')
|
||||
require(shared.inventory(local(out, 'stage'), stage=True) == dict(expected, **generated), 'whole stage/source/report set preserved')
|
||||
labels = ['outside'] + [verb + ' ' + field for field in ['version', 'relationships', 'threshold_candidates', 'next_candidate_sequence']
|
||||
for verb in ['delete', 'change']] + ['sequence +1', 'nonempty originals', 'version 0', 'version 3', 'version 2',
|
||||
'equal reordered keys', 'equal String reencoding', 'byte-identical deep copy']
|
||||
lines = kitchen_raw.splitlines()
|
||||
for label in labels:
|
||||
require(lines.count(' ✓ legacy migration control immutable ' + label) == 1, 'native negative/positive control missing: ' + label)
|
||||
# Successful return is deliberately silent. This gate never produces its own PASS.
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(allow_abbrev=False, description=__doc__)
|
||||
parser.add_argument('--verify-only', required=True, action='store_true')
|
||||
parser.add_argument('--marker-contracts', default=str(ROOT / MARKER))
|
||||
parser.add_argument('--full-output', default='native-full178', help='relative to BUILD_OUTPUT_DIR')
|
||||
verify(parser.parse_args())
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
try:
|
||||
main()
|
||||
except (OSError, ValueError, KeyError, TypeError, AssertionError) as error:
|
||||
print(str(error), file=sys.stderr)
|
||||
raise SystemExit(1)
|
||||
@@ -188,7 +188,93 @@ func _io_valid(event: Dictionary, kind: String, operation: StringName, path: Str
|
||||
var payload: Dictionary = event.get("payload", {})
|
||||
return event.get("event_type") == kind and event.get("source_service") == "save_service" and event.get("causation_command_id") == 0 and event.get("entity_ids") == [] and not String(event.get("event_id", "")).is_empty() and payload.get(&"operation") == operation and typeof(payload.get(&"ok")) == TYPE_BOOL and payload[&"ok"] and payload.get(&"code") == &"OK" and payload.get(&"save_path") == path and payload.get(&"checkpoint_absolute_minute") == checkpoint_minute and payload.get(&"saved_absolute_minute") == checkpoint_minute and event.get("absolute_minute") == checkpoint_minute and not String(payload.get(&"save_id", "")).is_empty() and payload.get(&"request_id") == 0 and payload.get(&"reason") == (&"manual" if operation == &"save" else &"") and (operation != &"load" or typeof(payload.get(&"duration_us")) == TYPE_INT and payload[&"duration_us"] >= 0 and typeof(payload.get(&"runtime_duration_us")) == TYPE_INT and payload[&"runtime_duration_us"] >= 0)
|
||||
|
||||
func _compare_loaded(writer: Dictionary, write_path: String, read_path: String, label: String) -> void:
|
||||
func _typed_sha(value: Variant) -> String:
|
||||
var context := HashingContext.new()
|
||||
context.start(HashingContext.HASH_SHA256)
|
||||
context.update(var_to_bytes(value))
|
||||
return context.finish().hex_encode()
|
||||
|
||||
func _legacy_relationship_v1_to_v2(expected_subtree: Variant, actual_subtree: Variant) -> Dictionary:
|
||||
var rejected := {"ok": false, "expected": null, "proof": {}}
|
||||
if not expected_subtree is Dictionary or not actual_subtree is Dictionary: return rejected
|
||||
var old: Dictionary = expected_subtree
|
||||
var actual: Dictionary = actual_subtree
|
||||
# The genuine fixture uses StringName keys, in this exact serialization order.
|
||||
if var_to_bytes(old.keys()) != var_to_bytes([&"version", &"relationships", &"threshold_candidates", &"next_candidate_sequence"]): return rejected
|
||||
if typeof(old[&"version"]) != TYPE_INT or old[&"version"] != 1: return rejected
|
||||
if not old[&"relationships"] is Array or not old[&"threshold_candidates"] is Array or typeof(old[&"next_candidate_sequence"]) != TYPE_INT: return rejected
|
||||
if var_to_bytes(actual.keys()) != var_to_bytes([&"version", &"legacy", &"relationships", &"original_relationships"]): return rejected
|
||||
if typeof(actual[&"version"]) != TYPE_INT or actual[&"version"] != 2: return rejected
|
||||
if not actual[&"legacy"] is Dictionary or var_to_bytes(actual[&"legacy"]) != var_to_bytes(old): return rejected
|
||||
if var_to_bytes(actual[&"legacy"].get(&"next_candidate_sequence")) != var_to_bytes(old[&"next_candidate_sequence"]): return rejected
|
||||
if not actual[&"original_relationships"] is Array or not actual[&"original_relationships"].is_empty(): return rejected
|
||||
# create_snapshot uses a typed compatibility collection even for empty v1 input.
|
||||
var projection: Array[Dictionary] = []
|
||||
for entry: Variant in old[&"relationships"]:
|
||||
if not entry is Dictionary: return rejected
|
||||
projection.append(entry.duplicate(true))
|
||||
if var_to_bytes(actual[&"relationships"]) != var_to_bytes(projection): return rejected
|
||||
var originals: Array[Dictionary] = []
|
||||
var migrated := {&"version": 2, &"legacy": old.duplicate(true), &"relationships": projection.duplicate(true), &"original_relationships": originals}
|
||||
if var_to_bytes(migrated) != var_to_bytes(actual): return rejected
|
||||
return {"ok": true, "expected": migrated, "proof": {"old_v1_bytes": var_to_bytes(old).size(), "old_v1_sha256": _typed_sha(old), "replaced_subtree_sha256": _typed_sha(migrated)}}
|
||||
|
||||
func _legacy_checkpoint(expected: Dictionary, reader: Dictionary) -> Dictionary:
|
||||
var migration := _legacy_relationship_v1_to_v2(expected[&"systems"][&"runtime"][&"relationships"], reader[&"systems"][&"runtime"][&"relationships"])
|
||||
if not migration["ok"]: return migration
|
||||
# Reverse just this subtree in a copy of the reader: every other byte must
|
||||
# already match the writer after the separately validated IO1 replacement.
|
||||
var outside := reader.duplicate(true)
|
||||
outside[&"systems"][&"runtime"][&"relationships"] = expected[&"systems"][&"runtime"][&"relationships"].duplicate(true)
|
||||
if var_to_bytes(outside) != var_to_bytes(expected): return {"ok": false}
|
||||
var result := expected.duplicate(true)
|
||||
result[&"systems"][&"runtime"][&"relationships"] = migration["expected"].duplicate(true)
|
||||
if var_to_bytes(result) != var_to_bytes(reader) or result != reader or SimulationDigest.compute(result) != SimulationDigest.compute(reader): return {"ok": false}
|
||||
return {"ok": true, "expected": result, "proof": migration["proof"]}
|
||||
|
||||
func _legacy_control(writer: Dictionary, reader: Dictionary, label: String, kind: String, field: StringName = &"", value: Variant = null) -> void:
|
||||
var writer_bytes := var_to_bytes(writer); var reader_bytes := var_to_bytes(reader)
|
||||
var candidate := writer.duplicate(true); var observed := reader.duplicate(true)
|
||||
var old: Dictionary = candidate[&"systems"][&"runtime"][&"relationships"]
|
||||
var actual: Dictionary = observed[&"systems"][&"runtime"][&"relationships"]
|
||||
match kind:
|
||||
"outside": candidate[&"legacy_control_outside"] = true
|
||||
"delete": old.erase(field)
|
||||
"change": old[field] = value
|
||||
"sequence": actual[&"legacy"][&"next_candidate_sequence"] += 1
|
||||
"originals": actual[&"original_relationships"].append({&"unapproved": true})
|
||||
"version": actual[&"version"] = value
|
||||
"reorder":
|
||||
var reordered := {}
|
||||
var keys := old.keys(); keys.reverse()
|
||||
for key: Variant in keys: reordered[key] = old[key]
|
||||
candidate[&"systems"][&"runtime"][&"relationships"] = reordered
|
||||
"reencode":
|
||||
var reencoded := {}
|
||||
for key: Variant in old: reencoded[String(key)] = old[key]
|
||||
candidate[&"systems"][&"runtime"][&"relationships"] = reencoded
|
||||
var before_candidate := var_to_bytes(candidate); var before_observed := var_to_bytes(observed)
|
||||
var encoding_control := true
|
||||
if kind in ["reorder", "reencode"]:
|
||||
encoding_control = candidate == writer and before_candidate != writer_bytes
|
||||
var result := _legacy_checkpoint(candidate, observed)
|
||||
expect(encoding_control and result["ok"] == (kind == "copy") and var_to_bytes(writer) == writer_bytes and var_to_bytes(reader) == reader_bytes and var_to_bytes(candidate) == before_candidate and var_to_bytes(observed) == before_observed, "legacy migration control immutable " + label)
|
||||
|
||||
func _legacy_controls(writer: Dictionary, reader: Dictionary) -> void:
|
||||
_legacy_control(writer, reader, "outside", "outside")
|
||||
for field: StringName in [&"version", &"relationships", &"threshold_candidates", &"next_candidate_sequence"]:
|
||||
_legacy_control(writer, reader, "delete " + String(field), "delete", field)
|
||||
_legacy_control(writer, reader, "change " + String(field), "change", field, null)
|
||||
_legacy_control(writer, reader, "sequence +1", "sequence")
|
||||
_legacy_control(writer, reader, "nonempty originals", "originals")
|
||||
for version: Variant in [0, 3, "2"]:
|
||||
_legacy_control(writer, reader, "version " + str(version), "version", &"", version)
|
||||
_legacy_control(writer, reader, "equal reordered keys", "reorder")
|
||||
_legacy_control(writer, reader, "equal String reencoding", "reencode")
|
||||
_legacy_control(writer, reader, "byte-identical deep copy", "copy")
|
||||
_proof("legacy14-migration-controls", {"attempts": 17, "writer_sha256": _typed_sha(writer), "reader_sha256": _typed_sha(reader), "failures": failures})
|
||||
|
||||
func _compare_loaded(writer: Dictionary, write_path: String, read_path: String, label: String, migrate_relationships_v1: bool = false) -> void:
|
||||
var reader: Dictionary = runtime.create_snapshot()
|
||||
var old: Dictionary = writer[&"systems"][&"runtime"][&"domain_events"]["recent_events"].back().duplicate(true)
|
||||
var actual: Dictionary = reader[&"systems"][&"runtime"][&"domain_events"]["recent_events"].back().duplicate(true)
|
||||
@@ -199,9 +285,22 @@ func _compare_loaded(writer: Dictionary, write_path: String, read_path: String,
|
||||
if event.get("event_id") == old.get("event_id"):
|
||||
valid = valid and var_to_bytes(event) == var_to_bytes(old)
|
||||
event["event_type"] = actual["event_type"]; event["payload"] = actual["payload"].duplicate(true); replaced += 1
|
||||
var migration_proof := {}
|
||||
if migrate_relationships_v1:
|
||||
_legacy_controls(expected, reader)
|
||||
var migration := _legacy_checkpoint(expected, reader)
|
||||
valid = valid and migration["ok"]
|
||||
if migration["ok"]:
|
||||
expected = migration["expected"]
|
||||
migration_proof = migration["proof"]
|
||||
migration_proof["write_checkpoint_sha256"] = FileAccess.get_sha256(LEGACY.path_join("write-checkpoint.var"))
|
||||
valid = valid and replaced == 1 and expected[&"systems"][&"runtime"][&"domain_events"]["published_ids"].has(actual.get("event_id")) and var_to_bytes(expected) == var_to_bytes(reader) and expected == reader and SimulationDigest.compute(expected) == SimulationDigest.compute(reader)
|
||||
_proof(label + "-expected-io1", expected); _proof(label + "-actual", reader)
|
||||
_proof(label + "-io1-binding", {"whole_typed_bytes_equal": valid, "writer_io": old, "reader_io": actual, "mapped_fields": ["event_type", "payload"], "event_deletion_count": 0, "write_path": write_path, "read_path": read_path, "copied_legacy_package": write_path != read_path})
|
||||
var binding := {"whole_typed_bytes_equal": valid, "writer_io": old, "reader_io": actual, "mapped_fields": ["event_type", "payload"], "event_deletion_count": 0, "write_path": write_path, "read_path": read_path, "copied_legacy_package": write_path != read_path}
|
||||
if migrate_relationships_v1:
|
||||
binding["mapped_fields"].append("relationships_v1_to_v2")
|
||||
binding["relationships_v1_to_v2"] = migration_proof
|
||||
_proof(label + "-io1-binding", binding)
|
||||
expect(valid, "whole typed loaded state unchanged except complete identity/saveID-bound IO1: " + label)
|
||||
|
||||
func _new_saved_boundary() -> void:
|
||||
@@ -340,7 +439,7 @@ func _legacy_saved_boundary() -> void:
|
||||
expect(persisted is Dictionary and inspected.get(&"ok", false) and inspected.get(&"read_only", false) and var_to_bytes(inspected.get(&"snapshot", {})) == var_to_bytes(persisted) and var_to_bytes(live) == var_to_bytes(runtime.create_snapshot()) and _package(path) == hashes, "old14 inspection exact complete typed persisted state and read-only for every live byte/package")
|
||||
expect(runtime.load_from_path(path).get(&"ok", false), "ordinary strict old14 formal Load, schema stays14")
|
||||
_listen(true)
|
||||
_compare_loaded(expected, contract["slot"], path, "legacy14-load")
|
||||
_compare_loaded(expected, contract["slot"], path, "legacy14-load", true)
|
||||
var cafe: FacilityInstance = runtime.facility_service.get_instance(ids["cafe"])
|
||||
var service: ServiceInstance = runtime.facility_service.get_service(ids["service"])
|
||||
expect(cafe.related_facility_id.is_empty() and service.state == ServiceInstance.State.ACTIVE and service.elapsed_minutes == 5 and cafe.state == FacilityInstance.State.OPEN and not cafe.related_facility_operating and cafe.operation_state == FacilityInstance.OperationState.INACTIVE, "old raw OPEN/ACTIVE5 remains serialized immediately; derived missing kitchen false")
|
||||
|
||||
Reference in New Issue
Block a user