Preserve navigation Windows package preparation and exact source provenance
This commit is contained in:
@@ -0,0 +1,27 @@
|
||||
# Repaired navigation prerelease adapters — not executed
|
||||
|
||||
Codex-9e8r6t /root/release_audit. Root owns execution, independent acceptance and publication of reserved `v0.2.0-preview.navigation.20260911` (#6/comment493). This is a separate adapter set; the earlier `.local/navigation-prerelease-20260911` and its review remain unchanged historical evidence. No clone, metadata, package, application or compiler was run to prepare this set. Only static AST/PowerShell parsing, source/receipt reads and private adapter writes were performed. `adapt.py` records the initial text adaptation; subsequent narrow review corrections are in the final scripts. Do not rerun the generator over the frozen set.
|
||||
|
||||
## Exact scope and provenance
|
||||
|
||||
The immutable code commit is `33c6d83e9ba9a5b1310b1b627d7c40affcbcf15c`. The 500 raw build inputs are `.local/text-repair-save-20260911/inputs1.json` (SHA256 `75a282fd3d4cb2dec6763046ea45d0caa015b998281fbdd6f222d8e3a175b13f`) and its ZIP `213fdb39ea7c3601b4baac674c3ab3ecc863ac44db1b092d922d3e24f26887b7`. Gates were actually captured on eeeaa126 with the repair delta; 33c6d83 commits those exact code bytes. The adapter proves normalized Git equivalence and raw-file identity; it never relabels the original gate HEAD as 33c6d83.
|
||||
|
||||
The actual five successful command receipts and their hash-bound logs plus complete.json are required. Full suite must report 755 passed, 0 failed, 1 ignored, no filtering. Preserved gui-repair.exe must match actual gui-build-result.json: SHA256 `b183ef31f16b3986538b3f2aba8435eab45978635c47f99ca01be6e72b168848`, 113,206,272 bytes. Old GUI5478 is not accepted. Native-v4 is separately root-owned and pending here; no native pass is inferred from these gates or an application launch.
|
||||
|
||||
EFEE server remains the historical September 7 artifact (`efeeb89c0d2e0f93df4472cb3e9f48b2b722a37ae1043c90a5a56f2e845fe665`); runtime remains F456 (`f45604a12e8bcd839fd86f667c1be1cc7c4f9da2b3c69ff381ac708f0e1ae7d4`). The server's original tar, build/hash records and source/notice inventory are copied separately. Current core language.rs differs and language_outline_tests.rs is added. The server is not rebuilt and must never be described as a current-source server. Historical registry checksums must still be locked; readback additionally requires historical package features, license/font notice identities and actual notice bytes in the ZIP. An unexpected difference refuses and requires review rather than a blanket licensing fallback.
|
||||
|
||||
## Future explicitly authorized sequence
|
||||
|
||||
From repository cwd, use bundled Python `C:/Users/User/.cache/codex-runtimes/codex-primary-runtime/dependencies/python/python.exe`:
|
||||
|
||||
1. `python .local/navigation-repair-prerelease-20260911/prepare.py`
|
||||
2. With root's metadata lease: `pwsh -NoProfile -File .local/navigation-repair-prerelease-20260911/package2.ps1`
|
||||
3. `python .local/navigation-repair-prerelease-20260911/readback.py`
|
||||
|
||||
Replace `python` with the absolute bundled executable. These are instructions, not executed results. Preparation exclusively creates source/, source-roundtrip/, bin/, evidence/ and receipts. It uses a separate local clone and branch `codex/navigation-repair-prerelease-source-20260911`; no root index/refs mutation. All 500 paths and bytes must match current working inputs and the designated Git tree. Newline-only restorations are measured afresh, scoped -text rules applied only in the clone, committed locally and checked byte-for-byte in a fresh checkout. No former 11/15-path count is assumed. Original evidence is copied byte-exact; no executable is compiled.
|
||||
|
||||
Root may have later **record-only** commits: the designated source must be an ancestor and every changed path must be under harness/ excluding harness/fixtures/. Those paths and the actual root HEAD are recorded separately, never substituted into binary provenance. Any later source, docs, dependency, script or instruction change refuses. Tracked WIP also refuses. Root HEAD/index/status must remain unchanged throughout preparation. Later record files are not silently included in the clone based at 33c6d83.
|
||||
|
||||
Packaging retains existing strict scripts, explicit x86_64-pc-windows-gnullvm and one exact locked/offline Cargo metadata invocation. It never builds or downloads. PS7 avoids the prior PS5 native stderr failure. Refusal-to-overwrite remains; a failed attempt preserves partial outputs and needs a fresh reviewed retry directory. Readback derives actual ZIP hash/counts and verifies every stage/checksum/source/evidence/binary entry. It is producer integrity evidence only, not independent package/server/native acceptance.
|
||||
|
||||
Do not execute old adapters, publish a source branch/tag/release, or treat VALIDATION.pending.md as final acceptance. No WSL operations are included. All output actions await root's explicit execution after native acceptance; final package GUI/server checks remain root/reviewer-owned.
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
# Pending repaired navigation Windows milestone
|
||||
|
||||
Draft only. Planned version: 0.2.0-preview.navigation.20260911. No ZIP or packaged acceptance exists from these adapters.
|
||||
|
||||
GUI: 33c6d83 committed equivalent of the 500 raw repair inputs, ZIP213fdb39; actual B183 preserved GUI. Recorded headless gates: 755 passed, 0 failed, 1 ignored; strict all-target Clippy, scoped format, diff check and GUI build succeeded. Original build captured eeeaa126 plus the repair delta; exact receipts retain that distinction.
|
||||
|
||||
Server: retained historical EFEE September 7 binary with its own source snapshot/build/notices. It is not built from 33c6d83. Runtime F456 unchanged. Current metadata collection is not retroactive server build provenance.
|
||||
|
||||
Pending: root's repaired native recovery/save acceptance; actual package creation/readback; independent historical/current notice coverage and packaged server restart/model oracle; packaged GUI launch/save/package-integrity acceptance; final source/tag/asset publication and byte readback. Earlier native5478 passed navigation checks but exposed false-clean Apply status; those passes do not close the repaired candidate's native gate. Root owns replacing this draft with actual results.
|
||||
@@ -0,0 +1,110 @@
|
||||
"""Static text adaptation only; never imports or runs the prepared adapters."""
|
||||
from pathlib import Path
|
||||
import ast
|
||||
import hashlib
|
||||
import json
|
||||
|
||||
BASE = Path(__file__).resolve().parent
|
||||
ROOT = BASE.parents[1]
|
||||
OLD = ROOT / '.local/navigation-prerelease-20260911'
|
||||
def sha(p):
|
||||
return hashlib.sha256(p.read_bytes()).hexdigest()
|
||||
old_inventory = {p.name: sha(p) for p in OLD.iterdir() if p.is_file()}
|
||||
native = (ROOT / '.local/text-repair-save-native-20260911/helper.py').read_text(encoding='utf-8')
|
||||
commands = native[native.index('COMMANDS = {'):native.index('\n\n\ndef now')]
|
||||
p = (OLD / 'prepare.py').read_text(encoding='utf-8')
|
||||
p = p.replace('import traceback', 'import traceback\nimport sys\nimport tomllib')
|
||||
p = p.replace("COMMIT = 'eeeaa1262748800c4c9aaa1eca79a7de96757358'", "COMMIT = '33c6d83e9ba9a5b1310b1b627d7c40affcbcf15c'")
|
||||
p = p.replace("BRANCH = 'codex/navigation-prerelease-source-20260911'", "BRANCH = 'codex/navigation-repair-prerelease-source-20260911'")
|
||||
p = p.replace("GATES = ROOT / '.local/text-navigation-native-fixes-20260911'\nDESKTOP = ROOT / '.local/text-navigation-desktop-20260911'", "GATES = ROOT / '.local/text-repair-save-20260911'")
|
||||
p = p.replace('72c2e8154f9945aa87d0cbf9cc148934a84614e15576673496aa5a5907c12154', '75a282fd3d4cb2dec6763046ea45d0caa015b998281fbdd6f222d8e3a175b13f')
|
||||
p = p.replace('7ff9e5703e33bd72e5c2186dd61125e965b82b8dfbf085961d514b005e716798', '213fdb39ea7c3601b4baac674c3ab3ecc863ac44db1b092d922d3e24f26887b7')
|
||||
p = p.replace('\n\n\ndef now', '\n\n' + commands + '\n\n\ndef now', 1)
|
||||
p = p.replace('inputs4', 'inputs1')
|
||||
a = p.index(" gate_path = GATES / 'final-gate-result.json'")
|
||||
b = p.index(" server = ROOT /", a)
|
||||
p = p[:a] + ''' assert len(expected) == 500
|
||||
complete = load(GATES / 'complete.json')
|
||||
assert complete['archiveSha256'] == ZIP_SHA and complete['inputsUnchanged'] is True
|
||||
assert complete['gates'] == list(COMMANDS) and complete['sourceHead'] == manifest['head']
|
||||
# The gates captured eeeaa126 plus the repair delta; COMMIT commits those
|
||||
# exact raw inputs. Never relabel the captured sourceHead as a clean build.
|
||||
assert manifest['head'] == 'eeeaa1262748800c4c9aaa1eca79a7de96757358'
|
||||
for name, command in COMMANDS.items():
|
||||
receipt = load(GATES / (name + '-result.json'))
|
||||
assert receipt['exitCode'] == 0 and receipt['inputsUnchanged'] is True and receipt['command'] == command
|
||||
assert sha(GATES / (name + '-stdout.log')) == receipt['stdoutSha256']
|
||||
assert sha(GATES / (name + '-stderr.log')) == receipt['stderrSha256']
|
||||
build = load(GATES / 'gui-build-result.json')['binary']
|
||||
artifact = build['artifact']
|
||||
assert artifact['target']['name'] == 'arcadia-studio' and artifact['profile']['test'] is False
|
||||
assert artifact['profile']['opt_level'] == '0' and artifact['profile']['debug_assertions'] is True
|
||||
assert Path(artifact['executable']).resolve() == Path(build['path']).resolve()
|
||||
assert Path(build['path']).resolve().is_relative_to(ROOT / '.local/target-gnullvm')
|
||||
gui = GATES / 'gui-repair.exe'
|
||||
assert sha(gui) == build['sha256'] == 'b183ef31f16b3986538b3f2aba8435eab45978635c47f99ca01be6e72b168848'
|
||||
assert gui.stat().st_size == build['bytes'] == 113206272
|
||||
summaries = re.findall(r'test result: ok\\. (\\d+) passed; (\\d+) failed; (\\d+) ignored; (\\d+) measured; (\\d+) filtered out;',
|
||||
(GATES / 'desktop-tests-stdout.log').read_text(encoding='utf-8'))
|
||||
assert summaries == [('755', '0', '1', '0', '0')]
|
||||
counts = dict(zip(('passed', 'failed', 'ignored', 'measured', 'filtered'), map(int, summaries[0])))
|
||||
''' + p[b:]
|
||||
p = p.replace(" manifest, frozen, build, gui, counts, server, dll, old = admission()", """ # Later root commits may record evidence only. Do not silently package later
|
||||
# production/docs/dependency or instruction changes under the tested source.
|
||||
git(ROOT, 'merge-base', '--is-ancestor', COMMIT, before['head'])
|
||||
later = git(ROOT, 'diff', '--name-only', '-z', COMMIT, before['head']).decode().split('\\0')[:-1]
|
||||
assert all(p.startswith('harness/') and not p.startswith('harness/fixtures/') for p in later), later
|
||||
manifest, frozen, build, gui, counts, server, dll, old = admission()
|
||||
for path, raw in frozen.items():
|
||||
assert ordinary(ROOT / path).read_bytes() == raw, f'Current frozen input changed: {path}'""")
|
||||
p = p.replace(" # Local clone has its own index/refs", """ registry = {(x['name'], x['version'], x['source']): x['registry_checksum']
|
||||
for x in notice['packages'] if x['source']}
|
||||
locked = {(x['name'], x['version'], x.get('source')): x.get('checksum')
|
||||
for x in tomllib.loads(frozen['Cargo.lock'].decode())['package']}
|
||||
assert registry and all(locked.get(k) == v for k, v in registry.items()), 'Historical registry coverage differs.'
|
||||
# Local clone has its own index/refs""")
|
||||
p = p.replace(" frozen[p] = (ROOT / p).read_bytes()", " if p in frozen:\n assert frozen[p] == (ROOT / p).read_bytes(), p\n frozen[p] = (ROOT / p).read_bytes()")
|
||||
a = p.index(' provenance.update({')
|
||||
b = p.index(" (BASE / 'evidence').mkdir()",a)
|
||||
p = p[:a] + ''' provenance.update({'gui-inputs1.json': GATES / 'inputs1.json', 'gui-complete.json': GATES / 'complete.json',
|
||||
'gui-gate-run.py': GATES / 'run.py'})
|
||||
for name in COMMANDS:
|
||||
for suffix in ('-result.json', '-stdout.log', '-stderr.log'):
|
||||
provenance['gui-' + name + suffix] = GATES / (name + suffix)
|
||||
''' + p[b:]
|
||||
p = p.replace("'build': 'evidence/gui-build-v3.json', 'preserved': 'evidence/gui-v3-preserved.json', 'inputs': 'evidence/gui-inputs1.json', 'full_gate': 'evidence/gui-full-corrected.json'", "'build': 'evidence/gui-gui-build-result.json', 'inputs': 'evidence/gui-inputs1.json', 'full_gate': 'evidence/gui-desktop-tests-result.json', 'complete': 'evidence/gui-complete.json', 'captured_head': manifest['head'], 'scope': 'Built from captured eeeaa126 plus exact repair input delta; those bytes are committed at base_commit, then reconstructed without changing code.'")
|
||||
p = p.replace("'branch': BRANCH, 'source_repository': str(SOURCE)", "'branch': BRANCH, 'record_only_root_paths_after_base': later, 'historical_registry_count': len(registry), 'source_repository': str(SOURCE)")
|
||||
p = p.replace("if __name__ == '__main__':\n", "if __name__ == '__main__':\n sys.stdout.reconfigure(encoding='utf-8')\n sys.stderr.reconfigure(encoding='utf-8')\n")
|
||||
(BASE / 'prepare.py').write_text(p, encoding='utf-8', newline='\n')
|
||||
ps = (OLD / 'package2.ps1').read_text(encoding='utf-8').replace('eeeaa1262748800c4c9aaa1eca79a7de96757358', '33c6d83e9ba9a5b1310b1b627d7c40affcbcf15c')
|
||||
(BASE / 'package2.ps1').write_text(ps, encoding='utf-8', newline='\n')
|
||||
r = (OLD / 'readback.py').read_text(encoding='utf-8').replace('import stat', 'import stat\nimport sys')
|
||||
r = r.replace('relative, historical_inputs', 'relative, historical_inputs, HISTORY')
|
||||
r = r.replace(" docs = load(stage / 'documentation-check.json')", """ previous = load(HISTORY / 'prior-dependency-notices.json')
|
||||
def registry_key(item):
|
||||
return item['name'], item['version'], item['source'], item['registry_checksum']
|
||||
collected = {registry_key(x): x for x in notices['packages'] if x['source']}
|
||||
covered = 0
|
||||
for old in previous['packages']:
|
||||
if not old['source']:
|
||||
continue
|
||||
current = collected[registry_key(old)]
|
||||
assert old['license'] == current['license']
|
||||
assert set(old['features']).issubset(current['features'])
|
||||
# Exact recorded notice files must remain in the actual ZIP. Current
|
||||
# metadata does not substitute for the historical server's provenance.
|
||||
new_notices = {(n['path'], n['sha256']) for n in current['notices']}
|
||||
for item in old['notices']:
|
||||
assert (item['path'], item['sha256']) in new_notices
|
||||
assert actual[item['path']]['sha256'] == item['sha256']
|
||||
assert old['embedded_fonts'] == current['embedded_fonts']
|
||||
covered += 1
|
||||
assert covered == preparation['historical_registry_count']
|
||||
docs = load(stage / 'documentation-check.json')""")
|
||||
r = r.replace("'notice_roots': notices['root_packages']", "'historical_registry_packages_covered': covered, 'notice_roots': notices['root_packages']")
|
||||
r = r.replace("if __name__ == '__main__':\n", "if __name__ == '__main__':\n sys.stdout.reconfigure(encoding='utf-8')\n sys.stderr.reconfigure(encoding='utf-8')\n")
|
||||
(BASE / 'readback.py').write_text(r, encoding='utf-8', newline='\n')
|
||||
for name in ('prepare.py', 'readback.py'):
|
||||
ast.parse((BASE / name).read_text(encoding='utf-8'), filename=name)
|
||||
assert old_inventory == {p.name: sha(p) for p in OLD.iterdir() if p.is_file()}
|
||||
print(json.dumps({'static_ast': 'PASS', 'old_adapter_files_unchanged': len(old_inventory)}))
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
{
|
||||
"scope": "Static adapters only; no clone/metadata/package/application execution",
|
||||
"files": [
|
||||
{
|
||||
"path": "prepare.py",
|
||||
"bytes": 19816,
|
||||
"sha256": "c00c89181f58a9f4afbb29eea7eb7c9c8473deee280531fd2d6be2d88d49a07c"
|
||||
},
|
||||
{
|
||||
"path": "package2.ps1",
|
||||
"bytes": 5189,
|
||||
"sha256": "1bd9c4b47d03f7479dfb627e8ef845a6f5770782a1cfa58e44b0e9b28b45ac64"
|
||||
},
|
||||
{
|
||||
"path": "readback.py",
|
||||
"bytes": 6307,
|
||||
"sha256": "81bad82aed8522d5377fb671b71f4bd7ba329206beb9e96c4cd11f3a8fb343cf"
|
||||
},
|
||||
{
|
||||
"path": "README.md",
|
||||
"bytes": 4909,
|
||||
"sha256": "00fa3176fce5e4e044c8966e4d820e7f3fedb82634e5680b4d957d726e8bdcf2"
|
||||
},
|
||||
{
|
||||
"path": "VALIDATION.pending.md",
|
||||
"bytes": 1219,
|
||||
"sha256": "694a2274ed5c71c3707a90698f7c3254ef4d86233b4bac2d3730b24b970bce1d"
|
||||
},
|
||||
{
|
||||
"path": "adapt.py",
|
||||
"bytes": 8836,
|
||||
"sha256": "ae0f6eb162a27d13c7f3e2dd636bd5b67068e1b9b30a2d29b275a3112f00ab5f"
|
||||
},
|
||||
{
|
||||
"path": "static-source-readback.json",
|
||||
"bytes": 1071,
|
||||
"sha256": "171f6d080e7d95f24c8563bae5e6d6e2d8e00e7802116ce47bdac700c249c520"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
# Private adapter only. Execute after root authorizes metadata/package work.
|
||||
#Requires -Version 7.0
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$prep = $PSScriptRoot
|
||||
$repo = Split-Path -Parent (Split-Path -Parent $prep)
|
||||
$source = Join-Path $prep 'source'
|
||||
$resultPath = Join-Path $prep 'package2-result.json'
|
||||
$logPath = Join-Path $prep 'package2.log'
|
||||
$metadataPath = Join-Path $prep 'metadata2-result.json'
|
||||
foreach ($path in @($resultPath, $logPath, $metadataPath)) {
|
||||
if (Test-Path -LiteralPath $path) { throw "Refusing existing evidence: $path" }
|
||||
}
|
||||
$prepared = Get-Content -LiteralPath (Join-Path $prep 'preparation.json') -Raw | ConvertFrom-Json
|
||||
$priorResult = Get-Content -LiteralPath (Join-Path $prep 'prepare-result.json') -Raw | ConvertFrom-Json
|
||||
if (-not $priorResult.success -or $prepared.base_commit -ne '33c6d83e9ba9a5b1310b1b627d7c40affcbcf15c' -or
|
||||
$prepared.version -ne '0.2.0-preview.navigation.20260911' -or $prepared.target -ne 'x86_64-pc-windows-gnullvm') {
|
||||
throw 'Missing successful designated navigation preparation.'
|
||||
}
|
||||
$head = (& git -C $source rev-parse HEAD).Trim()
|
||||
if ($LASTEXITCODE -ne 0 -or $head -ne $prepared.reconstruction_commit) { throw 'Reconstructed source HEAD changed.' }
|
||||
$dirty = & git -C $source status --porcelain
|
||||
if ($LASTEXITCODE -ne 0 -or $dirty) { throw 'Reconstructed source is not clean.' }
|
||||
foreach ($map in @($prepared.gui_inputs, $prepared.shared_non_cargo_inputs)) {
|
||||
foreach ($property in $map.PSObject.Properties) {
|
||||
if ((Get-FileHash -LiteralPath (Join-Path $source $property.Name)).Hash.ToLowerInvariant() -cne $property.Value) {
|
||||
throw "Frozen source changed: $($property.Name)"
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach ($item in $prepared.artifacts) {
|
||||
if ((Get-FileHash -LiteralPath (Join-Path $prep $item.destination)).Hash.ToLowerInvariant() -cne $item.sha256) {
|
||||
throw "Frozen binary changed: $($item.destination)"
|
||||
}
|
||||
}
|
||||
. (Join-Path $repo '.local/toolchain/env.ps1')
|
||||
$env:CARGO_BUILD_JOBS = '1'
|
||||
$env:CARGO_NET_OFFLINE = 'true'
|
||||
# PS7 native stderr is diagnostic output; never reinterpret ordinary Cargo stderr
|
||||
# as a PowerShell terminating error. Native exit codes remain explicitly checked.
|
||||
$PSNativeCommandUseErrorActionPreference = $false
|
||||
$global:arcadiaNavigationMetadata = [pscustomobject]@{
|
||||
Executable = (Get-Command cargo -CommandType Application).Source
|
||||
Calls = 0; Source = $source; ResultPath = $metadataPath
|
||||
}
|
||||
function cargo {
|
||||
$actual = @($args)
|
||||
$expected = @('metadata', '--locked', '--offline', '--manifest-path',
|
||||
(Join-Path $global:arcadiaNavigationMetadata.Source 'Cargo.toml'),
|
||||
'--filter-platform', 'x86_64-pc-windows-gnullvm', '--format-version', '1')
|
||||
if ($global:arcadiaNavigationMetadata.Calls -ne 0 -or ($actual -join "`n") -cne ($expected -join "`n")) {
|
||||
throw 'Only one exact locked offline metadata call is admitted.'
|
||||
}
|
||||
$global:arcadiaNavigationMetadata.Calls++
|
||||
$receipt = [ordered]@{ executable = $global:arcadiaNavigationMetadata.Executable; arguments = $actual; started = [DateTime]::UtcNow.ToString('o') }
|
||||
try {
|
||||
& $global:arcadiaNavigationMetadata.Executable @actual
|
||||
$receipt.exitCode = $LASTEXITCODE
|
||||
} finally {
|
||||
$receipt.finished = [DateTime]::UtcNow.ToString('o')
|
||||
[IO.File]::WriteAllText($global:arcadiaNavigationMetadata.ResultPath, ($receipt | ConvertTo-Json -Depth 8) + "`n", [Text.UTF8Encoding]::new($false))
|
||||
}
|
||||
$global:LASTEXITCODE = $receipt.exitCode
|
||||
if ($receipt.exitCode -ne 0) { throw "Offline metadata failed: $($receipt.exitCode)" }
|
||||
}
|
||||
$result = [ordered]@{ started = [DateTime]::UtcNow.ToString('o'); version = $prepared.version; source_commit = $head; native_acceptance = 'Pending' }
|
||||
try {
|
||||
& (Join-Path $source 'scripts/package-windows.ps1') `
|
||||
-Target $prepared.target -Version $prepared.version `
|
||||
-BinaryDirectory (Join-Path $prep 'bin') `
|
||||
-LlvmMinGWRoot (Join-Path $repo '.local/toolchain/llvm-mingw-20260826-ucrt-x86_64') `
|
||||
-BuildEvidence (Join-Path $prep 'build-evidence.json') `
|
||||
-Python 'C:/Users/User/.cache/codex-runtimes/codex-primary-runtime/dependencies/python/python.exe' `
|
||||
-OutputRoot (Join-Path $prep 'packages') *>&1 | Tee-Object -FilePath $logPath
|
||||
if ($global:arcadiaNavigationMetadata.Calls -ne 1) { throw 'Expected exactly one metadata call.' }
|
||||
$archive = Join-Path $prep "packages/ArcadiaStudio-$($prepared.version)-$($prepared.target).zip"
|
||||
$result.archive = $archive
|
||||
$result.archive_sha256 = (Get-FileHash -LiteralPath $archive).Hash.ToLowerInvariant()
|
||||
$result.archive_bytes = (Get-Item -LiteralPath $archive).Length
|
||||
$result.success = $true
|
||||
} catch {
|
||||
$result.success = $false
|
||||
$result.error = $_.ToString()
|
||||
$_ | Out-String | Add-Content -LiteralPath $logPath
|
||||
Write-Output $_
|
||||
} finally {
|
||||
$result.finished = [DateTime]::UtcNow.ToString('o')
|
||||
$result.metadata_calls = $global:arcadiaNavigationMetadata.Calls
|
||||
[IO.File]::WriteAllText($resultPath, ($result | ConvertTo-Json -Depth 8) + "`n", [Text.UTF8Encoding]::new($false))
|
||||
Remove-Item Function:\cargo
|
||||
Remove-Variable arcadiaNavigationMetadata -Scope Global
|
||||
}
|
||||
if (-not $result.success) { exit 1 }
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"started": "2026-09-11T21:03:07.239702+00:00",
|
||||
"scope": "Preparation only; no application/Cargo/package execution.",
|
||||
"success": false,
|
||||
"error": "CalledProcessError(1, ['git', 'checkout-index', '--all', '--prefix=C:/Users/User/Documents/Git/ARCADIA-Studio/.local/navigation-repair-prerelease-20260911/source-roundtrip/'])",
|
||||
"finished": "2026-09-11T21:03:40.828532+00:00"
|
||||
}
|
||||
@@ -0,0 +1,316 @@
|
||||
"""Prepare isolated exact sources/binaries only after recorded GUI/full gates.
|
||||
|
||||
Do not execute without root's preparation authorization. Never invokes Cargo,
|
||||
launches applications, publishes, or writes the main repository's index/refs.
|
||||
"""
|
||||
from datetime import datetime, timezone
|
||||
import hashlib
|
||||
import json
|
||||
from pathlib import Path, PurePosixPath
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
import tarfile
|
||||
import traceback
|
||||
import sys
|
||||
import tomllib
|
||||
import zipfile
|
||||
|
||||
BASE = Path(__file__).resolve().parent
|
||||
ROOT = BASE.parents[1]
|
||||
SOURCE = BASE / 'source'
|
||||
COMMIT = '33c6d83e9ba9a5b1310b1b627d7c40affcbcf15c'
|
||||
BRANCH = 'codex/navigation-repair-prerelease-source-20260911'
|
||||
VERSION = '0.2.0-preview.navigation.20260911'
|
||||
TARGET = 'x86_64-pc-windows-gnullvm'
|
||||
GATES = ROOT / '.local/text-repair-save-20260911'
|
||||
HISTORY = ROOT / '.local/contextual-prerelease-20260911/evidence'
|
||||
INPUT_SHA = '75a282fd3d4cb2dec6763046ea45d0caa015b998281fbdd6f222d8e3a175b13f'
|
||||
ZIP_SHA = '213fdb39ea7c3601b4baac674c3ab3ecc863ac44db1b092d922d3e24f26887b7'
|
||||
SERVER_SHA = 'efeeb89c0d2e0f93df4472cb3e9f48b2b722a37ae1043c90a5a56f2e845fe665'
|
||||
DLL_SHA = 'f45604a12e8bcd839fd86f667c1be1cc7c4f9da2b3c69ff381ac708f0e1ae7d4'
|
||||
HISTORICAL = {
|
||||
'server-original-gates.json': 'b7fdfc55ed4c964d276f20f1c57c81c1e924b8da315171089740ab8335ac9f0d',
|
||||
'server-original-binary-hashes.json': '110a03e323133efb48d50ad065d41414599aa85e0ac420e7d622aa04df4d54f8',
|
||||
'server-prior-input-comparisons.json': '3c78dea4e93dae186fd24ab826c6680178a2c7849c270a22deae2ff732f1857b',
|
||||
'prior-dependency-notices.json': '98fab15c1a2bb3aa727c6d52d5c69435b15efa56b6668ed717e7a04cf86f9b52',
|
||||
'server-source-snapshot.tar.gz': 'cbb371a93a562e1e641d7629fe351e016d63e66fc9112499b3fed6d5cd171f78',
|
||||
}
|
||||
SCRIPT_HASHES = {
|
||||
'scripts/package-windows.ps1': '28997ff78565619ed9c9b185f66dc922808f1f0be17f522b430c6c415448663d',
|
||||
'scripts/package-common.ps1': 'd859453a8230b4f305d822c8742b371a71e35df698109a5d55c948f6dfaf0640',
|
||||
'scripts/windows-notices.ps1': 'a5328c05579e1b9ca09142ee99e87c12994d12764f7c69448e392883d718ef01',
|
||||
}
|
||||
|
||||
COMMANDS = {
|
||||
'desktop-tests': ['cargo', 'test', '--locked', '--offline', '-p', 'oa-desktop', '--bin', 'arcadia-studio', '--', '--test-threads=1'],
|
||||
'desktop-clippy': ['cargo', 'clippy', '--locked', '--offline', '-p', 'oa-desktop', '--all-targets', '--', '-D', 'warnings'],
|
||||
'scoped-fmt': ['rustfmt', '--check', '--edition', '2024', '--config', 'skip_children=true', 'crates/oa-desktop/src/app.rs', 'crates/oa-desktop/src/document.rs', 'crates/oa-desktop/src/app/repair_save_state_tests.rs'],
|
||||
'diff-check': ['git', 'diff', '--check'],
|
||||
'gui-build': ['cargo', 'build', '--locked', '--offline', '-p', 'oa-desktop', '--bin', 'arcadia-studio', '--message-format=json-render-diagnostics'],
|
||||
}
|
||||
|
||||
|
||||
def now():
|
||||
return datetime.now(timezone.utc).isoformat()
|
||||
|
||||
|
||||
def ordinary(path):
|
||||
path = Path(path).absolute()
|
||||
for part in (path, *path.parents):
|
||||
if part.exists():
|
||||
assert not part.is_symlink() and not (getattr(part.lstat(), 'st_file_attributes', 0) & 0x400), str(part)
|
||||
return path
|
||||
|
||||
|
||||
def sha(path):
|
||||
with ordinary(path).open('rb') as stream:
|
||||
return hashlib.file_digest(stream, 'sha256').hexdigest()
|
||||
|
||||
|
||||
def load(path):
|
||||
return json.loads(ordinary(path).read_text(encoding='utf-8-sig'))
|
||||
|
||||
|
||||
def record(path, value):
|
||||
ordinary(path)
|
||||
with Path(path).open('x', encoding='utf-8', newline='\n') as stream:
|
||||
json.dump(value, stream, ensure_ascii=False, indent=2)
|
||||
stream.write('\n')
|
||||
|
||||
|
||||
def git(cwd, *args):
|
||||
return subprocess.run(['git', *args], cwd=cwd, check=True, capture_output=True).stdout
|
||||
|
||||
|
||||
def relative(name):
|
||||
p = PurePosixPath(name)
|
||||
assert name == p.as_posix() and not p.is_absolute() and p.parts and all(x not in ('..', '.') for x in p.parts)
|
||||
assert '\\' not in name and ':' not in name and '\n' not in name and '\r' not in name
|
||||
return name
|
||||
|
||||
|
||||
def blobs(cwd, revision, paths):
|
||||
paths = list(paths)
|
||||
raw = subprocess.run(['git', 'cat-file', '--batch'], cwd=cwd, check=True, capture_output=True,
|
||||
input=''.join(f'{revision}:{p}\n' for p in paths).encode()).stdout
|
||||
cursor, out = 0, {}
|
||||
for path in paths:
|
||||
end = raw.index(b'\n', cursor)
|
||||
header = raw[cursor:end].split()
|
||||
assert len(header) == 3 and header[1] == b'blob', path
|
||||
size = int(header[2])
|
||||
out[path] = raw[end + 1:end + 1 + size]
|
||||
cursor = end + size + 2
|
||||
assert cursor == len(raw)
|
||||
return out
|
||||
|
||||
|
||||
def root_identity():
|
||||
index = Path(git(ROOT, 'rev-parse', '--git-path', 'index').decode().strip())
|
||||
if not index.is_absolute():
|
||||
index = ROOT / index
|
||||
return {'head': git(ROOT, 'rev-parse', 'HEAD').decode().strip(), 'index_sha256': sha(index),
|
||||
'tracked_status': git(ROOT, 'status', '--porcelain', '-uno').decode()}
|
||||
|
||||
|
||||
def historical_inputs():
|
||||
for name, checksum in HISTORICAL.items():
|
||||
assert sha(HISTORY / name) == checksum, name
|
||||
inventory = load(HISTORY / 'server-prior-input-comparisons.json')
|
||||
entries = inventory['server_source_file_comparisons']
|
||||
expected = {relative(e['path']): e['recorded_sha256'] for e in entries}
|
||||
assert len(expected) == len(entries) and expected
|
||||
found = {}
|
||||
with tarfile.open(HISTORY / 'server-source-snapshot.tar.gz', 'r:gz') as archive:
|
||||
for member in archive.getmembers():
|
||||
name = member.name.removeprefix('./')
|
||||
if name in expected:
|
||||
assert member.isfile() and name not in found, name
|
||||
with archive.extractfile(member) as stream:
|
||||
found[name] = hashlib.file_digest(stream, 'sha256').hexdigest()
|
||||
assert found == expected
|
||||
assert any(e['Hash'].lower() == SERVER_SHA and Path(e['Path']).name == 'arcadia-server.exe'
|
||||
for e in load(HISTORY / 'server-original-binary-hashes.json'))
|
||||
return expected
|
||||
|
||||
|
||||
def admission():
|
||||
assert sha(GATES / 'inputs1.json') == INPUT_SHA
|
||||
assert sha(GATES / 'inputs1.zip') == ZIP_SHA
|
||||
manifest = load(GATES / 'inputs1.json')
|
||||
assert manifest['archiveSha256'] == ZIP_SHA
|
||||
expected = {relative(e['path']): e for e in manifest['files']}
|
||||
assert len(expected) == len(manifest['files']) and expected
|
||||
assert len({p.casefold() for p in expected}) == len(expected)
|
||||
with zipfile.ZipFile(GATES / 'inputs1.zip') as archive:
|
||||
assert len(archive.namelist()) == len(expected) and set(archive.namelist()) == set(expected)
|
||||
frozen = {name: archive.read(name) for name in expected}
|
||||
for name, raw in frozen.items():
|
||||
assert len(raw) == expected[name]['bytes'] and hashlib.sha256(raw).hexdigest() == expected[name]['sha256'], name
|
||||
assert len(expected) == 500
|
||||
complete = load(GATES / 'complete.json')
|
||||
assert complete['archiveSha256'] == ZIP_SHA and complete['inputsUnchanged'] is True
|
||||
assert complete['gates'] == list(COMMANDS) and complete['sourceHead'] == manifest['head']
|
||||
# The gates captured eeeaa126 plus the repair delta; COMMIT commits those
|
||||
# exact raw inputs. Never relabel the captured sourceHead as a clean build.
|
||||
assert manifest['head'] == 'eeeaa1262748800c4c9aaa1eca79a7de96757358'
|
||||
for name, command in COMMANDS.items():
|
||||
receipt = load(GATES / (name + '-result.json'))
|
||||
assert receipt['exitCode'] == 0 and receipt['inputsUnchanged'] is True and receipt['command'] == command
|
||||
assert sha(GATES / (name + '-stdout.log')) == receipt['stdoutSha256']
|
||||
assert sha(GATES / (name + '-stderr.log')) == receipt['stderrSha256']
|
||||
build = load(GATES / 'gui-build-result.json')['binary']
|
||||
artifact = build['artifact']
|
||||
assert artifact['target']['name'] == 'arcadia-studio' and artifact['profile']['test'] is False
|
||||
assert artifact['profile']['opt_level'] == '0' and artifact['profile']['debug_assertions'] is True
|
||||
assert Path(artifact['executable']).resolve() == Path(build['path']).resolve()
|
||||
assert Path(build['path']).resolve().is_relative_to(ROOT / '.local/target-gnullvm')
|
||||
gui = GATES / 'gui-repair.exe'
|
||||
assert sha(gui) == build['sha256'] == 'b183ef31f16b3986538b3f2aba8435eab45978635c47f99ca01be6e72b168848'
|
||||
assert gui.stat().st_size == build['bytes'] == 113206272
|
||||
summaries = re.findall(r'test result: ok\. (\d+) passed; (\d+) failed; (\d+) ignored; (\d+) measured; (\d+) filtered out;',
|
||||
(GATES / 'desktop-tests-stdout.log').read_text(encoding='utf-8'))
|
||||
assert summaries == [('755', '0', '1', '0', '0')]
|
||||
counts = dict(zip(('passed', 'failed', 'ignored', 'measured', 'filtered'), map(int, summaries[0])))
|
||||
server = ROOT / '.local/contextual-prerelease-20260911/bin/arcadia-server.exe'
|
||||
dll = ROOT / '.local/contextual-prerelease-20260911/bin/libunwind.dll'
|
||||
assert sha(server) == SERVER_SHA and sha(dll) == DLL_SHA
|
||||
old = historical_inputs()
|
||||
return manifest, frozen, build, gui, counts, server, dll, old
|
||||
|
||||
|
||||
def main():
|
||||
ordinary(BASE)
|
||||
for name in ('source', 'source-roundtrip', 'bin', 'evidence', 'preparation.json', 'committed-roundtrip.json', 'build-evidence.json', 'evidence-manifest.json'):
|
||||
assert not (BASE / name).exists(), f'Refusing existing output: {name}'
|
||||
before = root_identity()
|
||||
assert not before['tracked_status'], 'Root tracked edits must be frozen before preparation.'
|
||||
assert git(ROOT, 'rev-parse', COMMIT).decode().strip() == COMMIT
|
||||
# Later root commits may record evidence only. Do not silently package later
|
||||
# production/docs/dependency or instruction changes under the tested source.
|
||||
git(ROOT, 'merge-base', '--is-ancestor', COMMIT, before['head'])
|
||||
later = git(ROOT, 'diff', '--name-only', '-z', COMMIT, before['head']).decode().split('\0')[:-1]
|
||||
assert all(p.startswith('harness/') and not p.startswith('harness/fixtures/') for p in later), later
|
||||
manifest, frozen, build, gui, counts, server, dll, old = admission()
|
||||
for path, raw in frozen.items():
|
||||
assert ordinary(ROOT / path).read_bytes() == raw, f'Current frozen input changed: {path}'
|
||||
selectors = ['crates', '.cargo', 'Cargo.toml', 'Cargo.lock', 'docs', 'examples', 'harness/fixtures']
|
||||
current_paths = git(ROOT, 'ls-files', '-z', '--cached', '--others', '--exclude-standard', '--', *selectors).decode().split('\0')[:-1]
|
||||
assert sorted(current_paths) == sorted(frozen), 'Current input file set differs from the successful freeze.'
|
||||
committed_paths = git(ROOT, 'ls-tree', '-r', '--name-only', '-z', COMMIT, '--', *selectors).decode().split('\0')[:-1]
|
||||
assert sorted(committed_paths) == sorted(frozen), 'Committed input file set differs from the successful gate freeze.'
|
||||
assert shutil.disk_usage(BASE).free >= 2 * 1024 ** 3
|
||||
original = blobs(ROOT, COMMIT, frozen)
|
||||
for path, raw in frozen.items():
|
||||
assert original[path].replace(b'\r\n', b'\n') == raw.replace(b'\r\n', b'\n'), f'Non-newline source mismatch: {path}'
|
||||
notice = load(HISTORY / 'prior-dependency-notices.json')
|
||||
shared = {p: h for p, h in notice['workspace_build_inputs_sha256'].items() if p not in ('Cargo.toml', 'Cargo.lock')}
|
||||
for p, raw in blobs(ROOT, COMMIT, shared).items():
|
||||
assert hashlib.sha256(raw).hexdigest() == shared[p] or raw.replace(b'\r\n', b'\n') == (ROOT / p).read_bytes().replace(b'\r\n', b'\n')
|
||||
assert sha(ROOT / p) == shared[p], p
|
||||
if p in frozen:
|
||||
assert frozen[p] == (ROOT / p).read_bytes(), p
|
||||
frozen[p] = (ROOT / p).read_bytes()
|
||||
registry = {(x['name'], x['version'], x['source']): x['registry_checksum']
|
||||
for x in notice['packages'] if x['source']}
|
||||
locked = {(x['name'], x['version'], x.get('source')): x.get('checksum')
|
||||
for x in tomllib.loads(frozen['Cargo.lock'].decode())['package']}
|
||||
assert registry and all(locked.get(k) == v for k, v in registry.items()), 'Historical registry coverage differs.'
|
||||
# Local clone has its own index/refs and does not create a worktree in ROOT.
|
||||
git(ROOT, 'clone', '--no-hardlinks', '--no-checkout', str(ROOT), str(SOURCE))
|
||||
git(SOURCE, 'checkout', '-b', BRANCH, COMMIT)
|
||||
checks = {p: hashlib.sha256(raw).hexdigest() for p, raw in frozen.items()}
|
||||
original = blobs(SOURCE, COMMIT, checks)
|
||||
restores = []
|
||||
for p, raw in frozen.items():
|
||||
assert original[p].replace(b'\r\n', b'\n') == raw.replace(b'\r\n', b'\n'), p
|
||||
if hashlib.sha256(original[p]).hexdigest() != checks[p] or sha(SOURCE / p) != checks[p]:
|
||||
assert re.fullmatch(r'[A-Za-z0-9_./-]+', p), 'Attribute path needs explicit quoting review.'
|
||||
ordinary(SOURCE / p).write_bytes(raw)
|
||||
restores.append({'path': p, 'sha256': checks[p], 'base_git_sha256': hashlib.sha256(original[p]).hexdigest(),
|
||||
'bytes': len(raw), 'crlf': raw.count(b'\r\n'), 'lf': raw.count(b'\n')})
|
||||
if restores:
|
||||
with (SOURCE / '.gitattributes').open('ab') as out:
|
||||
out.write(b'\n# Exact frozen navigation GUI input bytes; historical server source is separate.\n')
|
||||
for item in restores:
|
||||
out.write((item['path'] + ' -text\n').encode())
|
||||
git(SOURCE, 'add', '--renormalize', '--', '.gitattributes', *[x['path'] for x in restores])
|
||||
git(SOURCE, '-c', 'user.name=Codex-9e8r6t', '-c', 'user.email=codex-9e8r6t@local.invalid',
|
||||
'commit', '-m', 'Preserve exact navigation prerelease GUI inputs')
|
||||
reconstruction = git(SOURCE, 'rev-parse', 'HEAD').decode().strip()
|
||||
committed = blobs(SOURCE, 'HEAD', checks)
|
||||
fresh = BASE / 'source-roundtrip'
|
||||
fresh.mkdir()
|
||||
git(SOURCE, 'checkout-index', '--all', '--prefix=' + fresh.as_posix() + '/')
|
||||
for p, checksum in checks.items():
|
||||
assert hashlib.sha256(committed[p]).hexdigest() == sha(SOURCE / p) == sha(fresh / p) == checksum, p
|
||||
for p, checksum in SCRIPT_HASHES.items():
|
||||
assert sha(SOURCE / p) == checksum, p
|
||||
assert not git(SOURCE, 'status', '--porcelain').strip()
|
||||
comparison = {'historical_file_count': len(old), 'changed_current_inputs': [p for p, h in old.items() if sha(SOURCE / p) != h],
|
||||
'added_current_files': sorted(p.relative_to(SOURCE).as_posix() for crate in ('oa-core', 'oa-server')
|
||||
for p in (SOURCE / 'crates' / crate).rglob('*') if p.is_file() and p.relative_to(SOURCE).as_posix() not in old),
|
||||
'scope': 'Historical server inputs verified against their own preserved tar, not required to equal the new GUI source.'}
|
||||
assert comparison['changed_current_inputs'] == ['crates/oa-core/src/language.rs']
|
||||
assert comparison['added_current_files'] == ['crates/oa-core/src/language_outline_tests.rs']
|
||||
(BASE / 'bin').mkdir()
|
||||
artifacts = []
|
||||
for source, name, checksum in ((gui, 'arcadia-studio.exe', build['sha256']), (server, 'arcadia-server.exe', SERVER_SHA), (dll, 'libunwind.dll', DLL_SHA)):
|
||||
assert sha(source) == checksum
|
||||
target = BASE / 'bin' / name
|
||||
with source.open('rb') as src, target.open('xb') as dst:
|
||||
shutil.copyfileobj(src, dst)
|
||||
assert sha(source) == sha(target) == checksum
|
||||
artifacts.append({'source': str(source), 'destination': 'bin/' + name, 'sha256': checksum, 'bytes': target.stat().st_size})
|
||||
provenance = {name: HISTORY / name for name in HISTORICAL}
|
||||
provenance.update({'gui-inputs1.json': GATES / 'inputs1.json', 'gui-complete.json': GATES / 'complete.json',
|
||||
'gui-gate-run.py': GATES / 'run.py'})
|
||||
for name in COMMANDS:
|
||||
for suffix in ('-result.json', '-stdout.log', '-stderr.log'):
|
||||
provenance['gui-' + name + suffix] = GATES / (name + suffix)
|
||||
(BASE / 'evidence').mkdir()
|
||||
evidence = []
|
||||
for name, source in provenance.items():
|
||||
checksum = sha(source)
|
||||
destination = BASE / 'evidence' / name
|
||||
with source.open('rb') as src, destination.open('xb') as dst:
|
||||
shutil.copyfileobj(src, dst)
|
||||
assert sha(source) == sha(destination) == checksum
|
||||
evidence.append({'source': str(source), 'destination': 'evidence/' + name, 'sha256': checksum, 'bytes': destination.stat().st_size})
|
||||
record(BASE / 'evidence-manifest.json', evidence)
|
||||
record(BASE / 'committed-roundtrip.json', {'base_commit': COMMIT, 'reconstruction_commit': reconstruction, 'checks': checks, 'clean': True})
|
||||
record(BASE / 'build-evidence.json', {
|
||||
'source_identity': f'GUI exact-byte reconstruction {reconstruction} from {COMMIT}; {len(manifest["files"])} frozen GUI gate inputs plus separately verified shared build resources. This is not the original build commit. EFEE server retains its separate September 7 source snapshot; its old language.rs differs from current GUI core. No current-source server claim.',
|
||||
'profile': f'Prebuilt Windows development GUI, actual full desktop result {counts}. Retained EFEE server is the historical development artifact, not rebuilt. Native/package acceptance remains pending.',
|
||||
'compiler': 'Original GUI Cargo artifact/profile and server build records retained separately; package collector records current compiler/sysroot facts. No reproducible build or new server build claimed.',
|
||||
'target': TARGET, 'binary_sha256': {'arcadia-studio.exe': build['sha256'], 'arcadia-server.exe': SERVER_SHA},
|
||||
'provenance_reference_base': 'evidence/ references are external preparation records, not files inside the ZIP.',
|
||||
'per_binary_provenance': {
|
||||
'arcadia-studio.exe': {'build': 'evidence/gui-gui-build-result.json', 'inputs': 'evidence/gui-inputs1.json', 'full_gate': 'evidence/gui-desktop-tests-result.json', 'complete': 'evidence/gui-complete.json', 'captured_head': manifest['head'], 'scope': 'Built from captured eeeaa126 plus exact repair input delta; those bytes are committed at base_commit, then reconstructed without changing code.'},
|
||||
'arcadia-server.exe': {'build': 'evidence/server-original-gates.json', 'hashes': 'evidence/server-original-binary-hashes.json', 'source_tar': 'evidence/server-source-snapshot.tar.gz', 'source_inventory': 'evidence/server-prior-input-comparisons.json', 'notices': 'evidence/prior-dependency-notices.json', 'scope': 'Unchanged historical EFEE; separate source, not rebuilt from navigation source.'}}})
|
||||
after = root_identity()
|
||||
assert after == before, 'Root HEAD/index/tracked state changed during preparation; inspect before proceeding.'
|
||||
record(BASE / 'preparation.json', {'version': VERSION, 'target': TARGET, 'base_commit': COMMIT, 'reconstruction_commit': reconstruction,
|
||||
'branch': BRANCH, 'record_only_root_paths_after_base': later, 'historical_registry_count': len(registry), 'source_repository': str(SOURCE), 'restorations': restores, 'gui_inputs': {e['path']: e['sha256'] for e in manifest['files']},
|
||||
'shared_non_cargo_inputs': shared, 'historical_server_inputs': old, 'historical_server_comparison': comparison,
|
||||
'artifacts': artifacts, 'full_test_counts': counts, 'root_before': before, 'root_after': after, 'prepared': now(),
|
||||
'scope': 'Isolated preparation only; package integrity and native acceptance pending.'})
|
||||
print(json.dumps({'success': True, 'reconstruction': reconstruction, 'gui_inputs': len(manifest['files']), 'restorations': len(restores), 'historical_server_inputs': len(old)}))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.stdout.reconfigure(encoding='utf-8')
|
||||
sys.stderr.reconfigure(encoding='utf-8')
|
||||
assert not (BASE / 'prepare-result.json').exists()
|
||||
status = {'started': now(), 'scope': 'Preparation only; no application/Cargo/package execution.'}
|
||||
try:
|
||||
main()
|
||||
status['success'] = True
|
||||
except BaseException as error:
|
||||
status.update(success=False, error=repr(error))
|
||||
traceback.print_exc()
|
||||
raise
|
||||
finally:
|
||||
status['finished'] = now()
|
||||
record(BASE / 'prepare-result.json', status)
|
||||
@@ -0,0 +1,105 @@
|
||||
"""Read back the produced ZIP/stage/source without launching either executable."""
|
||||
from datetime import datetime, timezone
|
||||
import hashlib
|
||||
import json
|
||||
from pathlib import Path
|
||||
import stat
|
||||
import sys
|
||||
import zipfile
|
||||
from prepare import BASE, SOURCE, VERSION, TARGET, COMMIT, SERVER_SHA, DLL_SHA, ordinary, sha, load, record, git, relative, historical_inputs, HISTORY
|
||||
|
||||
|
||||
def main():
|
||||
assert not (BASE / 'package-readback.json').exists()
|
||||
preparation = load(BASE / 'preparation.json')
|
||||
terminal = load(BASE / 'package2-result.json')
|
||||
assert terminal['success'] is True and terminal['metadata_calls'] == 1
|
||||
assert preparation['base_commit'] == COMMIT and preparation['version'] == VERSION and preparation['target'] == TARGET
|
||||
name = f'ArcadiaStudio-{VERSION}-{TARGET}'
|
||||
archive_path = BASE / 'packages' / (name + '.zip')
|
||||
stage = BASE / 'packages' / name
|
||||
assert Path(terminal['archive']).resolve() == archive_path.resolve()
|
||||
assert terminal['archive_sha256'] == sha(archive_path) and terminal['archive_bytes'] == archive_path.stat().st_size
|
||||
actual = {}
|
||||
with zipfile.ZipFile(archive_path) as archive:
|
||||
entries = archive.infolist()
|
||||
assert entries and len({e.filename.casefold() for e in entries}) == len(entries)
|
||||
for entry in entries:
|
||||
assert entry.filename.startswith(name + '/') and not entry.is_dir()
|
||||
path = relative(entry.filename[len(name) + 1:])
|
||||
assert not (entry.flag_bits & 1) and not stat.S_ISLNK(entry.external_attr >> 16)
|
||||
assert entry.date_time == (2000, 1, 1, 0, 0, 0)
|
||||
with archive.open(entry) as stream:
|
||||
checksum = hashlib.file_digest(stream, 'sha256').hexdigest()
|
||||
assert sha(stage / path) == checksum and (stage / path).stat().st_size == entry.file_size
|
||||
actual[path] = {'sha256': checksum, 'bytes': entry.file_size}
|
||||
files = {ordinary(p).relative_to(stage).as_posix() for p in stage.rglob('*') if p.is_file()}
|
||||
assert set(actual) == files
|
||||
sums = {}
|
||||
for line in (stage / 'SHA256SUMS.txt').read_text(encoding='utf-8').splitlines():
|
||||
checksum, path = line.split(' ', 1)
|
||||
assert relative(path) not in sums
|
||||
sums[path] = checksum
|
||||
assert set(sums) == set(actual) - {'SHA256SUMS.txt'}
|
||||
assert all(actual[p]['sha256'] == h for p, h in sums.items())
|
||||
companion = (archive_path.with_suffix('.zip.sha256')).read_text(encoding='utf-8').strip().split()
|
||||
assert companion == [terminal['archive_sha256'], archive_path.name]
|
||||
for item in preparation['artifacts']:
|
||||
p = Path(item['destination']).name
|
||||
assert actual[p]['sha256'] == sha(BASE / item['destination']) == item['sha256']
|
||||
assert actual['arcadia-server.exe']['sha256'] == SERVER_SHA and actual['libunwind.dll']['sha256'] == DLL_SHA
|
||||
assert git(SOURCE, 'rev-parse', 'HEAD').decode().strip() == preparation['reconstruction_commit']
|
||||
assert not git(SOURCE, 'status', '--porcelain').strip()
|
||||
for mapping in (preparation['gui_inputs'], preparation['shared_non_cargo_inputs']):
|
||||
for path, checksum in mapping.items():
|
||||
assert sha(SOURCE / path) == checksum
|
||||
# This comparison deliberately uses the old server tar, never GUI source.
|
||||
assert historical_inputs() == preparation['historical_server_inputs']
|
||||
for item in load(BASE / 'evidence-manifest.json'):
|
||||
assert sha(BASE / item['destination']) == item['sha256'] and (BASE / item['destination']).stat().st_size == item['bytes']
|
||||
package = load(stage / 'package.json')
|
||||
assert package['version'] == VERSION and package['target'] == TARGET
|
||||
assert package['build_evidence']['evidence'] == load(BASE / 'build-evidence.json')
|
||||
for path, checksum in package['input_sha256'].items():
|
||||
assert actual[path]['sha256'] == checksum
|
||||
notices = load(stage / 'dependency-notices.json')
|
||||
previous = load(HISTORY / 'prior-dependency-notices.json')
|
||||
def registry_key(item):
|
||||
return item['name'], item['version'], item['source'], item['registry_checksum']
|
||||
collected = {registry_key(x): x for x in notices['packages'] if x['source']}
|
||||
covered = 0
|
||||
for old in previous['packages']:
|
||||
if not old['source']:
|
||||
continue
|
||||
current = collected[registry_key(old)]
|
||||
assert old['license'] == current['license']
|
||||
assert set(old['features']).issubset(current['features'])
|
||||
# Exact recorded notice files must remain in the actual ZIP. Current
|
||||
# metadata does not substitute for the historical server's provenance.
|
||||
new_notices = {(n['path'], n['sha256']) for n in current['notices']}
|
||||
for item in old['notices']:
|
||||
assert (item['path'], item['sha256']) in new_notices
|
||||
assert actual[item['path']]['sha256'] == item['sha256']
|
||||
assert old['embedded_fonts'] == current['embedded_fonts']
|
||||
for font in old['embedded_fonts']:
|
||||
for item in font['notices']:
|
||||
assert actual[item['path']]['sha256'] == item['sha256']
|
||||
covered += 1
|
||||
assert covered == preparation['historical_registry_count']
|
||||
docs = load(stage / 'documentation-check.json')
|
||||
result = {'atUtc': datetime.now(timezone.utc).isoformat(), 'status': 'PASS', 'archive_sha256': sha(archive_path),
|
||||
'archive_bytes': archive_path.stat().st_size, 'file_count': len(actual), 'files': actual,
|
||||
'manifest_entries': len(sums), 'source_commit': preparation['reconstruction_commit'],
|
||||
'gui_inputs_verified': len(preparation['gui_inputs']), 'historical_server_inputs_verified': len(preparation['historical_server_inputs']),
|
||||
'historical_server_comparison': preparation['historical_server_comparison'],
|
||||
'historical_registry_packages_covered': covered, 'notice_roots': notices['root_packages'], 'notice_package_count': len(notices['packages']),
|
||||
'documentation_check': docs, 'binary_imports': package['imports'],
|
||||
'scope': 'Producer packaging readback only; no application launch or independent/native acceptance.'}
|
||||
record(BASE / 'package-readback.json', result)
|
||||
print(json.dumps({k: v for k, v in result.items() if k != 'files'}, ensure_ascii=False, indent=2))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.stdout.reconfigure(encoding='utf-8')
|
||||
sys.stderr.reconfigure(encoding='utf-8')
|
||||
main()
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"scope": "Read-only source measurement; adapters not executed",
|
||||
"input_files": 500,
|
||||
"input_bytes": 9482855,
|
||||
"newline_only_git_differences": [
|
||||
"crates/oa-desktop/src/behavior/creation.rs",
|
||||
"crates/oa-desktop/src/behavior/scenario/workspace.rs",
|
||||
"crates/oa-desktop/src/behavior/topology/contextual.rs",
|
||||
"crates/oa-desktop/src/guide.rs",
|
||||
"crates/oa-desktop/src/semantic_review/artifacts_view.rs",
|
||||
"crates/oa-desktop/src/semantic_review/attachments_view.rs",
|
||||
"crates/oa-desktop/src/semantic_review/keyed.rs",
|
||||
"crates/oa-desktop/src/semantic_review/keyed_rows.rs",
|
||||
"crates/oa-desktop/src/semantic_review/keyed_tests.rs",
|
||||
"crates/oa-desktop/src/semantic_review/ordered.rs",
|
||||
"docs/api.html",
|
||||
"docs/api.rst",
|
||||
"docs/language.html",
|
||||
"docs/openapi.json",
|
||||
"harness/fixtures/rest-coverage/coverage-probe.json"
|
||||
],
|
||||
"source_commit": "33c6d83e9ba9a5b1310b1b627d7c40affcbcf15c",
|
||||
"zip_sha256": "213fdb39ea7c3601b4baac674c3ab3ecc863ac44db1b092d922d3e24f26887b7",
|
||||
"outputs_absent": true
|
||||
}
|
||||
Reference in New Issue
Block a user