Replace the oversized header and reference rail with stable editor slots and a retained supporting drawer. Bind diagnostic, result and menu actions to their original publication and visit; preserve effective selections on navigation and dismiss Actions, Find and the drawer one surface per Escape. Retain exact Windows source captures, eight final gates (871 desktop passes), independent reviews, native selection/rename/recovery trials and the original failures. All 526 tested inputs and staged evidence were byte-checked before commit. Combined preview integration and packaging remain with issue 27; Linux remains paused.
278 lines
16 KiB
Python
278 lines
16 KiB
Python
"""Archive closed trial 3 / attempt 6; never run GUI, Git, compilers or oracles.
|
|
|
|
Run only after parent acceptance of these helper bytes and normal trial closure.
|
|
A pre-existing destination is always refused. Failed copies remain for inspection.
|
|
Historical receipts establish their own scope; this is not a new native evaluator.
|
|
"""
|
|
from pathlib import Path, PurePosixPath
|
|
from datetime import datetime, timezone
|
|
import hashlib
|
|
import json
|
|
import re
|
|
import shutil
|
|
import zipfile
|
|
|
|
BASE = Path(__file__).resolve().parent
|
|
ROOT = Path('C:/ar-text-9e8r6t')
|
|
TRIAL = BASE / 'trial-text-3'
|
|
GATE = ROOT / '.local/text-workspace-gates-20260912/attempt-6'
|
|
DEST = ROOT / 'harness/experiments/text-workspace-native3-20260913'
|
|
FIXTURES = ROOT / 'harness/experiments/text-workspace-native2-20260913'
|
|
REVIEWS = BASE.parent / 'text-workspace-design-20260912'
|
|
GATES = ['escape', 'clippy', 'membership', 'focused', 'selection', 'tests', 'timing', 'gui']
|
|
|
|
|
|
def require(condition, message):
|
|
if not condition:
|
|
raise RuntimeError(message)
|
|
|
|
|
|
def identity(path):
|
|
digest = hashlib.sha256()
|
|
size = 0
|
|
with path.open('rb') as stream:
|
|
for chunk in iter(lambda: stream.read(1024 * 1024), b''):
|
|
digest.update(chunk)
|
|
size += len(chunk)
|
|
return {'bytes': size, 'sha256': digest.hexdigest()}
|
|
|
|
|
|
def read(path):
|
|
return json.loads(path.read_text(encoding='utf-8'))
|
|
|
|
|
|
def match(path, expected):
|
|
measured = identity(path)
|
|
require(measured['sha256'] == expected['sha256'], f'Hash mismatch: {path}')
|
|
if 'bytes' in expected:
|
|
require(measured['bytes'] == expected['bytes'], f'Size mismatch: {path}')
|
|
return measured
|
|
|
|
|
|
def relative(name):
|
|
path = PurePosixPath(name)
|
|
require(name and '\\' not in name and not path.is_absolute()
|
|
and all(part not in ('..', '.', '') and ':' not in part for part in path.parts)
|
|
and path.as_posix() == name, f'Unsafe relative path: {name!r}')
|
|
return name
|
|
|
|
|
|
def main():
|
|
require(not DEST.exists(), f'Fresh destination required; preserve existing attempt: {DEST}')
|
|
plan = {}
|
|
names = set()
|
|
external = []
|
|
|
|
def add(source, name):
|
|
name = relative(name)
|
|
require(name.casefold() not in names, f'Duplicate/case-colliding archive name: {name}')
|
|
require(source.is_file() and not source.is_symlink(), f'Expected regular file: {source}')
|
|
names.add(name.casefold())
|
|
plan[name] = (source, identity(source))
|
|
|
|
def pin(path, role):
|
|
row = {'role': role, 'path': str(path.resolve()), **identity(path)}
|
|
external.append(row)
|
|
return row
|
|
|
|
# Closure facts are rechecked against preserved files, not trusted booleans.
|
|
closed = read(TRIAL / 'closed.json')
|
|
launch = read(TRIAL / 'launch.json')
|
|
binding = read(TRIAL / 'launch-manifest.json')
|
|
require(closed['exitCode'] == 0 and closed['launchReceiptError'] is None,
|
|
'Trial must have a recorded normal exit 0 and successful launch receipt')
|
|
require(closed['binaryUnchanged'] is True and closed['dllUnchanged'] is True,
|
|
'Closure reported changed executable or DLL')
|
|
require(closed['pid'] == launch['pid'], 'Launch/closure process mismatch')
|
|
match(TRIAL / 'launch-manifest.json', {'sha256': launch['manifestSha256']})
|
|
match(BASE / 'attempt-6-launch-manifest.json', {'sha256': launch['manifestSha256']})
|
|
require(binding['nativeReady'] is True and binding['target'] == 'x86_64-pc-windows-gnullvm',
|
|
'Wrong/unready source gate binding')
|
|
for stream in ['stdout', 'stderr']:
|
|
match(TRIAL / (stream + '.log'), closed[stream])
|
|
require(Path(launch['executable']).resolve() == (TRIAL / 'bin/arcadia-studio.exe').resolve(),
|
|
'Trial executable path mismatch')
|
|
match(TRIAL / 'bin/arcadia-studio.exe', launch['binary'])
|
|
match(TRIAL / 'bin/arcadia-studio.exe', binding['binary'])
|
|
match(BASE / 'launch.py', launch['helper'])
|
|
match(BASE / 'bind_gates.py', binding['helper'])
|
|
|
|
# Exact archived inputs and all eight successful gate receipts/logs.
|
|
inputs = read(GATE / 'inputs.json')
|
|
complete = read(GATE / 'complete.json')
|
|
require(complete['inputsUnchanged'] is True and complete['gates'] == GATES,
|
|
'Attempt 6 must contain all eight completed unchanged-input gates')
|
|
require(binding['sourceArchiveSha256'] == inputs['archiveSha256']
|
|
and binding['baseCommit'] == inputs['baseCommit']
|
|
and binding['sourceFiles'] == len(inputs['files']), 'Source binding mismatch')
|
|
match(GATE / 'inputs.zip', {'sha256': inputs['archiveSha256']})
|
|
entries = {relative(row['path']): row for row in inputs['files']}
|
|
require(len(entries) == len(inputs['files']), 'Duplicate input manifest paths')
|
|
with zipfile.ZipFile(GATE / 'inputs.zip') as archive:
|
|
require(len(archive.namelist()) == len(entries) and set(archive.namelist()) == set(entries),
|
|
'Input ZIP member inventory mismatch')
|
|
for name, row in entries.items():
|
|
raw = archive.read(name) # Also verifies the stored ZIP CRC.
|
|
require(len(raw) == row['bytes'] and hashlib.sha256(raw).hexdigest() == row['sha256'],
|
|
f'Input ZIP member mismatch: {name}')
|
|
receipts = {}
|
|
evidence_paths = set()
|
|
for name in GATES:
|
|
receipt = read(GATE / (name + '-result.json'))
|
|
require(receipt['exitCode'] == 0 and receipt['inputsUnchanged'] is True,
|
|
f'Failed or changed-input gate: {name}')
|
|
for stream in ['stdout', 'stderr']:
|
|
match(GATE / (name + '-' + stream + '.log'), {'sha256': receipt[stream + 'Sha256']})
|
|
receipts[name] = receipt
|
|
for row in binding['evidence']:
|
|
path = Path(row['path']).resolve()
|
|
require(path.parent == GATE.resolve(), f'Binding evidence outside attempt 6: {path}')
|
|
require(path.name not in evidence_paths, f'Duplicate binding evidence: {path}')
|
|
match(path, row)
|
|
evidence_paths.add(path.name)
|
|
expected_evidence = {'inputs.json', 'inputs.zip', 'environment.json', 'complete.json'}
|
|
expected_evidence.update(name + suffix for name in GATES
|
|
for suffix in ['-result.json', '-stdout.log', '-stderr.log'])
|
|
require(evidence_paths == expected_evidence, 'Binding does not reference exactly the eight gate receipts/logs')
|
|
require(binding['membership'] == receipts['membership']['testMembership'], 'Membership binding mismatch')
|
|
binary = receipts['gui']['binary']
|
|
require(Path(binary['preserved']).resolve() == (GATE / 'arcadia-studio.exe').resolve()
|
|
and Path(binding['binary']['path']).resolve() == (GATE / 'arcadia-studio.exe').resolve(),
|
|
'Preserved GUI path mismatch')
|
|
match(GATE / 'arcadia-studio.exe', binary)
|
|
match(GATE / 'arcadia-studio.exe', binding['binary'])
|
|
for path in sorted(GATE.iterdir()):
|
|
if path.is_file() and path.suffix in {'.json', '.log', '.zip'}:
|
|
add(path, 'attempt-6/' + path.name)
|
|
|
|
# The authoritative trial-2 archive supplies baseline/expected physical bytes.
|
|
# Link to it instead of duplicating the four fixture trees in this archive.
|
|
previous = read(FIXTURES / 'manifest.json')
|
|
old_files = {relative(row['path']): row for row in previous['files']}
|
|
require(len(old_files) == len(previous['files']), 'Duplicate prior archive paths')
|
|
fixture_manifest = read(BASE / 'fixture-manifest.json')
|
|
match(BASE / 'fixture-manifest.json', old_files['helpers/fixture-manifest.json'])
|
|
match(FIXTURES / 'helpers/fixture-manifest.json', old_files['helpers/fixture-manifest.json'])
|
|
for case, row in fixture_manifest['cases'].items():
|
|
for variant, key in [('baseline', 'original_files'), ('expected', 'expected_files')]:
|
|
prefix = 'fixtures/' + case + '/' + variant + '/'
|
|
archived = {name.removeprefix(prefix): {k: value[k] for k in ['bytes', 'sha256']}
|
|
for name, value in old_files.items() if name.startswith(prefix)}
|
|
require(archived == row[key], f'Authoritative archived fixture inventory differs: {case}/{variant}')
|
|
for name in archived:
|
|
match(FIXTURES / (prefix + relative(name)), archived[name])
|
|
pin(FIXTURES / 'manifest.json', 'authoritative trial-2 archive / fixture inventories')
|
|
pin(FIXTURES / 'helpers/fixture-manifest.json', 'authoritative fixture manifest')
|
|
dll_hash = fixture_manifest['dll_sha256']
|
|
for path, role in [(TRIAL / 'bin/libunwind.dll', 'trial DLL'),
|
|
(BASE / 'runtime/libunwind.dll', 'shared runtime DLL')]:
|
|
match(path, {'sha256': dll_hash})
|
|
pin(path, role)
|
|
pin(TRIAL / 'bin/arcadia-studio.exe', 'closed native executable; intentionally not copied')
|
|
pin(GATE / 'arcadia-studio.exe', 'gate-preserved executable; intentionally not copied')
|
|
|
|
# Capture every existing verification; require no simple transaction or extra
|
|
# baseline/expected pair. One representative expected check is sufficient.
|
|
verifications = []
|
|
screenshot_images = set()
|
|
screenshot_references = set()
|
|
for path in sorted(TRIAL.iterdir()):
|
|
if path.is_file():
|
|
require(path.suffix in {'.json', '.log', '.md', '.txt'}, f'Unexpected trial root payload: {path}')
|
|
add(path, 'trial-text-3/' + path.name)
|
|
elif path.name == 'screenshots':
|
|
for shot in sorted(path.iterdir()):
|
|
require(shot.is_file() and shot.suffix in {'.json', '.jpg', '.png'}, f'Unexpected screenshot payload: {shot}')
|
|
add(shot, 'trial-text-3/screenshots/' + shot.name)
|
|
if shot.suffix == '.json':
|
|
for image in read(shot)['screenshots']:
|
|
image_name = relative(image['file'])
|
|
require('/' not in image_name, 'Screenshot reference must be a local filename')
|
|
require((path / image_name).is_file(), f'Missing screenshot reference: {image_name}')
|
|
screenshot_references.add(image_name)
|
|
else:
|
|
screenshot_images.add(shot.name)
|
|
elif path.name.startswith('verification-'):
|
|
label = re.fullmatch(r'verification-(simple|representative)-([a-z0-9-]+)', path.name)
|
|
require(label is not None, f'Unknown verification directory: {path}')
|
|
case = label.group(1)
|
|
result = read(path / 'result.json')
|
|
require(result['passed'] is True and result['exitCode'] == 0 and result['failure'] is None
|
|
and result['physicalBytesMatch'] is True and result['actualUnchangedByOracle'] is True,
|
|
f'Verification is not a recorded PASS: {path}')
|
|
variant = result['expectedVariant']
|
|
require(variant in {'baseline', 'expected'}, 'Unknown verification expectation')
|
|
key = 'original_files' if variant == 'baseline' else 'expected_files'
|
|
require(result['actualFiles'] == fixture_manifest['cases'][case][key], 'Verification physical inventory differs from its named expectation')
|
|
require(result['binarySha256'] == launch['binary']['sha256']
|
|
and result['oracleSha256'] == fixture_manifest['oracle_sha256']
|
|
and result['dllSha256'] == dll_hash, 'Verification binary/oracle/DLL pin mismatch')
|
|
command = result['command']
|
|
require(len(command) == 4 and command[1] == 'same'
|
|
and Path(command[2]).resolve() == (BASE / case / variant / 'project.arcadia').resolve()
|
|
and Path(command[3]).resolve() == (BASE / case / 'work/project.arcadia').resolve(),
|
|
'Verification command does not name the recorded expectation and work fixture')
|
|
oracle = Path(command[0])
|
|
require(oracle.resolve() == (BASE.parents[1] / '.local/staged-source-oracle-20260911/staged-source-authoring-contract.exe').resolve(),
|
|
'Unexpected oracle executable path')
|
|
match(oracle, {'sha256': result['oracleSha256']})
|
|
if not any(row['role'] == 'saved-disk oracle; intentionally not copied' for row in external):
|
|
pin(oracle, 'saved-disk oracle; intentionally not copied')
|
|
for stream in ['stdout', 'stderr']:
|
|
match(path / (stream + '.log'), {'sha256': result[stream + 'Sha256']})
|
|
require({p.name for p in path.iterdir()} == {'result.json', 'stdout.log', 'stderr.log'}, 'Unexpected verification payload; review before archiving')
|
|
for item in sorted(path.iterdir()):
|
|
add(item, 'trial-text-3/' + path.name + '/' + item.name)
|
|
verifications.append({'directory': path.name, 'case': case, 'expectedVariant': variant,
|
|
'scope': result['scope'], 'resultSha256': identity(path / 'result.json')['sha256']})
|
|
else:
|
|
require(path.name in {'bin', 'profile', 'tmp', 'cwd'}, f'Unclassified trial directory: {path}')
|
|
require(screenshot_images and screenshot_images == screenshot_references, 'Screenshot payload/reference inventory differs or is empty')
|
|
require(verifications, 'At least one saved-state verification is required; no transaction sequence is inferred')
|
|
|
|
for name in ['fixture-manifest.json', 'bind_gates.py', 'launch.py', 'verify_saved.py', 'native-recorder.js']:
|
|
add(BASE / name, 'helpers/' + name)
|
|
add(GATE.parent / 'gate.py', 'helpers/gate.py')
|
|
add(Path(__file__).resolve(), 'helpers/archive_trial3.py')
|
|
for name in ['native2-escape-undo-review.md', 'escape-correction-review.md',
|
|
'focus-selection-review.md', 'departure-selection-review.md',
|
|
'selection-correction-evidence-review.md', 'integration-review.md']:
|
|
add(REVIEWS / name, 'reviews/' + name)
|
|
# A fresh attempt-6 review can be preserved if authored before this helper runs.
|
|
if (REVIEWS / 'attempt6-evidence-review.md').is_file():
|
|
add(REVIEWS / 'attempt6-evidence-review.md', 'reviews/attempt6-evidence-review.md')
|
|
|
|
# Only now create the exclusive destination. No overwrite or cleanup recovery.
|
|
DEST.mkdir()
|
|
records = []
|
|
for name, (source, expected) in sorted(plan.items()):
|
|
target = DEST / name
|
|
target.parent.mkdir(parents=True, exist_ok=True)
|
|
with source.open('rb') as incoming, target.open('xb') as outgoing:
|
|
shutil.copyfileobj(incoming, outgoing, 1024 * 1024)
|
|
match(source, expected)
|
|
match(target, expected)
|
|
records.append({'path': name, **expected})
|
|
manifest = {
|
|
'atUtc': datetime.now(timezone.utc).isoformat(),
|
|
'scope': 'Closed development native trial 3 bound to exact attempt-6 inputs and eight successful gates. Saved-disk checks have only their recorded expectations; no unperformed transaction, full UI workflow or packaged-release acceptance is inferred.',
|
|
'trial': 'trial-text-3', 'attempt': 'attempt-6', 'closedExitCode': closed['exitCode'],
|
|
'sourceArchiveSha256': inputs['archiveSha256'], 'gates': GATES,
|
|
'savedVerifications': verifications,
|
|
'fixtureArchive': '../text-workspace-native2-20260913/manifest.json',
|
|
'externalPins': external,
|
|
'helperScope': 'Archived gate.py/verify_saved.py are the current preserved helpers; receipts do not independently bind their historical helper bytes. Launch and bind_gates helper hashes are checked against their receipts.',
|
|
'excluded': ['executables', 'DLL payloads', 'profiles', 'temporary directories', 'working fixture copies', 'duplicate authored fixtures', 'build caches'],
|
|
'files': records,
|
|
}
|
|
with (DEST / 'manifest.json').open('x', encoding='utf-8', newline='\n') as output:
|
|
json.dump(manifest, output, ensure_ascii=False, indent=2)
|
|
output.write('\n')
|
|
print(json.dumps({'directory': str(DEST), 'files': len(records),
|
|
'bytes': sum(row['bytes'] for row in records),
|
|
'manifestSha256': identity(DEST / 'manifest.json')['sha256']}), flush=True)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|