Verify Windows contextual authoring and preserve postboot evidence
This commit is contained in:
+57
@@ -0,0 +1,57 @@
|
||||
import datetime
|
||||
import hashlib
|
||||
import json
|
||||
from pathlib import Path
|
||||
import sys
|
||||
import uuid
|
||||
|
||||
repo = Path(__file__).resolve().parents[2]
|
||||
trial = repo / '.local/native-qa/contextual-source-20260911-v2/lifeline'
|
||||
base = repo / 'harness/fixtures/staged-source-authoring/base'
|
||||
mode = sys.argv[1]
|
||||
assert mode in ('before-stop', 'after-restore')
|
||||
expected = {p.name: p.read_text(encoding='utf-8') for p in base.glob('*.arcadia')}
|
||||
assert len(expected) == 2
|
||||
matches = []
|
||||
for p in (trial / 'profile/local/ArcadiaStudio/recovery').glob('*.json'):
|
||||
raw = p.read_bytes()
|
||||
j = json.loads(raw)
|
||||
draft = (j.get('behavior') or {}).get('draft') or {}
|
||||
source = (draft.get('source') or {}).get('editing') or {}
|
||||
if (draft.get('name') == ' Retain parent 雨 ' and source.get('name') == ' Retain source Δ '
|
||||
and (mode == 'before-stop' or j.get('origin') is None)):
|
||||
matches.append((p, raw, j, draft, source))
|
||||
assert len(matches) == 1, f'Expected one completed matching recovery record, got {len(matches)}'
|
||||
p, raw, j, draft, source = matches[0]
|
||||
assert j['accepted'] == {'entry': 'project.arcadia', 'files': expected}
|
||||
assert j['workspace']['entry'] == 'project.arcadia'
|
||||
assert j['workspace']['applied'] is True
|
||||
assert j['workspace']['files'] == {k: [v, v] for k, v in expected.items()}
|
||||
assert set(j['workspace']['reachable']) == set(expected)
|
||||
assert source['kind'] == 'Activity' and source['layer'] == 'Oa' and source['parent'] is None
|
||||
assert source['document'] == 'd1510000-0000-4000-8000-000000000001'
|
||||
assert source['scope'] == {'Lifeline': {'owner': 'd1510000-0000-4000-8000-000000000002', 'viewpoint': 'OperationalActivity'}}
|
||||
assert draft['tool'] == 'Lifeline' and draft['source']['staged'] is None
|
||||
assert draft['owner'] == 'd1510000-0000-4000-8000-000000000002'
|
||||
ids = [draft['invocation'], source['id'], source['token'], *source['identities']]
|
||||
assert len(ids) == 4 and len(set(ids)) == 4
|
||||
assert all(uuid.UUID(i).version == 4 for i in ids)
|
||||
if mode == 'before-stop':
|
||||
assert {p.name: p.read_text(encoding='utf-8') for p in (trial / 'work').glob('*.arcadia')} == expected
|
||||
else:
|
||||
before = json.loads((trial / 'recovery-evidence/before-stop.json').read_bytes())
|
||||
assert j['accepted'] == before['accepted']
|
||||
assert j['behavior'] == before['behavior'], 'Authored pending behavior/identities changed on restore'
|
||||
assert j['origin'] is None, 'Recovered copy must not retain writable source origin'
|
||||
out = trial / 'recovery-evidence'
|
||||
out.mkdir(exist_ok=True)
|
||||
with (out / f'{mode}.json').open('xb') as f:
|
||||
f.write(raw)
|
||||
result = {'atUtc': datetime.datetime.now(datetime.timezone.utc).isoformat(), 'mode': mode,
|
||||
'source': str(p), 'sha256': hashlib.sha256(raw).hexdigest(), 'reservedIds': ids,
|
||||
'acceptedBaselineFiles': len(expected), 'rawParentAndChildNames': 'exact',
|
||||
'kindAndRoot': 'Activity/Oa/root', 'result': 'PASS'}
|
||||
with (out / f'{mode}-result.json').open('x', encoding='utf-8') as f:
|
||||
json.dump(result, f, ensure_ascii=False, indent=2)
|
||||
f.write('\n')
|
||||
print(json.dumps(result, ensure_ascii=False))
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
"""Preserve a real GUI Save checkpoint and run the unchanged independent oracle."""
|
||||
import argparse
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
import shutil
|
||||
import subprocess
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('trial', choices=['lifeline', 'machine'])
|
||||
parser.add_argument('checkpoint', choices=['first', 'undo', 'redo', 'reopen', 'cancelled'])
|
||||
args = parser.parse_args()
|
||||
root = Path(__file__).resolve().parents[2]
|
||||
stage = root/'.local/native-qa/contextual-source-20260911-v2'/args.trial
|
||||
prior = root/'.local/native-qa/contextual-source-20260911-v1'
|
||||
base = root/'harness/fixtures/staged-source-authoring/base'
|
||||
oracle = root/'.local/staged-source-oracle-20260911/staged-source-authoring-contract.exe'
|
||||
runtime = prior/'bin'
|
||||
digest = lambda p: hashlib.sha256(p.read_bytes()).hexdigest().upper()
|
||||
assert digest(oracle) == '967D9BA3C6FB3A7EFB410833903EC94556CA2D48A67D5072C8BB160F40A00F9C'
|
||||
assert digest(runtime/'libunwind.dll') == 'F45604A12E8BCD839FD86F667C1BE1CC7C4F9DA2B3C69FF381AC708F0E1AE7D4'
|
||||
destination = stage/('checkpoint-'+args.checkpoint)
|
||||
assert not destination.exists(), 'Refuse to overwrite a checkpoint'
|
||||
source = stage/('cancelled-copy' if args.checkpoint == 'cancelled' else 'work')
|
||||
assert source.is_dir() and (source/'project.arcadia').is_file()
|
||||
assert all(not p.is_symlink() for p in source.rglob('*'))
|
||||
shutil.copytree(source, destination)
|
||||
entry = lambda p:str(p/'project.arcadia')
|
||||
first = stage/'checkpoint-first'
|
||||
commands = []
|
||||
if args.checkpoint == 'cancelled':
|
||||
assert args.trial == 'lifeline'
|
||||
commands.append(['same', entry(base), entry(destination)])
|
||||
elif args.checkpoint == 'undo':
|
||||
commands.append(['same', entry(base if args.trial == 'lifeline' else prior/'checkpoint-chain'), entry(destination), entry(first)])
|
||||
elif args.trial == 'lifeline':
|
||||
commands.append(['check', 'lifeline', entry(base), entry(destination), entry(first)])
|
||||
else:
|
||||
commands.append(['check', 'machine', entry(base), entry(destination), entry(prior/'checkpoint-lifeline'), entry(prior/'checkpoint-chain'), entry(first)])
|
||||
if args.checkpoint == 'reopen':
|
||||
commands.append(['same', entry(stage/'checkpoint-redo'), entry(destination)])
|
||||
env = dict(os.environ)
|
||||
system_root = next(v for k,v in env.items() if k.upper() == 'SYSTEMROOT')
|
||||
env['PATH'] = str(runtime)+';'+system_root+'\\System32;'+system_root
|
||||
results = []
|
||||
for index, command in enumerate(commands, 1):
|
||||
log = stage/(args.checkpoint+'-check-'+str(index)+'.log')
|
||||
assert not log.exists()
|
||||
result = subprocess.run([str(oracle), *command],env=env,stdout=subprocess.PIPE,stderr=subprocess.STDOUT)
|
||||
log.write_bytes(result.stdout)
|
||||
results.append({'args':command, 'exitCode':result.returncode, 'log':log.name})
|
||||
record = {'scope':'Independent complete model, raw source, original identity and physical-file contract', 'trial':args.trial,'checkpoint':args.checkpoint,'oracleSha256':digest(oracle),'checks':results,'files':[{'path':p.relative_to(destination).as_posix(),'bytes':p.stat().st_size,'sha256':digest(p)} for p in sorted(destination.rglob('*')) if p.is_file()]}
|
||||
(stage/(args.checkpoint+'-result.json')).write_text(json.dumps(record,indent=2)+'\n',encoding='utf8')
|
||||
for result in results:
|
||||
print((stage/result['log']).read_text(encoding='utf8'), end='')
|
||||
assert all(result['exitCode'] == 0 for result in results), 'Oracle failure preserved'
|
||||
print(json.dumps({'state':'pass','trial':args.trial,'checkpoint':args.checkpoint,'files':len(record['files']),'checks':len(results)}))
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
param(
|
||||
[Parameter(Mandatory)][ValidateSet('lifeline','machine')][string]$Trial,
|
||||
[Parameter(Mandatory)][ValidatePattern('^[a-z0-9-]+$')][string]$Run
|
||||
)
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$trialRepo = 'C:\Users\User\Documents\Git\ARCADIA-Studio'
|
||||
$evidence = Join-Path $trialRepo '.local/contextual-source-layout-20260911'
|
||||
$verified = Join-Path $evidence 'gui-verified'
|
||||
$nativeTrial = Join-Path $trialRepo ".local/native-qa/contextual-source-20260911-v2/$Trial"
|
||||
$launchDir = Join-Path $nativeTrial "runs/$Run"
|
||||
if (Test-Path -LiteralPath $launchDir) { throw 'Refuse to overwrite a native run' }
|
||||
if (Get-Process -Name arcadia-studio -ErrorAction SilentlyContinue) { throw 'Studio already running' }
|
||||
$preflight = Get-Content -LiteralPath (Join-Path (Split-Path $nativeTrial) 'preflight.json') -Raw | ConvertFrom-Json
|
||||
if (@($preflight.checks | Where-Object exitCode -ne 0).Count) { throw 'Native input oracle did not pass' }
|
||||
$manifest = Get-Content -LiteralPath (Join-Path $verified 'manifest.json') -Raw | ConvertFrom-Json
|
||||
foreach ($artifact in $manifest.artifacts) {
|
||||
if ((Get-FileHash -LiteralPath (Join-Path $verified $artifact.file) -Algorithm SHA256).Hash -ne $artifact.sha256) { throw 'Verified artifact changed' }
|
||||
}
|
||||
foreach ($gate in 'focused4','clippy1','fmt1','gui1') {
|
||||
$status = Get-Content -LiteralPath (Join-Path $evidence ($gate + '-status.json')) -Raw | ConvertFrom-Json
|
||||
if ($status.exitCode -ne 0) { throw "Unpassed build gate $gate" }
|
||||
}
|
||||
New-Item -ItemType Directory -Path $launchDir | Out-Null
|
||||
foreach ($part in 'local','roaming') {
|
||||
$profilePart = Join-Path $nativeTrial "profile/$part"
|
||||
if (-not (Test-Path -LiteralPath $profilePart)) { New-Item -ItemType Directory -Path $profilePart | Out-Null }
|
||||
}
|
||||
$savedPath = $env:PATH
|
||||
$savedAppData = $env:APPDATA
|
||||
$savedLocalAppData = $env:LOCALAPPDATA
|
||||
try {
|
||||
$env:PATH = "$verified;$env:SystemRoot\System32;$env:SystemRoot"
|
||||
$env:APPDATA = "$nativeTrial/profile/roaming"
|
||||
$env:LOCALAPPDATA = "$nativeTrial/profile/local"
|
||||
$binary = Join-Path $verified 'arcadia-studio.exe'
|
||||
$child = Start-Process -FilePath $binary -WorkingDirectory $verified -WindowStyle Hidden -PassThru -RedirectStandardOutput "$launchDir/stdout.log" -RedirectStandardError "$launchDir/stderr.log"
|
||||
[ordered]@{pid=$child.Id;startedUtc=[DateTimeOffset]::UtcNow.ToString('o');trial=$Trial;run=$Run;executable=$binary;artifacts=$manifest.artifacts;profile="$nativeTrial/profile";workingModel="$nativeTrial/work/project.arcadia";scope='Matching corrected source-layout GUI; native assertions remain separate'} | ConvertTo-Json -Depth 6 | Set-Content -LiteralPath "$launchDir/launch.json"
|
||||
$child | Select-Object Id,ProcessName
|
||||
} finally {
|
||||
$env:PATH = $savedPath
|
||||
$env:APPDATA = $savedAppData
|
||||
$env:LOCALAPPDATA = $savedLocalAppData
|
||||
}
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
root = Path(__file__).resolve().parents[2]
|
||||
prior = root/'.local/native-qa/contextual-source-20260911-v1'
|
||||
stage = root/'.local/native-qa/contextual-source-20260911-v2'
|
||||
base = root/'harness/fixtures/staged-source-authoring/base'
|
||||
oracle = root/'.local/staged-source-oracle-20260911/staged-source-authoring-contract.exe'
|
||||
digest = lambda p: hashlib.sha256(p.read_bytes()).hexdigest().upper()
|
||||
resume = sys.argv[1:] == ['--resume-preflight']
|
||||
assert not stage.exists() or resume, 'Refuse to overwrite native stage'
|
||||
assert digest(base/'project.arcadia') == 'AA844950EC4FB86F262A257E1FC370A26557EA7978D8A4CAB78265037660234C'
|
||||
assert digest(base/'notes.arcadia') == 'AD9DE02963C20D1F43856455A797DEB05CA674659E45F2C4B96B52783024F8BD'
|
||||
assert digest(oracle) == '967D9BA3C6FB3A7EFB410833903EC94556CA2D48A67D5072C8BB160F40A00F9C'
|
||||
assert digest(prior/'bin/libunwind.dll') == 'F45604A12E8BCD839FD86F667C1BE1CC7C4F9DA2B3C69FF381AC708F0E1AE7D4'
|
||||
def inventory(directory):
|
||||
return {p.relative_to(directory).as_posix():digest(p) for p in directory.rglob('*') if p.is_file()}
|
||||
assert inventory(prior/'checkpoint-chain-redo') == inventory(prior/'checkpoint-chain')
|
||||
assert len(inventory(prior/'checkpoint-chain-redo')) == 4
|
||||
if resume:
|
||||
assert not (stage/'preflight.json').exists()
|
||||
assert inventory(base) == inventory(stage/'lifeline/work')
|
||||
assert inventory(prior/'checkpoint-chain-redo') == inventory(stage/'machine/work')
|
||||
else:
|
||||
shutil.copytree(base, stage/'lifeline/work')
|
||||
shutil.copytree(prior/'checkpoint-chain-redo', stage/'machine/work')
|
||||
env = dict(os.environ)
|
||||
system_root = next(v for k,v in env.items() if k.upper() == 'SYSTEMROOT')
|
||||
env['PATH'] = str(prior/'bin') + ';' + system_root + '\\System32;' + system_root
|
||||
entry = lambda p:str(p/'project.arcadia')
|
||||
checks = [
|
||||
('self-check', ['self-check', entry(base)]),
|
||||
('lifeline-initial', ['check', 'baseline', entry(base), entry(stage/'lifeline/work')]),
|
||||
('machine-initial', ['check', 'chain', entry(base), entry(stage/'machine/work'), entry(prior/'checkpoint-lifeline'), entry(prior/'checkpoint-chain')]),
|
||||
]
|
||||
results = []
|
||||
for name,args in checks:
|
||||
assert not (stage/(name+'.log')).exists(), 'Refuse to overwrite earlier checker log'
|
||||
p = subprocess.run([str(oracle),*args],env=env,stdout=subprocess.PIPE,stderr=subprocess.STDOUT)
|
||||
(stage/(name+'.log')).write_bytes(p.stdout)
|
||||
results.append({'check':name, 'exitCode':p.returncode, 'args':args})
|
||||
assert p.returncode == 0, p.stdout
|
||||
(stage/'preflight.json').write_text(json.dumps({'scope':'Frozen inputs only; no new GUI or native interaction', 'oracle':str(oracle), 'oracleSha256':digest(oracle), 'checks':results, 'lifelineInput':inventory(stage/'lifeline/work'), 'machineInput':inventory(stage/'machine/work')},indent=2)+'\n',encoding='utf8')
|
||||
print(json.dumps({'state':'prepared', 'checks':len(results), 'path':str(stage)}))
|
||||
Reference in New Issue
Block a user