Files
ARCADIA-Studio/harness/experiments/contextual-prerelease-20260911/reviewer/review.py
T

446 lines
30 KiB
Python

"""Independent frozen archive and packaged-server checks; never runs Cargo or GUI."""
from pathlib import Path, PurePosixPath
import hashlib
import json
import os
import re
import socket
import stat
import struct
import subprocess
import sys
import tarfile
import time
import tomllib
import traceback
import urllib.request
import uuid
import zipfile
OUT = Path(__file__).resolve().parent
ROOT = OUT.parents[1]
PREP = ROOT / '.local/contextual-prerelease-20260911'
NAME = 'ArcadiaStudio-0.2.0-preview.contextual.20260911-x86_64-pc-windows-gnullvm'
ZIP = PREP / 'packages' / (NAME + '.zip')
EXTRACT = OUT / 'extracted'
PACKAGE = EXTRACT / NAME
SOURCE = PREP / 'source'
FROZEN = json.loads((OUT / 'frozen-inputs.json').read_text(encoding='utf-8'))
SOURCE_COMMIT = FROZEN['source_commit']
BASE_COMMIT = 'b2c3e54001fc19a05423bcd5808ceea78d13ff59'
EXPECTED_ZIP = FROZEN['zip_sha256'].lower()
BINARIES = {
'arcadia-studio.exe': '5657bab5c841939b66f901efe6c0ffb2d7cbbe8cc5f111a0ca3de8073d3846ce',
'arcadia-server.exe': 'efeeb89c0d2e0f93df4472cb3e9f48b2b722a37ae1043c90a5a56f2e845fe665',
'libunwind.dll': 'f45604a12e8bcd839fd86f667c1be1cc7c4f9da2b3c69ff381ac708f0e1ae7d4',
}
RESULT = {'checks': [], 'scope': sys.argv[1], 'started_utc': time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime())}
def check(condition, label):
if not condition:
raise AssertionError(label)
RESULT['checks'].append(label)
def sha(data):
return hashlib.sha256(data).hexdigest()
def read_json(path):
return json.loads(path.read_text(encoding='utf-8-sig'))
def write_json(path, value):
with path.open('x', encoding='utf-8', newline='\n') as f:
json.dump(value, f, indent=2, ensure_ascii=False)
f.write('\n')
def file_map(directory):
result = {}
for p in directory.rglob('*'):
check(not p.is_symlink() and not (getattr(p.lstat(), 'st_file_attributes', 0) & 0x400), 'ordinary path: ' + p.relative_to(directory).as_posix())
if p.is_file():
result[p.relative_to(directory).as_posix()] = sha(p.read_bytes())
return result
def safe_name(name):
p = PurePosixPath(name)
return not p.is_absolute() and p.as_posix() == name and '\\' not in name and ':' not in name and all(c not in ('', '.', '..') and not c.endswith(('.', ' ')) and not re.match(r'(?i)^(con|nul|prn|aux|com[1-9]|lpt[1-9])(\.|$)', c) for c in p.parts)
def git_blobs(ref, paths):
raw = subprocess.run(['git', 'cat-file', '--batch'], input=''.join(ref + ':' + p + '\n' for p in paths).encode(), cwd=ROOT, capture_output=True, check=True).stdout
result, cursor = {}, 0
for p in paths:
end = raw.index(b'\n', cursor)
header = raw[cursor:end].split()
check(len(header) == 3 and header[1] == b'blob', 'Git blob exists: ' + ref[:7] + ':' + p)
size = int(header[2])
result[p] = raw[end + 1:end + 1 + size]
cursor = end + 2 + size
check(cursor == len(raw), 'Git batch framing complete: ' + ref[:7])
return result
def artifact():
check(not EXTRACT.exists(), 'clean extraction destination absent')
check(ZIP.stat().st_size == FROZEN['zip_bytes'] and sha(ZIP.read_bytes()) == EXPECTED_ZIP, 'frozen ZIP size and SHA256')
with zipfile.ZipFile(ZIP) as archive:
infos = archive.infolist()
names = [i.filename for i in infos]
check(0 < len(infos) == FROZEN['zip_members'], 'measured ZIP members equal frozen producer inventory')
check(len({n.casefold() for n in names}) == len(names), 'no duplicate or case-colliding ZIP names')
check(all(safe_name(n) and n.startswith(NAME + '/') for n in names), 'one safe ZIP root without traversal or Windows special paths')
check(all(not i.is_dir() and stat.S_IFMT(i.external_attr >> 16) in (0, stat.S_IFREG) for i in infos), 'ordinary file-only ZIP entries')
check(all(i.date_time == (2000, 1, 1, 0, 0, 0) and i.flag_bits & 1 == 0 for i in infos), 'fixed timestamps and no encrypted entries')
check(archive.testzip() is None, 'all ZIP CRCs')
EXTRACT.mkdir()
archive.extractall(EXTRACT)
archive_hashes = {i.filename[len(NAME) + 1:]: sha(archive.read(i)) for i in infos}
actual = file_map(PACKAGE)
check(actual == archive_hashes, 'complete extracted file set and bytes equal ZIP')
sums = {}
for line in (PACKAGE / 'SHA256SUMS.txt').read_text().splitlines():
match = re.fullmatch(r'([0-9a-f]{64}) (.+)', line)
check(match is not None and safe_name(match[2]) and match[2] not in sums, 'well-formed unique checksum row')
sums[match[2]] = match[1]
check(sums == {p: h for p, h in actual.items() if p != 'SHA256SUMS.txt'}, 'checksum inventory covers every other file exactly')
package = read_json(PACKAGE / 'package.json')
inputs = package['input_sha256']
generated = {'SHA256SUMS.txt', 'START-HERE.txt', 'THIRD-PARTY-NOTICES.txt', 'dependency-notices.json', 'documentation-check.json', 'package.json'}
check(set(actual) - set(inputs) == generated and all(actual[p] == h.lower() for p, h in inputs.items()), 'complete package input hashes plus exactly six generated files')
check(package['version'] == '0.2.0-preview.contextual.20260911' and package['target'] == 'x86_64-pc-windows-gnullvm', 'version and target')
check(all(actual[p] == h for p, h in BINARIES.items()), 'exact GUI, server and runtime hashes')
for p in BINARIES:
data = (PACKAGE / p).read_bytes()
offset = struct.unpack_from('<I', data, 0x3c)[0]
check(data[:2] == b'MZ' and data[offset:offset + 4] == b'PE\0\0' and struct.unpack_from('<H', data, offset + 4)[0] == 0x8664, 'ordinary PE x86_64: ' + p)
check({p for p in actual if '/' not in p} == {'arcadia-server.exe', 'arcadia-studio.exe', 'libunwind.dll', 'LICENSE', 'README.md'} | generated, 'only declared portable top-level files')
check({p.split('/')[0] for p in actual if '/' in p} == {'deployment', 'docs', 'examples', 'licenses'}, 'no harness, private profile, source checkout or credential-directory payload')
for p in actual:
if p.startswith(('docs/', 'examples/', 'deployment/')) or p == 'LICENSE':
check((SOURCE / p).is_file() and sha((SOURCE / p).read_bytes()) == actual[p], 'public source document/example bytes: ' + p)
RESULT['archive'] = {'path': str(ZIP), 'sha256': EXPECTED_ZIP, 'bytes': ZIP.stat().st_size, 'file_count': len(actual), 'files': actual}
RESULT['package_claims'] = {k: package[k] for k in ('version', 'target', 'acceptance', 'build', 'build_evidence')}
check(actual['README.md'] == sha((SOURCE / 'scripts/package-readme.md').read_bytes()), 'packaged README equals its explicit public source input')
provenance()
notices(actual)
check(file_map(PACKAGE) == actual, 'artifact verification leaves extracted package unchanged')
check(sha(ZIP.read_bytes()) == EXPECTED_ZIP, 'artifact verification leaves frozen ZIP unchanged')
def local_source_map(directory):
result = {}
for p in directory.rglob('*'):
rel = p.relative_to(directory)
check(not p.is_symlink() and not (getattr(p.lstat(), 'st_file_attributes', 0) & 0x400), 'ordinary source input: ' + str(p))
if p.is_file() and rel.parts[0] != 'target' and p.name != '.cargo-ok':
result[rel.as_posix()] = sha(p.read_bytes())
return result
def provenance():
gui_path = ROOT / '.local/contextual-source-layout-20260911/gui-verified/manifest.json'
input_path = gui_path.parent / '../inputs4.json'
check(sha(gui_path.read_bytes()) == '4beb64b955aecf5ae3bfb606316ec1fc9d17d288ed85023515148bb1890f034f', 'original GUI5657 manifest identity')
check(sha(input_path.read_bytes()) == '669724858967d83ab7da5f78bfafb6065e2bcab3bad0e68d0a74feb86c7195e7', 'original GUI5657 raw input manifest identity')
gui = read_json(gui_path)
inputs = read_json(input_path)
expected = {i['path'].replace('\\', '/'): i['sha256'].lower() for i in inputs['inputs']}
check(len(expected) == len(inputs['inputs']) == 238, 'measured 238 unique original GUI inputs')
check({i['file']: i['sha256'].lower() for i in gui['artifacts']} == {k: BINARIES[k] for k in ('arcadia-studio.exe', 'libunwind.dll')}, 'GUI record binds shipped GUI and runtime')
derived = git_blobs(SOURCE_COMMIT, list(expected))
base = git_blobs(BASE_COMMIT, list(expected))
differences = []
mixed = 'crates/oa-desktop/src/behavior/creation.rs'
wanted = {'docs/openapi.json'} | {
'crates/oa-desktop/src/' + p for p in (
'behavior/creation.rs', 'behavior/scenario/workspace.rs',
'behavior/topology/contextual.rs', 'guide.rs',
'semantic_review/artifacts_view.rs', 'semantic_review/attachments_view.rs',
'semantic_review/keyed_rows.rs', 'semantic_review/keyed_tests.rs',
'semantic_review/keyed.rs', 'semantic_review/ordered.rs')
}
for p, h in expected.items():
current = (SOURCE / p).read_bytes()
check(sha(derived[p]) == h == sha(current), 'original GUI input equals reconstructed Git blob and checkout: ' + p)
if sha(base[p]) != h:
check(base[p].replace(b'\r\n', b'\n') == current.replace(b'\r\n', b'\n'), 'restoration changes only newline bytes: ' + p)
uniform = base[p].replace(b'\r\n', b'\n').replace(b'\n', b'\r\n')
if p == mixed:
crlf = current.count(b'\r\n')
bare_lf = current.count(b'\n') - crlf
check(crlf > 0 and bare_lf > 0 and sha(uniform) != h, 'creation.rs retains exact mixed newlines; uniform conversion is insufficient')
else:
check(sha(uniform) == h, 'exact uniform CRLF restoration: ' + p)
differences.append(p)
check(set(differences) == wanted and len(differences) == 11, 'exact eleven documented newline restorations')
delta = subprocess.run(['git', 'diff', '--name-only', BASE_COMMIT, SOURCE_COMMIT], cwd=ROOT, check=True, capture_output=True).stdout.decode().splitlines()
check(set(delta) == wanted | {'.gitattributes'}, 'reconstruction changes only eleven exact inputs and preservation attributes')
semantic_delta = subprocess.run(['git', 'diff', '--name-only', '--ignore-space-at-eol', BASE_COMMIT, SOURCE_COMMIT], cwd=ROOT, check=True, capture_output=True).stdout.decode().splitlines()
check(semantic_delta == ['.gitattributes'], 'no non-newline source change in reconstruction')
origin_path = ROOT / 'harness/experiments/textual-authoring-20260911-local/provenance.json'
origin = read_json(origin_path)
check(origin['exe_sha256'].lower() == BINARIES['arcadia-server.exe'], 'original independent server record binds unchanged EFEE')
tar_path = ROOT / '.local/linux-qa/runtime/source7-20260907T082753190288Z.tar.gz'
check(sha(tar_path.read_bytes()) == 'cbb371a93a562e1e641d7629fe351e016d63e66fc9112499b3fed6d5cd171f78', 'original server source tar identity')
server_expected = {i['path']: i['snapshot_sha256'].lower() for i in origin['source_comparisons']}
check(len(server_expected) == len(origin['source_comparisons']) == 120, 'measured 120 unique originally recorded server source files')
server_base = git_blobs(BASE_COMMIT, list(server_expected))
tar_manifest = read_json(tar_path.with_suffix('').with_suffix('.tar.manifest.json'))
with tarfile.open(tar_path) as tar:
members = {m.name.removeprefix('./'): m for m in tar.getmembers() if m.isfile()}
for p, h in server_expected.items():
check(sha(tar.extractfile(members[p]).read()) == h == sha(server_base[p]) == sha((SOURCE / p).read_bytes()), 'original server tar equals base Git and reconstructed input: ' + p)
for crate in ('oa-core', 'oa-server'):
prefix = 'crates/' + crate + '/'
expected_crate = {p[len(prefix):]: h for p, h in server_expected.items() if p.startswith(prefix)}
check(local_source_map(SOURCE / 'crates' / crate) == expected_crate, 'complete unchanged server input closure: ' + crate)
shared = [p for p in members if p in ('Cargo.toml', 'Cargo.lock', 'docs/openapi.json') or p.startswith('build/')]
old_shared = {p: tar.extractfile(members[p]).read() for p in shared}
for p, raw in old_shared.items():
check(sha(raw) == tar_manifest['files'][p].lower(), 'original server shared input matches original tar manifest: ' + p)
if p not in ('Cargo.toml', 'Cargo.lock'):
check(raw == (SOURCE / p).read_bytes(), 'unchanged actual shared server build bytes: ' + p)
old_manifest = tomllib.loads(old_shared['Cargo.toml'].decode())
new_manifest = tomllib.loads((SOURCE / 'Cargo.toml').read_text())
old_members = old_manifest['workspace']['members']
check(new_manifest['workspace']['members'] == old_members + ['crates/oa-cli'], 'only explicit CLI workspace member added since original server build')
new_manifest['workspace']['members'] = old_members
check(new_manifest == old_manifest, 'workspace dependency, resolver and profile semantics remain unchanged')
old_lock = tomllib.loads(old_shared['Cargo.lock'].decode())
new_lock = tomllib.loads((SOURCE / 'Cargo.lock').read_text())
old_packages = {(p['name'], p['version'], p.get('source')): p for p in old_lock.pop('package')}
new_packages = {(p['name'], p['version'], p.get('source')): p for p in new_lock.pop('package')}
cli = new_packages.pop(('oa-cli', '0.2.0', None))
check(cli == {'name': 'oa-cli', 'version': '0.2.0', 'dependencies': ['oa-core', 'serde', 'serde_json']}, 'one explicit local CLI lock entry, no new feature request')
check(old_lock == new_lock and old_packages == new_packages, 'all original lock identities/dependencies/checksums unchanged')
cli_manifest = tomllib.loads((SOURCE / 'crates/oa-cli/Cargo.toml').read_text())
check(cli_manifest['dependencies'] == {'oa-core': {'path': '../oa-core'}, 'serde': {'workspace': True}, 'serde_json': {'workspace': True}}, 'CLI uses existing core/serde requests')
check(not set(cli_manifest).intersection({'features', 'build-dependencies', 'target'}), 'CLI adds no feature/build/target request')
evidence = read_json(PACKAGE / 'package.json')['build_evidence']['evidence']
check(evidence == read_json(PREP / 'build-evidence.json'), 'packaged build evidence equals the producer frozen evidence bytes semantically')
check(evidence['binary_sha256'] == {k: v for k, v in BINARIES.items() if k.endswith('.exe')}, 'build evidence binds both exact shipped executables')
check(SOURCE_COMMIT in evidence['source_identity'] and BASE_COMMIT in evidence['source_identity'], 'evidence distinguishes reconstruction from release base')
check('not' in evidence['source_identity'].lower() and 'original build commit' in evidence['source_identity'].lower(), 'reconstruction is not claimed as original build commit')
check('server' in evidence['profile'].lower() and 'september 7' in evidence['profile'].lower(), 'server retains its separate historical build provenance')
check('neither a new server build nor release-profile/reproducible-build acceptance is claimed' in evidence['profile'].lower(), 'development/reproducibility limitation retained')
preserved_originals = {
'evidence/gui-build-status.json': '.local/contextual-source-layout-20260911/gui1-status.json',
'evidence/gui-stage-manifest.json': '.local/contextual-source-layout-20260911/gui-verified/manifest.json',
'evidence/gui-inputs4.json': '.local/contextual-source-layout-20260911/inputs4.json',
'evidence/gui-full679-result.json': 'harness/experiments/contextual-source-layout-full-20260911/full-result.json',
'evidence/server-original-gates.json': '.local/source7-post-routing-gates-status.json',
'evidence/server-original-binary-hashes.json': '.local/native-qa/source7-post-routing-binary-hashes.json',
'evidence/server-prior-input-comparisons.json': '.local/authoring-prerelease-20260911/inventory.json',
}
referred = set()
for binary, references in evidence['per_binary_provenance'].items():
check(binary in BINARIES and binary.endswith('.exe'), 'per-binary history names a shipped executable')
for value in references.values():
if value.startswith('evidence/'):
check(value in preserved_originals, 'known independently identified historical evidence path: ' + value)
check((PREP / value).read_bytes() == (ROOT / preserved_originals[value]).read_bytes(), 'preserved per-binary evidence equals original bytes: ' + value)
referred.add(value)
check(referred == set(preserved_originals), 'both original binary histories remain complete and separate')
check('not files inside the distributable ZIP' in evidence['provenance_reference_base'], 'external provenance paths are not advertised as bundled files')
RESULT['provenance'] = {
'gui_manifest_sha256': sha(gui_path.read_bytes()), 'gui_inputs_sha256': sha(input_path.read_bytes()),
'gui_inputs': len(expected), 'historical_gui_input_record': {k: v for k, v in inputs.items() if k != 'inputs'},
'source_commit': SOURCE_COMMIT, 'base_commit': BASE_COMMIT, 'eol_restorations': differences,
'mixed_newline_input': mixed, 'server_source_files': len(server_expected),
'server_origin_sha256': sha(origin_path.read_bytes()), 'shared_input_files': shared,
'original_lock_packages': len(old_packages), 'additional_local_lock_package': cli,
'original_server_lock_sha256': sha(old_shared['Cargo.lock']),
'current_gui_lock_sha256': sha((SOURCE / 'Cargo.lock').read_bytes()),
'tar_sha256': sha(tar_path.read_bytes())
}
def notices(actual):
notice = read_json(PACKAGE / 'dependency-notices.json')
check(notice['root_packages'] == ['oa-desktop', 'oa-server'], 'exact desktop/server notice roots')
previous_path = ROOT / '.local/authoring-prerelease-review-20260911/extracted/ArcadiaStudio-0.2.0-preview.authoring.20260911-x86_64-pc-windows-gnullvm/dependency-notices.json'
check(sha(previous_path.read_bytes()) == '98fab15c1a2bb3aa727c6d52d5c69435b15efa56b6668ed717e7a04cf86f9b52', 'previous accepted notice inventory identity')
previous = read_json(previous_path)
key = lambda p: (p['name'], p['version'], p['source'])
old = {key(p): p for p in previous['packages']}
current = {key(p): p for p in notice['packages']}
check(len(current) == len(notice['packages']) and set(current) == set(old), 'measured notice package identities remain the accepted desktop/server graph')
for identity, p in current.items():
if p['source'] is not None:
check(p == old[identity], 'unchanged registry identity/features/source inventory/notices: ' + p['name'])
check(notice['rust'] == previous['rust'] and notice['runtime'] == previous['runtime'], 'unchanged exact Rust/runtime inventory and notice provenance')
check(notice['cargo_lock_sha256'].lower() == sha((SOURCE / 'Cargo.lock').read_bytes()), 'notice lock equals reconstructed current GUI lock, distinct from historical server lock')
for p, h in notice['workspace_build_inputs_sha256'].items():
check(sha((SOURCE / p).read_bytes()) == h, 'notice common build input: ' + p)
lock = tomllib.loads((SOURCE / 'Cargo.lock').read_text())
locked = {(p['name'], p['version'], p.get('source')): p for p in lock['package']}
referenced = set()
def refs(items):
for i in items:
p = i['path']
check(safe_name(p) and p.startswith('licenses/') and actual.get(p) == i['sha256'].lower(), 'bundled exact notice reference: ' + p)
referenced.add(p)
fonts = []
for p in notice['packages']:
key = (p['name'], p['version'], p['source'])
check(key in locked and p['registry_checksum'] == locked[key].get('checksum'), 'notice identity/checksum occurs in historical lock: ' + p['name'] + '@' + p['version'])
check(len(p['notices']) > 0, 'package has notice text: ' + p['name'])
refs(p['notices'])
for f in p['embedded_fonts']:
check(len(f['notices']) > 0, 'embedded font notice present: ' + p['name'] + '/' + f['path'])
refs(f['notices'])
fonts.append({'package': p['name'], 'path': f['path'], 'sha256': f['sha256'], 'license': f['license']})
if p['source'] is None:
directory = SOURCE / 'crates' / p['name']
files = local_source_map(directory)
check(files == p['local_source_sha256'], 'complete ordinary local-crate inventory: ' + p['name'])
refs(notice['rust']['notices'])
refs(notice['runtime']['notices'])
check(notice['runtime']['sha256'] == BINARIES['libunwind.dll'], 'runtime notice is bound to shipped DLL')
library_dir = ROOT / '.local/toolchain/rustup/toolchains/stable-x86_64-pc-windows-gnullvm/lib/rustlib/x86_64-pc-windows-gnullvm/lib'
check(set(notice['rust']['target_libraries_sha256']) == set(previous['rust']['target_libraries_sha256']), 'measured compiler target-library file set unchanged')
for name, h in notice['rust']['target_libraries_sha256'].items():
check(sha((library_dir / name).read_bytes()) == h, 'retained compiler target-library bytes: ' + name)
RESULT['notices'] = {'packages': len(notice['packages']), 'roots': notice['root_packages'], 'lock_sha256': notice['cargo_lock_sha256'], 'notice_reference_files': len(referenced), 'additional_license_files': sorted({p for p in actual if p.startswith('licenses/')} - referenced), 'embedded_fonts': fonts, 'rust_version': notice['rust']['version'], 'runtime_provenance': notice['runtime']['provenance'], 'scope': notice['scope']}
def server():
prior = read_json(OUT / 'artifact-result.json')
check(prior['status'] == 'PASS' and file_map(PACKAGE) == prior['archive']['files'], 'previously verified extracted package is unchanged')
runtime = OUT / 'server-run'
runtime.mkdir()
data_dir = runtime / 'data'
temp = runtime / 'temp'
temp.mkdir()
windows = Path(os.environ['SystemRoot'])
env = {'SystemRoot': str(windows), 'WINDIR': str(windows), 'PATH': str(windows / 'System32') + ';' + str(windows), 'TEMP': str(temp), 'TMP': str(temp)}
exe = PACKAGE / 'arcadia-server.exe'
RESULT['environment'] = env
RESULT['runtime_package_file_count'] = len(prior['archive']['files'])
RESULT['processes'] = []
for arg in ('--version', '--help'):
output = subprocess.run([str(exe), arg], cwd=PACKAGE, env=env, capture_output=True, timeout=15, creationflags=subprocess.CREATE_NO_WINDOW)
(runtime / (arg[2:] + '.stdout')).write_bytes(output.stdout)
(runtime / (arg[2:] + '.stderr')).write_bytes(output.stderr)
check(output.returncode == 0 and not output.stderr, arg + ' succeeds under clean runtime PATH')
if arg == '--version':
check(output.stdout.decode().strip() == 'arcadia-server 0.2.0', 'server reports underlying application version 0.2.0')
check(not data_dir.exists(), 'help/version do not create explicit test data directory')
opener = urllib.request.build_opener(urllib.request.ProxyHandler({}))
request_number = 0
def request(port, method, path, payload=None):
nonlocal request_number
request_number += 1
body = None if payload is None else json.dumps(payload, ensure_ascii=False).encode()
req = urllib.request.Request(f'http://127.0.0.1:{port}' + path, data=body, method=method, headers={} if body is None else {'Content-Type': 'application/json'})
with opener.open(req, timeout=10) as response:
raw = response.read()
meta = {'method': method, 'path': path, 'status': response.status, 'headers': dict(response.headers), 'body_sha256': sha(raw)}
prefix = runtime / f'http-{request_number:02d}'
prefix.with_suffix('.body').write_bytes(raw)
write_json(prefix.with_suffix('.json'), meta)
return meta, raw
def start(index):
with socket.socket() as s:
s.bind(('127.0.0.1', 0))
port = s.getsockname()[1]
out = (runtime / f'server-{index}.stdout').open('xb')
err = (runtime / f'server-{index}.stderr').open('xb')
args = [str(exe), '--bind', f'127.0.0.1:{port}', '--data-dir', str(data_dir)]
process = subprocess.Popen(args, cwd=PACKAGE, env=env, stdout=out, stderr=err, creationflags=subprocess.CREATE_NO_WINDOW)
out.close()
err.close()
RESULT['processes'].append({'pid': process.pid, 'port': port, 'arguments': args})
deadline = time.monotonic() + 15
while True:
if process.poll() is not None:
raise AssertionError('packaged server exited before listening')
try:
with socket.create_connection(('127.0.0.1', port), timeout=.2):
break
except OSError:
if time.monotonic() > deadline:
process.terminate()
process.wait(timeout=5)
raise AssertionError('packaged server did not listen within 15s')
time.sleep(.05)
return process, port
def stop(process):
process.terminate()
code = process.wait(timeout=10)
RESULT['processes'][-1]['exit_after_explicit_terminate'] = code
process = None
try:
process, port = start(1)
for path in ('/health', '/health/ready'):
meta, raw = request(port, 'GET', path)
check(meta['status'] == 200 and json.loads(raw) == {'status': 'ok'}, path + ' contract')
meta, raw = request(port, 'GET', '/api/v1/openapi.json')
check(meta['status'] == 200 and raw == (PACKAGE / 'docs/openapi.json').read_bytes(), 'served OpenAPI equals complete packaged source bytes')
name = 'Independent contextual package persistence — 救助 20260911'
meta, raw = request(port, 'POST', '/api/v1/projects', {'name': name})
created = json.loads(raw)
check(meta['status'] == 201, 'one create returns 201')
resource = str(uuid.UUID(created['resource_id']))
model_id = str(uuid.UUID(created['model_id']))
expected_summary = {'resource_id': resource, 'revision': 1, 'model_id': model_id, 'schema_version': 7, 'name': name, 'elements': 0, 'relations': 0, 'migration': None}
check(created == expected_summary and resource != model_id, 'complete create summary with distinct fresh resource/model IDs')
etag = f'"p-{resource}-r1"'
headers = {k.lower(): v for k, v in meta['headers'].items()}
path = '/api/v1/projects/' + resource
check(headers.get('etag') == etag and headers.get('location') == path, 'create ETag and resource Location')
meta, raw = request(port, 'GET', path)
stored = json.loads(raw)
model = {'schema_version': 7, 'id': model_id, 'alias': 'mission', 'name': name, 'description': '', 'elements': {}, 'relations': {}, 'diagrams': {}, 'artifact_links': {}, 'traces': {}}
source = f'arcadia 7;\nproject mission {json.dumps(name, ensure_ascii=False)} id "{model_id}" {{\n description "";\n}}\n'
expected = {'resource_id': resource, 'revision': 1, 'model': model, 'source_project': {'entry': 'project.arcadia', 'files': {'project.arcadia': source}}}
check(meta['status'] == 200 and stored == expected, 'GET equals independently predicted complete Project and SourceBundle')
check({k.lower(): v for k, v in meta['headers'].items()}.get('etag') == etag, 'GET ETag equals created revision')
disk = data_dir / (resource + '.json')
disk_bytes = disk.read_bytes()
check(json.loads(disk_bytes) == expected, 'durable disk envelope equals complete expected GET')
check({p.name for p in data_dir.iterdir()} == {'.arcadia.lock', disk.name}, 'private store contains only one project and lock')
stop(process)
process = None
process, port = start(2)
meta, raw = request(port, 'GET', '/health/ready')
check(meta['status'] == 200 and json.loads(raw) == {'status': 'ok'}, 'restarted packaged server ready')
meta, raw = request(port, 'GET', path)
check(meta['status'] == 200 and json.loads(raw) == expected, 'restart retains exact complete Project and SourceBundle')
check({k.lower(): v for k, v in meta['headers'].items()}.get('etag') == etag and disk.read_bytes() == disk_bytes, 'restart retains revision, ETag and every durable envelope byte')
RESULT['persistence'] = {'resource_id': resource, 'model_id': model_id, 'revision': 1, 'etag': etag, 'disk_sha256': sha(disk_bytes), 'expected': expected, 'stop_kind': 'explicit process termination after acknowledged durable create; not a graceful-drain claim'}
finally:
if process is not None and process.poll() is None:
stop(process)
check(file_map(PACKAGE) == prior['archive']['files'], 'server execution leaves complete extracted package unchanged')
check(sha(ZIP.read_bytes()) == EXPECTED_ZIP, 'frozen ZIP unchanged after independent runtime checks')
if __name__ == '__main__':
result_path = OUT / (sys.argv[1] + '-result.json')
if result_path.exists():
raise SystemExit('Refusing to overwrite prior evidence: ' + str(result_path))
try:
{'artifact': artifact, 'server': server}[sys.argv[1]]()
RESULT['status'] = 'PASS'
except Exception as error:
RESULT['status'] = 'FAIL'
RESULT['error'] = repr(error)
RESULT['traceback'] = traceback.format_exc()
RESULT['finished_utc'] = time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime())
RESULT['script_sha256'] = sha(Path(__file__).read_bytes())
RESULT['frozen_inputs_sha256'] = sha((OUT / 'frozen-inputs.json').read_bytes())
write_json(result_path, RESULT)
print(json.dumps({k: RESULT[k] for k in ('scope', 'status', 'started_utc', 'finished_utc')}, indent=2))
print('checks:', len(RESULT['checks']))
if RESULT['status'] == 'FAIL':
print(RESULT['traceback'])
sys.exit(1)