feat(analysis): make resource budgets recoverable across CLI and MCP (#9706)

* refactor(analysis): centralize resource budgets and recovery facts

* feat(analysis): expose ASAR and Ghidra runtime admission controls

* feat(artifacts): expose reader admission and preserve limited recognition

* feat(javascript): expose recoverable semantic precision budgets

* feat(web): add recoverable source-map budgets and compact retained views

* feat(web): make historical capture decoding budgets recoverable

* fix(desktop): enforce selected view policies across capture and export

* feat(runtime): make Frida and LLDB retention budgets selectable

* feat(native): expose diagnostic budgets and preserve exhaustion causes

* feat(managed): expose recoverable snapshot and representation budgets

* feat(metadata): make Go and Rust inspection budgets recoverable

* feat(wasm): expose representation and producer admissions

* feat(evm): expose per-call worker resource budgets

* feat(android): make resource and string-pool omissions recoverable

* feat(producers): expose command and recorded-capture budgets

* docs(analysis): record recovery contracts and integrated validation

* fix(docs): link calibration oracle from published verification pages

* fix(docs): restore desktop task anchors and receipt links

* test(analysis): align fixtures with selectable budget contracts

* fix(analysis): remove source-location projector dependency cycle

* test(analysis): refresh budget recovery and verifier regressions

* test(analysis): preserve format and publication recovery expectations

* test(windows): align conformance with owned runtime admission

* fix(desktop): preserve selected alias kinds in exported state

* test(analysis): align provider fixtures with recoverable budgets

* test(desktop): validate emitted alias metadata structurally

* fix(ghidra): classify bounded Windows endpoint refusals

* test(cli): assert selectable artifact admission recovery

* fix(cli): preserve capture provenance in environment builds
This commit is contained in:
morluto authored and GitHub committed 2026-11-10 05:11:20 -05:00
1 parent 55b6d2ca20
commit cb4e5cfbff
515 files changed
+21066 -3637

No files matched your search

@@ -0,0 +1,59 @@
"""Aggregate retained observer receipts and complete CLI records; needs tiktoken."""
import argparse
import json
from pathlib import Path
import statistics
import tiktoken
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('inputs', type=Path)
parser.add_argument('output', type=Path)
options = parser.parse_args()
encoder = tiktoken.get_encoding('cl100k_base')
def compact(value):
return json.dumps(value, ensure_ascii=False, separators=(',', ':'))
rows = []
for path in sorted(options.inputs.glob('*.metrics.json')):
metrics = json.loads(path.read_text())
record = json.loads(path.with_name(path.name.replace('.metrics.json', '.replies.json')).read_text())
summary = json.loads(path.with_suffix('.stdout').read_text())
tokens = {
'arguments': len(encoder.encode(compact(record['argv']))),
'reply': len(encoder.encode(compact(json.loads(record['stdout'])))),
'progress_stderr': len(encoder.encode(record['stderr'])),
}
rows.append({
'name': path.stem.removesuffix('.metrics'),
'task': summary['case'], 'budget': summary['budget'],
'verified_facts': summary['verified_facts'], 'unknowns': summary['unknowns'],
'wall_s': metrics['elapsed_seconds'],
'cpu_s': metrics['cpu_seconds_waited_subtree'],
'host_group_rss_bytes': metrics['peak_process_group_rss_bytes_sampled'],
'tokens': tokens, 'reply_bytes': summary['reply_bytes'],
'exit_code': metrics['exit_code'], 'cleanup': metrics['cleanup'],
'stop_reason': metrics['stop_reason'],
})
medians = []
for task in ['theme', 'ms']:
for budget in ['default', 'double']:
selected = [row for row in rows if row['task'] == task and row['budget'] == budget]
if not selected:
continue
medians.append({
'task': task, 'budget': budget, 'trials': len(selected),
'facts': selected[0]['verified_facts'], 'unknowns': selected[0]['unknowns'],
'wall_s': statistics.median(row['wall_s'] for row in selected),
'cpu_s': statistics.median(row['cpu_s'] for row in selected),
'host_group_rss_mib': statistics.median(row['host_group_rss_bytes'] / 1048576 for row in selected),
'tokens': statistics.median(sum(row['tokens'].values()) for row in selected),
'reply_bytes': selected[0]['reply_bytes'],
})
options.output.write_text(json.dumps({'rows': rows, 'medians': medians}, indent=2) + '\n')
if __name__ == '__main__':
main()
@@ -0,0 +1,16 @@
{
"theme": {
"url": "https://demo.actualbudget.org/static/js/theme.EvD-JhP6.chunk.js.map",
"map_sha256": "ceef8194eaca339e55f35cf2decf7ceffa121c7744ee1fffd976a07d39477b32",
"reported_source": "../../../../component-library/src/theme.ts",
"source_sha256": "05307dc6f927705a8c76e43579c910bf6415ce28b375eea19251dc51dd2b1726"
},
"ms": {
"version": "2.1.3",
"source": "locked installed node_modules/ms",
"files": {
"index.js": "e5f0b6a946a9b2b356a28557728410717df54ea2f599edb619f9839df6b7b0e9",
"package.json": "1a6b4d9739790c0b94ab96c8cc0507e281c164c311ff4fbf5e57fb8d26290b40"
}
}
}
@@ -0,0 +1,323 @@
{
"method": "tiktoken0.12.0 cl100k_base compactUnicodeJSON argv and full parsedstdoutEvidence plusliteralprogressstderr; no MCPcatalog/chat/modelbilling",
"runtime_hashes": {
"scripts/rea.mjs": "11120fdc741c2eeca9ce903de5fd18cd1c7c3fffa15ae2beccca750f161d9ee3",
"dist/cli.js": "acd23e0f186188c99fb905cba36f81e11f92d7f280acd684a9ee93ccc75eb9ce",
"dist/domain/javascript/javascriptSemanticResourceLimits.js": "567ecef0cdb593c75ac156075a43c0e3fc8fb6b5ade3991614f58feb256d075c",
"dist/domain/javascript/javascriptSemanticPrecision.js": "56cec74b6fbe0c2dd6e4fb058811f35635d63ae3235f74d2b408e1b7c6dcc366",
"dist/domain/javascript/javascriptSemanticAnalysis.js": "c21da46e866ce49f69a45e0b5ab923fcb8eb6746a50e8f6c8b77ee61d0ca0bbd",
"dist/javascript/analysis/JavaScriptAnalysisWorker.js": "132f199041291d70ae304474a86143fb4bba3e8df4d0114607bce0f5c3f2fe61",
"dist/cli-runtime/cli.js": "b24d2408a52483a8e9b93435110c243deb53bb7ef94757c802a0c6246d8df20c",
"dist/cli-runtime/cliBuiltinArguments.js": "7b0c81902f568cdd7f8003b7baadcb4f493828b1e472ee613940168213e15090",
"./dist/composition/javascriptAnalysisWorkerProcess.js": "ab927845ccfe917549198b5438d4d206c3ecfb087b0e79e431519bbf4ef1f9fd"
},
"rows": [
{
"name": "r1-ms-default",
"task": "ms",
"budget": "default",
"verified_facts": 1,
"unknowns": 133,
"wall_s": 0.9129875000071479,
"cpu_s": 1.239214,
"host_group_rss_bytes": 344621056,
"tokens": {
"arguments": 67,
"reply": 233873,
"progress_stderr": 673
},
"reply_bytes": 624004,
"exit_code": 0,
"cleanup": "group_empty",
"stop_reason": null
},
{
"name": "r1-ms-double",
"task": "ms",
"budget": "double",
"verified_facts": 1,
"unknowns": 133,
"wall_s": 0.9050752910115989,
"cpu_s": 1.2302469999999999,
"host_group_rss_bytes": 348717056,
"tokens": {
"arguments": 81,
"reply": 233914,
"progress_stderr": 673
},
"reply_bytes": 624244,
"exit_code": 0,
"cleanup": "group_empty",
"stop_reason": null
},
{
"name": "r1-theme-default",
"task": "theme",
"budget": "default",
"verified_facts": 237,
"unknowns": 0,
"wall_s": 0.9374774580064695,
"cpu_s": 1.221098,
"host_group_rss_bytes": 346652672,
"tokens": {
"arguments": 67,
"reply": 258656,
"progress_stderr": 673
},
"reply_bytes": 733950,
"exit_code": 0,
"cleanup": "group_empty",
"stop_reason": null
},
{
"name": "r1-theme-double",
"task": "theme",
"budget": "double",
"verified_facts": 237,
"unknowns": 0,
"wall_s": 0.9283738329977496,
"cpu_s": 1.236008,
"host_group_rss_bytes": 346423296,
"tokens": {
"arguments": 81,
"reply": 258721,
"progress_stderr": 673
},
"reply_bytes": 734190,
"exit_code": 0,
"cleanup": "group_empty",
"stop_reason": null
},
{
"name": "r2-ms-default",
"task": "ms",
"budget": "default",
"verified_facts": 1,
"unknowns": 133,
"wall_s": 0.8994753339939052,
"cpu_s": 1.211395,
"host_group_rss_bytes": 326647808,
"tokens": {
"arguments": 67,
"reply": 233873,
"progress_stderr": 673
},
"reply_bytes": 624004,
"exit_code": 0,
"cleanup": "group_empty",
"stop_reason": null
},
{
"name": "r2-ms-double",
"task": "ms",
"budget": "double",
"verified_facts": 1,
"unknowns": 133,
"wall_s": 0.9041235420008888,
"cpu_s": 1.206674,
"host_group_rss_bytes": 326893568,
"tokens": {
"arguments": 81,
"reply": 233914,
"progress_stderr": 673
},
"reply_bytes": 624244,
"exit_code": 0,
"cleanup": "group_empty",
"stop_reason": null
},
{
"name": "r2-theme-default",
"task": "theme",
"budget": "default",
"verified_facts": 237,
"unknowns": 0,
"wall_s": 0.9415311659977306,
"cpu_s": 1.231308,
"host_group_rss_bytes": 348160000,
"tokens": {
"arguments": 67,
"reply": 258656,
"progress_stderr": 673
},
"reply_bytes": 733950,
"exit_code": 0,
"cleanup": "group_empty",
"stop_reason": null
},
{
"name": "r2-theme-double",
"task": "theme",
"budget": "double",
"verified_facts": 237,
"unknowns": 0,
"wall_s": 0.9384306669962825,
"cpu_s": 1.228734,
"host_group_rss_bytes": 350060544,
"tokens": {
"arguments": 81,
"reply": 258721,
"progress_stderr": 673
},
"reply_bytes": 734190,
"exit_code": 0,
"cleanup": "group_empty",
"stop_reason": null
},
{
"name": "r3-ms-default",
"task": "ms",
"budget": "default",
"verified_facts": 1,
"unknowns": 133,
"wall_s": 0.8975688329956029,
"cpu_s": 1.2376939999999998,
"host_group_rss_bytes": 337887232,
"tokens": {
"arguments": 67,
"reply": 233873,
"progress_stderr": 673
},
"reply_bytes": 624004,
"exit_code": 0,
"cleanup": "group_empty",
"stop_reason": null
},
{
"name": "r3-ms-double",
"task": "ms",
"budget": "double",
"verified_facts": 1,
"unknowns": 133,
"wall_s": 0.9211560420080787,
"cpu_s": 1.22497,
"host_group_rss_bytes": 346587136,
"tokens": {
"arguments": 81,
"reply": 233914,
"progress_stderr": 673
},
"reply_bytes": 624244,
"exit_code": 0,
"cleanup": "group_empty",
"stop_reason": null
},
{
"name": "r3-theme-default",
"task": "theme",
"budget": "default",
"verified_facts": 237,
"unknowns": 0,
"wall_s": 0.9006596249964787,
"cpu_s": 1.225968,
"host_group_rss_bytes": 340787200,
"tokens": {
"arguments": 67,
"reply": 258656,
"progress_stderr": 673
},
"reply_bytes": 733950,
"exit_code": 0,
"cleanup": "group_empty",
"stop_reason": null
},
{
"name": "r3-theme-double",
"task": "theme",
"budget": "double",
"verified_facts": 237,
"unknowns": 0,
"wall_s": 0.9200200000050245,
"cpu_s": 1.2206029999999999,
"host_group_rss_bytes": 346898432,
"tokens": {
"arguments": 81,
"reply": 258721,
"progress_stderr": 673
},
"reply_bytes": 734190,
"exit_code": 0,
"cleanup": "group_empty",
"stop_reason": null
}
],
"medians": [
{
"task": "theme",
"budget": "default",
"trials": 3,
"facts": 237,
"unknowns": 0,
"wall_s": 0.9374774580064695,
"cpu_s": 1.225968,
"host_group_rss_mib": 330.59375,
"tokens": 259396,
"reply_bytes": 733950
},
{
"task": "theme",
"budget": "double",
"trials": 3,
"facts": 237,
"unknowns": 0,
"wall_s": 0.9283738329977496,
"cpu_s": 1.228734,
"host_group_rss_mib": 330.828125,
"tokens": 259475,
"reply_bytes": 734190
},
{
"task": "ms",
"budget": "default",
"trials": 3,
"facts": 1,
"unknowns": 133,
"wall_s": 0.8994753339939052,
"cpu_s": 1.2376939999999998,
"host_group_rss_mib": 322.234375,
"tokens": 234613,
"reply_bytes": 624004
},
{
"task": "ms",
"budget": "double",
"trials": 3,
"facts": 1,
"unknowns": 133,
"wall_s": 0.9050752910115989,
"cpu_s": 1.22497,
"host_group_rss_mib": 330.53125,
"tokens": 234668,
"reply_bytes": 624244
}
],
"baseline": {
"head": "e2a2ede5440df396e44b3406ef511a31a680290e",
"working_tree": "uncommitted runtime; relevant compiled entrypoint/owner hashes retained; HEAD alone does not identify it",
"original_task_sha256": "a17526f4b4325139896488b1d64106e5f955f7a5f998a062cb6d319425136782",
"original_oracle_sha256": "1f4070c8814a1cd45b5ab205efd9cbf797f4b82aff195abb41877f79642c5388"
},
"measurement_scope": {
"observer": "sampled simultaneous process-group RSS; not a kernel hard cap",
"excluded_memory": "isolated JavaScript worker launches detached into another process group; its peak memory is unmeasured",
"cpu": "wait4 waited-subtree user+system; sampler and unwaited descendants excluded",
"cache": "fresh CLI/worker per trial; OS caches not flushed; fixed default-then-double ordering",
"node": "24.22.1",
"host": "macOS26.3 arm64",
"heap_mib": "CLI and isolated worker both held512 via NODE_OPTIONS and --max-heap-mb",
"deadline_seconds": 90,
"group_early_stop_mib": 1536,
"calls_per_task": 1,
"retries": 0
},
"unknown_equivalence": {
"theme": {
"unknown_meaning_sha256": "4f53cda18c2baa0c0354bb5f9a3ecbe5ed12ab4d8e11ba873c2f11161202b945",
"equivalent_trials": 6
},
"ms": {
"unknown_meaning_sha256": "9868f30b3b2ffc97cea3b26908a54673ff0137970a41c8c6ab95356ae48a9f19",
"equivalent_trials": 6
}
}
}
@@ -0,0 +1,76 @@
"""Verify pinned inputs and prepare inert application directories and an oracle."""
import argparse
import hashlib
import json
from pathlib import Path
import re
import shutil
def sha256(path):
return hashlib.sha256(path.read_bytes()).hexdigest()
def theme_values(source):
prefix, suffix = 'export const theme = {', '};\n'
if not source.startswith(prefix) or not source.endswith(suffix):
raise ValueError('Expected the pinned literal-only theme declaration')
body = source[len(prefix):-len(suffix)]
pair = re.compile(r"\s*([A-Za-z_]\w*)\s*:\s*'([^'\\]*)'\s*,")
values = {}
position = 0
while body[position:].strip():
item = pair.match(body, position)
if item is None or item[1] in values:
raise ValueError('Oracle only accepts distinct literal CSS property declarations')
values[item[1]] = item[2]
position = item.end()
if len(values) != 237:
raise ValueError('The pinned source must declare 237 theme properties')
return values
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('repo', type=Path)
parser.add_argument('source_map', type=Path)
parser.add_argument('output', type=Path)
options = parser.parse_args()
manifest = json.loads((Path(__file__).parent / 'manifest.json').read_text())
pinned = manifest['theme']
if sha256(options.source_map) != pinned['map_sha256']:
parser.error('Source-map digest does not match the pinned artifact')
document = json.loads(options.source_map.read_text())
matching = [text for name, text in zip(document['sources'], document['sourcesContent'])
if name == pinned['reported_source']]
if len(matching) != 1 or matching[0] is None:
parser.error('Expected one embedded original theme source')
source = matching[0]
if hashlib.sha256(source.encode()).hexdigest() != pinned['source_sha256']:
parser.error('Embedded source digest mismatch')
theme = options.output / 'cases/theme'
control = options.output / 'cases/ms'
theme.mkdir(parents=True, exist_ok=True)
control.mkdir(parents=True, exist_ok=True)
(theme / 'theme.ts').write_text(source)
(theme / 'package.json').write_text(json.dumps({
'name': 'rea-captured-theme-calibration', 'type': 'module', 'main': 'theme.ts',
}))
for name, digest in manifest['ms']['files'].items():
path = options.repo / 'node_modules/ms' / name
if sha256(path) != digest:
parser.error(f'Installed ms input digest mismatch: {name}')
shutil.copyfile(path, control / name)
oracle = {
'theme': {'source_file': 'theme.ts', 'source_sha256': pinned['source_sha256'],
'expected': theme_values(source)},
'ms': {'source_file': 'index.js',
'source_sha256': manifest['ms']['files']['index.js'], 'expected': {'s': 1000}},
}
options.output.mkdir(parents=True, exist_ok=True)
(options.output / 'oracle.json').write_text(json.dumps(oracle, indent=2) + '\n')
if __name__ == '__main__':
main()
@@ -0,0 +1,68 @@
import { readFile, writeFile } from "node:fs/promises";
import { createHash } from "node:crypto";
import { parse } from "@babel/parser";
const [output, ...files] = process.argv.slice(2);
const rows = [];
for (const file of files) {
const text = await readFile(file, "utf8");
const ast = parse(text, { sourceType: "unambiguous" });
const strings = [];
let nodes = 0;
const stack = [ast];
while (stack.length) {
const n = stack.pop();
if (!n || typeof n !== "object") continue;
if (Array.isArray(n)) {
stack.push(...n);
continue;
}
if (typeof n.type === "string") nodes++;
if (n.type === "StringLiteral") {
let oracle = false;
try {
oracle = JSON.parse(text.slice(n.start, n.end)) === n.value;
} catch {}
strings.push({
start: n.start,
end: n.end,
units: n.value.length,
jsonBytes: Buffer.byteLength(JSON.stringify(n.value)),
oracle,
});
}
if (
n.type === "TemplateLiteral" &&
n.expressions.length === 0 &&
n.quasis[0].value.cooked !== null
) {
const value = n.quasis[0].value.cooked;
const raw = text.slice(n.start + 1, n.end - 1);
strings.push({
start: n.start,
end: n.end,
units: value.length,
jsonBytes: Buffer.byteLength(JSON.stringify(value)),
oracle: !raw.includes("\\") && raw === value,
template: true,
});
}
for (const [k, v] of Object.entries(n))
if (
!["loc", "extra", "comments", "tokens"].includes(k) &&
v &&
typeof v === "object"
)
stack.push(v);
}
strings.sort((a, b) => b.units - a.units);
rows.push({
file,
bytes: Buffer.byteLength(text),
sha256: createHash("sha256").update(text).digest("hex"),
nodes,
strings: strings.length,
largest: strings.slice(0, 5),
});
}
await writeFile(output, JSON.stringify(rows, null, 2));
console.log(JSON.stringify(rows));
@@ -0,0 +1,192 @@
[
{
"file": "/tmp/rea-source-map-calibration/theme.EvD-JhP6.chunk.js",
"bytes": 29718,
"sha256": "10ecf4c66ae103222a7c146ad9b945dc83e78b6c78ce05021296e3c2f799bd5b",
"nodes": 4022,
"strings": 349,
"largest": [
{
"start": 1434,
"end": 1589,
"units": 152,
"jsonBytes": 155,
"oracle": true
},
{
"start": 3123,
"end": 3227,
"units": 102,
"jsonBytes": 104,
"oracle": true,
"template": true
},
{
"start": 7255,
"end": 7330,
"units": 73,
"jsonBytes": 75,
"oracle": true,
"template": true
},
{
"start": 18225,
"end": 18297,
"units": 70,
"jsonBytes": 72,
"oracle": true,
"template": true
},
{
"start": 10083,
"end": 10154,
"units": 69,
"jsonBytes": 71,
"oracle": true,
"template": true
}
]
},
{
"file": "/tmp/rea-source-map-calibration/index.CMrE7aQb.js",
"bytes": 2078745,
"sha256": "937f792d1849b69a6e0741234428b22b4884ec0157ea31dd6949dd1bacc46456",
"nodes": 460754,
"strings": 15897,
"largest": [
{
"start": 78956,
"end": 79894,
"units": 936,
"jsonBytes": 938,
"oracle": true,
"template": true
},
{
"start": 104891,
"end": 105550,
"units": 657,
"jsonBytes": 659,
"oracle": true,
"template": true
},
{
"start": 1752523,
"end": 1753169,
"units": 644,
"jsonBytes": 662,
"oracle": true,
"template": true
},
{
"start": 1751921,
"end": 1752324,
"units": 401,
"jsonBytes": 413,
"oracle": true,
"template": true
},
{
"start": 583954,
"end": 584343,
"units": 387,
"jsonBytes": 391,
"oracle": true,
"template": true
}
]
},
{
"file": "/tmp/rea-source-map-calibration/DateSelect.BVYaCNUe.chunk.js",
"bytes": 2191517,
"sha256": "9e45139dd0b510ad7014188a5e9d11b307c2ac15dfdc761050388e76e94759bf",
"nodes": 363578,
"strings": 29000,
"largest": [
{
"start": 34808,
"end": 47930,
"units": 13120,
"jsonBytes": 13122,
"oracle": true,
"template": true
},
{
"start": 21787,
"end": 34790,
"units": 13001,
"jsonBytes": 13003,
"oracle": true,
"template": true
},
{
"start": 8827,
"end": 21772,
"units": 12943,
"jsonBytes": 12945,
"oracle": true,
"template": true
},
{
"start": 47947,
"end": 50426,
"units": 2477,
"jsonBytes": 2479,
"oracle": true,
"template": true
},
{
"start": 50458,
"end": 52394,
"units": 1934,
"jsonBytes": 1936,
"oracle": true,
"template": true
}
]
},
{
"file": "node_modules/ms/index.js",
"bytes": 3024,
"sha256": "e5f0b6a946a9b2b356a28557728410717df54ea2f599edb619f9839df6b7b0e9",
"nodes": 427,
"strings": 48,
"largest": [
{
"start": 702,
"end": 757,
"units": 53,
"jsonBytes": 55,
"oracle": false
},
{
"start": 1851,
"end": 1865,
"units": 12,
"jsonBytes": 14,
"oracle": false
},
{
"start": 1876,
"end": 1889,
"units": 11,
"jsonBytes": 13,
"oracle": false
},
{
"start": 1745,
"end": 1754,
"units": 7,
"jsonBytes": 9,
"oracle": false
},
{
"start": 1639,
"end": 1648,
"units": 7,
"jsonBytes": 9,
"oracle": false
}
]
}
]
@@ -0,0 +1,70 @@
"""Run one complete CLI task and independently check the required source facts."""
import argparse
import hashlib
import json
import os
from pathlib import Path
import subprocess
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('repo', type=Path)
parser.add_argument('node')
parser.add_argument('inputs', type=Path)
parser.add_argument('case', choices=['theme', 'ms'])
parser.add_argument('budget', choices=['default', 'double'])
parser.add_argument('output', type=Path)
options = parser.parse_args()
oracle = json.loads((options.inputs / 'oracle.json').read_text())[options.case]
target = options.inputs / 'cases' / options.case
source = target / oracle['source_file']
if hashlib.sha256(source.read_bytes()).hexdigest() != oracle['source_sha256']:
raise ValueError('Selected source digest does not match the oracle')
argv = [options.node, str(options.repo / 'scripts/rea.mjs'),
'analyze-javascript-application', str(target), '--max-heap-mb', '512',
'--format', 'json']
if options.budget == 'double':
argv += ['--semantic-precision', '{"primitive_bytes":2097152}']
environment = {**os.environ, 'NODE_OPTIONS': '--max-old-space-size=512'}
result = subprocess.run(argv, cwd=options.repo, env=environment, capture_output=True,
text=True, timeout=45)
options.output.parent.mkdir(parents=True, exist_ok=True)
options.output.write_text(json.dumps({
'argv': argv, 'stdout': result.stdout, 'stderr': result.stderr,
'exit_code': result.returncode,
}) + '\n')
if result.returncode != 0:
raise RuntimeError(f'CLI failed: {result.stderr[:500]}')
evidence = json.loads(result.stdout)
graph = evidence['normalized_result']['semantic_graph']
nodes = {node['node_id']: node for node in graph['nodes']}
if any(node['identity']['artifact_sha256'] != oracle['source_sha256']
for node in nodes.values()
if node['identity']['module_path'] == oracle['source_file']):
raise ValueError('Selected graph source identity differs from the oracle')
facts = {}
for edge in graph['relations']:
if edge['relation'] != 'defines':
continue
left, right = nodes[edge['source_node_id']], nodes[edge['target_node_id']]
if left['kind'] != 'literal':
continue
if options.case == 'theme' and right['kind'] == 'property-slot':
facts[right['properties']['name']] = left['properties']['value']
if options.case == 'ms' and right['kind'] == 'binding' and right['label'] == 's':
facts['s'] = left['properties']['value']
if facts != oracle['expected']:
raise ValueError('Required source facts differ from the independent oracle')
if any('primitive-bytes' in json.dumps(unknown) for unknown in graph['unknowns']):
raise ValueError('Selected byte allowance unexpectedly limited the task')
print(json.dumps({
'case': options.case, 'budget': options.budget, 'verified_facts': len(facts),
'source_sha256': oracle['source_sha256'],
'reply_bytes': len(result.stdout.encode()), 'unknowns': len(graph['unknowns']),
}))
if __name__ == '__main__':
main()