mirror of
https://github.com/morluto/rea.git
synced 2026-10-26 17:35:11 +00:00
perf(apple): reuse retained Interface Builder graph validation (#5885)
Reuse successful owning-schema validation only for exact immutable Evidence snapshots, preserve current parent binding and complete source facts, and collect only requested document pages. Verify real XLD CLI/MCP workflows and archived connector UIDs, including complete pre-change baselines for both admission sizes and a wrong-baseline negative control. Fixes #5855.
This commit is contained in:
1 parent
aa53c06538
commit
33f912919d
8 files changed
+2722
-26
No files matched your search
@@ -0,0 +1,50 @@
|
||||
"""Read archived AppKit connector UIDs independently with Python's plistlib."""
|
||||
|
||||
import argparse
|
||||
import hashlib
|
||||
import json
|
||||
from pathlib import Path
|
||||
import plistlib
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument('--bundle', type=Path, required=True)
|
||||
parser.add_argument('--document', required=True)
|
||||
parser.add_argument('--baseline', type=Path, required=True,
|
||||
help='Complete pre-change 64-document Evidence; equality control, not the oracle')
|
||||
parser.add_argument('--small-baseline', type=Path, required=True,
|
||||
help='Complete pre-change 1-document Evidence')
|
||||
parser.add_argument('--output', type=Path, required=True)
|
||||
args = parser.parse_args()
|
||||
data = (args.bundle / args.document).read_bytes()
|
||||
objects = plistlib.loads(data)['$objects']
|
||||
connections = []
|
||||
for index, value in enumerate(objects):
|
||||
if not isinstance(value, dict) or not {'NSSource', 'NSLabel'} <= value.keys():
|
||||
continue
|
||||
classname = objects[value['$class'].data]['$classname']
|
||||
if classname not in ('NSNibOutletConnector', 'NSNibControlConnector'):
|
||||
continue
|
||||
connections.append({
|
||||
'archive_index': index,
|
||||
'kind': 'outlet' if classname == 'NSNibOutletConnector' else 'action',
|
||||
'name': objects[value['NSLabel'].data],
|
||||
'source': str(value['NSSource'].data),
|
||||
'destination': str(value['NSDestination'].data),
|
||||
})
|
||||
if not connections:
|
||||
raise ValueError('No supported archived connectors; choose a keyed AppKit document')
|
||||
args.output.write_text(json.dumps({
|
||||
'document': args.document,
|
||||
'archive_sha256': hashlib.sha256(data).hexdigest(),
|
||||
'connections': connections,
|
||||
'complete_result_paths': {
|
||||
'1': str(args.small_baseline.resolve()),
|
||||
'64': str(args.baseline.resolve()),
|
||||
},
|
||||
}, indent=2) + '\n')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,225 @@
|
||||
# Retained Interface Builder graph validation
|
||||
|
||||
Issue #5855. Baseline `f801b2fbfd28f4e3713adefddc7bd73db44c284c`, Linux
|
||||
x86_64 / AMD EPYC 7B12 / Node 24.18.0 / npm 11.16.0. This is static archive
|
||||
analysis on Linux, without executing an app or using ibtool or a native
|
||||
analysis provider.
|
||||
|
||||
## Question and oracle
|
||||
|
||||
Identify the AAC settings outlets and VBR action in the official
|
||||
[XLD 20250302 app](https://tmkk.undo.jp/xld/index_e.html), including archive
|
||||
source/destination object identities, connection evidence and limitations.
|
||||
|
||||
- [Official DMG](https://tmkk.undo.jp/xld/xld-20250302.dmg) SHA-256:
|
||||
`0032a5470ea4e32a11a35b8077ebf4986102891f8eb82743094f2c6621ad8aeb`.
|
||||
- Selected document:
|
||||
`Contents/PlugIns/XLDAacOutput.bundle/Contents/Resources/English.lproj/XLDAacOutput.nib`.
|
||||
- Archive SHA-256:
|
||||
`4fdbfba451ee64b1d333c96a4f3d1a0c4b543a36b9b3686e1151b51e4dd14c72`.
|
||||
- Python's independent `plistlib` reads the actual `$objects` table and
|
||||
`NSNibOutletConnector` / `NSNibControlConnector` records. Observed connections:
|
||||
|
||||
| Kind | Label | Source UID | Destination UID |
|
||||
| ------ | ------------- | ---------- | --------------- |
|
||||
| outlet | o_bitrate | 2 | 10 |
|
||||
| outlet | o_gaplessFlag | 2 | 41 |
|
||||
| outlet | o_prefPane | 2 | 53 |
|
||||
| outlet | o_quality | 2 | 62 |
|
||||
| outlet | o_useVBR | 2 | 58 |
|
||||
| action | vbrChecked: | 58 | 2 |
|
||||
|
||||
The benchmark checks each connector, both endpoint records, relationship kinds,
|
||||
archive path/digest and page coverage against these independent facts. It also
|
||||
compares all returned document rows with the exported complete parent; all
|
||||
64-document normalized results match the pre-change complete CLI Evidence.
|
||||
The complete exported parent Evidence is equal across versions for both
|
||||
admission sizes. The reproduction oracle requires a separate pre-change
|
||||
complete Evidence file for each size, and the harness compares the entire
|
||||
parent, including its identity and parameters, for the selected limit. Equality is a preservation control, not an independent proof
|
||||
of every decoded fact.
|
||||
|
||||
The default 64-document report contains 6,528 nodes and 1,378 edges. The small
|
||||
control admits one document from the same unmodified real app, with 80 nodes
|
||||
and 13 edges. Both reports retain truncation and hierarchy limitations. Resource
|
||||
sessions have no executable subject or executable digest; archive identities
|
||||
must not be promoted to executable identity.
|
||||
|
||||
## Completed workflow and accounting
|
||||
|
||||
`interface-builder-views.mjs` uses the real SDK and compiled stdio MCP server:
|
||||
connect, discover the full catalog, open bundle resources, decode with summary
|
||||
delivery, exhaust three 32-row node pages and one edge page for the selected
|
||||
document, repeat those same four pages as a warm query diagnostic, export the
|
||||
complete Evidence, verify the answer and complete parent, and await cleanup.
|
||||
The repeat is included in all whole-task costs. Each run starts a fresh server
|
||||
and isolated account. Decoding is the first decode in that server; retained
|
||||
queries follow the summary's first validation. OS caches were not flushed, so
|
||||
these are not disk-cold measurements.
|
||||
|
||||
Artifact acquisition/extraction, build time and independent oracle preparation
|
||||
are outside this analysis task. No actual model session or billed model token
|
||||
usage was measured. The SDK benchmark answers a fixed factual question; it does
|
||||
not establish agent usability or general provider performance.
|
||||
|
||||
Experiments ran serially using `resource-run.py --observe-payload-exit`: one CPU
|
||||
(affinity 15), 100% quota, 4 GiB hard memory / 3 GiB early stop, no swap and a
|
||||
192-task ceiling. CPU and peak memory cover the SDK, server and observer family.
|
||||
Peak is cgroup memory.peak, including charged page cache, rather than summed RSS.
|
||||
Payload-exit cgroup counters are authenticated and cleanup is verified; final
|
||||
whole-scope counters are unavailable after the transient scope disappears.
|
||||
Every accepted and failed task has exit/cleanup verification and zero observed
|
||||
memory high/max/OOM/OOM-kill and PID-limit events. Missing final counters are
|
||||
not interpreted as zero.
|
||||
|
||||
## Observations
|
||||
|
||||
Medians across all accepted baseline runs and the measured optimized
|
||||
production-source cohort. The later harness-only review fix does not change
|
||||
those production files; its revalidation is recorded separately. Five
|
||||
earlier candidate runs remain labeled pre-review diagnostics and are excluded
|
||||
from the final-source medians. One failed initial harness run is also retained.
|
||||
All individual runs, phase timings, token estimates and raw receipt hashes are
|
||||
in `interface-builder-views.json`.
|
||||
|
||||
| Admitted documents | Revision | Runs | Whole wall s | Family CPU s | Family peak MiB | First four pages ms | Repeated four pages ms |
|
||||
| ------------------ | -------- | ---- | ------------ | ------------ | --------------- | ------------------- | ---------------------- |
|
||||
| 64 | baseline | 5 | 30.798 | 30.532 | 1614.7 | 2606.3 | 1817.8 |
|
||||
| 64 | final | 5 | 27.683 | 27.424 | 1551.1 | 338.8 | 94.3 |
|
||||
| 1 | baseline | 4 | 15.702 | 15.456 | 952.6 | 389.5 | 101.0 |
|
||||
| 1 | final | 4 | 18.101 | 17.822 | 944.8 | 367.5 | 101.7 |
|
||||
|
||||
The larger sampled task has about 10% lower whole wall/CPU medians and 94.8%
|
||||
lower repeated-page latency. Whole-task ranges overlap substantially:
|
||||
27.420–51.481 s baseline and 25.247–45.451 s final. Startup/initial decoding
|
||||
vary independently of the retained-query change; these observations do not
|
||||
establish a general whole-task speedup or stable memory reduction. Both
|
||||
versions' slow first runs remain included.
|
||||
|
||||
The small sampled task is slower overall (15.3% wall), with no repeat-page
|
||||
benefit. In the two later interleaved small pairs, baseline took 16.883/18.317 s
|
||||
and final took 16.988/20.276 s. Do not interpret the earlier unpaired small
|
||||
measurements as evidence of a cold-start improvement. Startup variance remains
|
||||
unexplained; this optimization is justified by repeated large-parent queries,
|
||||
not a claim that every completed task becomes faster.
|
||||
|
||||
An additional production-boundary diagnostic isolates the changed projection
|
||||
from startup, decoding and transport. It imports both compiled owners, seals
|
||||
separate clones of the actual exported parent with each owner's snapshot
|
||||
module, and alternates version order for three epochs. Each epoch projects a
|
||||
summary and the same eight pages; every resulting payload is equal. Median
|
||||
projection CPU falls from 157.4 to 30.3 ms for the one-document parent and
|
||||
6003.5 to 914.7 ms for the 64-document parent. Snapshot preparation, Evidence
|
||||
authentication and all adapters are excluded from this diagnostic; the public
|
||||
tasks above account for their whole costs. First large-summary CPU rises from
|
||||
620.2 to 879.8 ms because representation comparison is paid up front. This is a
|
||||
reuse tradeoff, not a faster first decode. The diagnostic source/receipt hashes,
|
||||
per-call wall/CPU times and bounded process-family counters are retained in the
|
||||
JSON; it does not replace real public workflow evidence.
|
||||
|
||||
Selection still scans retained rows to count every match; no additional
|
||||
per-document index is retained. The whole original graph and serialization
|
||||
costs still exist.
|
||||
|
||||
Public input/output contracts and complete parent Evidence are unchanged.
|
||||
Tokenizer estimates count the entire 212-tool input catalog once, every RPC's
|
||||
arguments and text response, including warm diagnostics and export. The raw SDK
|
||||
catalog, including output schemas, is reported separately. Structured content
|
||||
and model-specific discovery/exposure may incur different costs. The 64-document task estimates are 282,885 baseline and 282,887 for the
|
||||
initial candidate (198,492 input-catalog tokens in each); the two-token change
|
||||
comes solely from the export destination. The first ten RPC requests and
|
||||
replies are exactly equal. Raw SDK catalog exposure estimates 891,652 tokens
|
||||
including output schemas. Final-source values and every run are retained in
|
||||
the JSON. There is no claimed token reduction.
|
||||
|
||||
## Ownership and reuse
|
||||
|
||||
The Interface Builder view owner validates an unknown normalized result with
|
||||
its existing schema. Reuse requires the application's seal plus exact owned
|
||||
immutable snapshot identity and representation-preserving comparison against
|
||||
the parsed result. A weak set records only successful snapshots. It retains no
|
||||
second graph, file-path cache, index, transient errors or failed subject
|
||||
bindings. Every view still checks its current parent's target binding and
|
||||
constructs current parent provenance and limitations. Different results,
|
||||
mutable imports, forged seals and schema normalization revalidate normally.
|
||||
|
||||
Document selection uses the shared matching pager to count all matching rows
|
||||
while retaining only the requested page. Unfiltered requests keep direct
|
||||
slicing. Row IDs, ordering, provenance, decoder truncation and unknowns remain
|
||||
unchanged. CLI and MCP share the same application/domain implementation.
|
||||
|
||||
The snapshot comparison helper's type accepts owning schemas with optional
|
||||
undefined members; its runtime comparison is unchanged and refuses a parsed
|
||||
representation that actually adds an undefined field.
|
||||
|
||||
## Reproduction and limits
|
||||
|
||||
Extract the official app without running it. On the baseline, save complete
|
||||
CLI JSON with `rea decode-interface-builder <XLD.app> --format json` and a
|
||||
separate control with `rea decode-interface-builder <XLD.app> --max-documents 1
|
||||
--format json` (without profiler logging mixed into either JSON). Then:
|
||||
|
||||
```sh
|
||||
python3 experiments/analysis-efficiency/interface-builder-oracle.py \
|
||||
--bundle '<XLD.app>' \
|
||||
--document 'Contents/PlugIns/XLDAacOutput.bundle/Contents/Resources/English.lproj/XLDAacOutput.nib' \
|
||||
--baseline '<complete-64-document-evidence.json>' \
|
||||
--small-baseline '<complete-1-document-evidence.json>' --output '<oracle.json>'
|
||||
python3 experiments/analysis-efficiency/resource-run.py --observe-payload-exit -- \
|
||||
node experiments/analysis-efficiency/interface-builder-views.mjs \
|
||||
--runtime '<absolute baseline or candidate checkout>' --input '<XLD.app>' \
|
||||
--oracle '<oracle.json>' --output '<new output directory>' --documents 64
|
||||
```
|
||||
|
||||
Repeat with `--documents 1`. Paths must be absolute. The oracle uses standard
|
||||
Python only; token estimates use offline tiktoken 0.12.0 / cl100k_base. Count
|
||||
compact JSON tool name/description/inputSchema entries once, compact
|
||||
`{name,arguments}` for every transcript event, and every returned text block;
|
||||
these are estimates, not actual model usage.
|
||||
|
||||
Failed attempts remain visible. `base64-a` completed all eight page RPCs but the
|
||||
first harness incorrectly expected an action-source `contains` edge. Production
|
||||
correctly returned `target_action`; the harness was corrected before accepted
|
||||
runs. That failure's costs/receipt remain in the JSON. An initial typecheck
|
||||
refused the snapshot helper's unnecessarily narrow generic constraint for the
|
||||
optional resource root; the constraint was corrected without changing runtime
|
||||
comparison. Build resource observations with a still-exiting Turbo child are
|
||||
not qualified task measurements.
|
||||
|
||||
KeepingYouAwake 1.6.8 (official ZIP SHA-256
|
||||
`8001a149b4490c008fdac19898bce9902d516c4aa6412a7eb0f9a37443b15c6b`)
|
||||
was also downloaded and smoke-decoded through the real CLI
|
||||
(17 documents, 1,369 nodes, 542 edges), but its NIBArchive records cannot be read
|
||||
by this plistlib oracle. It is not a comparative benchmark or independent NIB
|
||||
correctness claim. macOS ibtool, runtime UI, other archive variants, globally
|
||||
complete hierarchy/class interpretation, #5720/#5721 and the remaining source
|
||||
decoder issues are unverified or unchanged here.
|
||||
|
||||
## Integration verification
|
||||
|
||||
The measured comparison above was pinned to `f801b2fbf`. The branch was then
|
||||
rebased onto `f5410bdc4` (the separate #5806 scalar-record fix). All six recorded owned-source
|
||||
fingerprints remained identical across that rebase. On the rebased runtime both real SDK
|
||||
tasks passed again, with the complete retained parent equal to the original
|
||||
baseline for both admission sizes. The complete real CLI Evidence and portable
|
||||
CLI page also equal the baseline/MCP results. These integration runs are recorded
|
||||
separately in the JSON and excluded from the pinned comparison medians.
|
||||
|
||||
The relevant 12-file suite passed 204 tests after the paging refactor. After
|
||||
rebasing, another six-file run passed 78 tests covering scalar source facts,
|
||||
keyed archive number metadata, Interface Builder views, production MCP summary
|
||||
paging and compiled CLI numeric/hierarchy boundaries. Both cohorts verified
|
||||
owned cleanup with no observed memory/PID limit events; tests used the bounded
|
||||
two-CPU, 4 GiB guard. Native ibtool/macOS runtime behavior is not established by
|
||||
these tests.
|
||||
|
||||
Greptile identified that the original script only enforced the pre-change
|
||||
comparison for 64 documents; the one-document equality above had been checked
|
||||
manually. The oracle now requires both baseline files and the harness compares
|
||||
the selected complete parent, including its Evidence ID and parameters, for
|
||||
either limit. Both real workflows passed again. An intentional wrong baseline
|
||||
(the 64-document parent supplied for the 1-document task) is rejected after all
|
||||
other real-task checks succeed, with cleanup verified. These review checks and
|
||||
updated harness fingerprints are recorded separately. Production source and
|
||||
original comparison medians are unchanged; original measured fingerprints are
|
||||
preserved rather than relabeled as the new harness.
|
||||
@@ -0,0 +1,238 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { createHash } from "node:crypto";
|
||||
import { createWriteStream } from "node:fs";
|
||||
import { mkdir, mkdtemp, readFile, writeFile } from "node:fs/promises";
|
||||
import { createRequire } from "node:module";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join, resolve } from "node:path";
|
||||
import { finished } from "node:stream/promises";
|
||||
import { pathToFileURL } from "node:url";
|
||||
import { parseArgs } from "node:util";
|
||||
import { createRetainedQueryCaller } from "./retained-query-calls.mjs";
|
||||
import { finalizeRetainedQueryReceipt } from "./retained-query-finalization.mjs";
|
||||
|
||||
const { values } = parseArgs({
|
||||
options: {
|
||||
runtime: { type: "string" },
|
||||
input: { type: "string" },
|
||||
oracle: { type: "string" },
|
||||
output: { type: "string" },
|
||||
documents: { type: "string", default: "64" },
|
||||
},
|
||||
});
|
||||
for (const key of ["runtime", "input", "oracle", "output"])
|
||||
assert.equal(values[key], resolve(values[key]));
|
||||
const maxDocuments = Number(values.documents);
|
||||
assert.ok([1, 64].includes(maxDocuments));
|
||||
const oracle = JSON.parse(await readFile(values.oracle, "utf8"));
|
||||
const baselinePath = oracle.complete_result_paths?.[String(maxDocuments)];
|
||||
assert.equal(
|
||||
typeof baselinePath,
|
||||
"string",
|
||||
"Provide a pre-change baseline for the selected document limit.",
|
||||
);
|
||||
assert.equal(baselinePath, resolve(baselinePath));
|
||||
const sha256 = (bytes) => createHash("sha256").update(bytes).digest("hex");
|
||||
assert.equal(
|
||||
sha256(await readFile(join(values.input, oracle.document))),
|
||||
oracle.archive_sha256,
|
||||
);
|
||||
const require = createRequire(join(values.runtime, "package.json"));
|
||||
const { Client } = await import(
|
||||
pathToFileURL(require.resolve("@modelcontextprotocol/client"))
|
||||
);
|
||||
const { StdioClientTransport } = await import(
|
||||
pathToFileURL(require.resolve("@modelcontextprotocol/client/stdio"))
|
||||
);
|
||||
await mkdir(values.output, { mode: 0o700 });
|
||||
const account = await mkdtemp(join(tmpdir(), "rea-interface-builder-views-"));
|
||||
const report = {
|
||||
question:
|
||||
"Identify archived AAC settings outlets and the VBR action with source/destination object identities, coverage and complete Evidence access.",
|
||||
max_documents: maxDocuments,
|
||||
calls: [],
|
||||
};
|
||||
const client = new Client({
|
||||
name: "rea-interface-builder-views",
|
||||
version: "1",
|
||||
});
|
||||
const transport = new StdioClientTransport({
|
||||
command: process.execPath,
|
||||
args: [join(values.runtime, "scripts/rea.mjs"), "mcp"],
|
||||
env: {
|
||||
HOME: account,
|
||||
PATH: process.env.PATH,
|
||||
NODE_OPTIONS: "--max-old-space-size=2048",
|
||||
UV_THREADPOOL_SIZE: "1",
|
||||
REA_LOG_LEVEL: "silent",
|
||||
},
|
||||
stderr: "pipe",
|
||||
});
|
||||
const serverLog = createWriteStream(join(values.output, "server.log"));
|
||||
const serverLogFinished = finished(serverLog);
|
||||
serverLogFinished.catch(() => {});
|
||||
transport.stderr?.pipe(serverLog);
|
||||
const transcript = [];
|
||||
const call = createRetainedQueryCaller(client, (receipt, event) => {
|
||||
report.calls.push(receipt);
|
||||
transcript.push(event);
|
||||
});
|
||||
async function result(name, input, phase = name) {
|
||||
const reply = await call(name, input, phase);
|
||||
assert.ok(!reply.isError, JSON.stringify(reply));
|
||||
assert.ok(reply.structuredContent);
|
||||
assert.equal(reply.content.length, 1);
|
||||
assert.equal(reply.content[0].type, "text");
|
||||
assert.deepEqual(JSON.parse(reply.content[0].text), reply.structuredContent);
|
||||
return reply.structuredContent;
|
||||
}
|
||||
try {
|
||||
await client.connect(transport);
|
||||
const catalog = await client.listTools();
|
||||
assert.equal(catalog.nextCursor, undefined);
|
||||
report.catalog_tools = catalog.tools.length;
|
||||
await writeFile(join(values.output, "catalog.json"), JSON.stringify(catalog));
|
||||
await result("open_bundle_resources", { path: values.input });
|
||||
const initial = await result("decode_interface_builder", {
|
||||
max_documents: maxDocuments,
|
||||
detail: "summary",
|
||||
});
|
||||
const summary = initial.normalized_result;
|
||||
report.parent_evidence_id = summary.parent_evidence_id;
|
||||
assert.equal(summary.resource_root.executable_sha256, null);
|
||||
assert.equal(summary.artifact.sha256, null);
|
||||
assert.equal(summary.summary.truncated, true);
|
||||
assert.ok(
|
||||
summary.summary.documents.some(
|
||||
(d) =>
|
||||
d.relative_path === oracle.document &&
|
||||
d.archive_sha256 === oracle.archive_sha256,
|
||||
),
|
||||
);
|
||||
const collections = {};
|
||||
for (const pass of ["first", "warm"]) {
|
||||
for (const collection of ["nodes", "edges"]) {
|
||||
const rows = [];
|
||||
let offset = 0;
|
||||
for (;;) {
|
||||
const selected = await result(
|
||||
"inspect_analysis_view",
|
||||
{
|
||||
source: {
|
||||
kind: "retained-evidence",
|
||||
evidence_id: report.parent_evidence_id,
|
||||
},
|
||||
view: {
|
||||
kind: "interface-builder",
|
||||
collection,
|
||||
document: oracle.document,
|
||||
offset,
|
||||
limit: 32,
|
||||
},
|
||||
},
|
||||
`retained:${pass}:${collection}:${offset}`,
|
||||
);
|
||||
const page = selected.normalized_result;
|
||||
assert.equal(page.parent_evidence_id, report.parent_evidence_id);
|
||||
assert.equal(page.source_truncated, true);
|
||||
assert.ok(page.unknowns.length > 0);
|
||||
rows.push(...page.items);
|
||||
if (page.coverage.exhausted) {
|
||||
assert.equal(rows.length, page.coverage.total);
|
||||
break;
|
||||
}
|
||||
assert.ok(page.items.length > 0);
|
||||
offset += page.items.length;
|
||||
}
|
||||
if (pass === "warm") assert.deepEqual(rows, collections[collection]);
|
||||
else collections[collection] = rows;
|
||||
}
|
||||
}
|
||||
const byId = new Map(collections.nodes.map((n) => [n.id, n]));
|
||||
for (const expected of oracle.connections) {
|
||||
const connector = collections.nodes.find(
|
||||
(n) => n.kind === expected.kind && n.name === expected.name,
|
||||
);
|
||||
assert.ok(connector);
|
||||
const sourceRelation =
|
||||
expected.kind === "outlet" ? "contains" : "target_action";
|
||||
const source = collections.edges.find(
|
||||
(e) => e.to === connector.id && e.relation === sourceRelation,
|
||||
);
|
||||
assert.equal(
|
||||
byId.get(source.from).attributes.interface_builder_object_id,
|
||||
expected.source,
|
||||
);
|
||||
const relation = expected.kind === "outlet" ? "outlet_to" : "target_action";
|
||||
const destination = collections.edges.find(
|
||||
(e) =>
|
||||
e.from === connector.id &&
|
||||
e.relation === relation &&
|
||||
byId.get(e.to)?.attributes.interface_builder_object_id ===
|
||||
expected.destination,
|
||||
);
|
||||
assert.equal(
|
||||
byId.get(destination.to).attributes.interface_builder_object_id,
|
||||
expected.destination,
|
||||
);
|
||||
for (const row of [connector, source, destination])
|
||||
assert.ok(
|
||||
row.evidence.some(
|
||||
(e) =>
|
||||
e.artifact_path === oracle.document &&
|
||||
e.artifact_sha256 === oracle.archive_sha256,
|
||||
),
|
||||
);
|
||||
}
|
||||
report.answer = oracle.connections;
|
||||
await result("export_evidence_bundle", {
|
||||
path: join(values.output, "evidence.json"),
|
||||
});
|
||||
const bundle = JSON.parse(
|
||||
await readFile(join(values.output, "evidence.json"), "utf8"),
|
||||
);
|
||||
const parent = bundle.records.find(
|
||||
(e) => e.evidence_id === report.parent_evidence_id,
|
||||
);
|
||||
assert.equal(parent.subject, null);
|
||||
assert.equal(parent.operation, "decode_interface_builder");
|
||||
const full = parent.normalized_result;
|
||||
for (const collection of ["nodes", "edges"])
|
||||
assert.deepEqual(
|
||||
collections[collection],
|
||||
full.graph[collection].filter((r) =>
|
||||
r.evidence.some((e) => e.artifact_path === oracle.document),
|
||||
),
|
||||
);
|
||||
const baselineBytes = await readFile(baselinePath, "utf8");
|
||||
const expectedParent = JSON.parse(baselineBytes);
|
||||
report.baseline_sha256 = sha256(baselineBytes);
|
||||
report.baseline_evidence_id = expectedParent.evidence_id;
|
||||
const mismatch = `Complete retained Evidence differs from the pre-change ${maxDocuments}-document baseline.`;
|
||||
assert.equal(parent.evidence_id, expectedParent.evidence_id, mismatch);
|
||||
assert.deepEqual(parent, expectedParent, mismatch);
|
||||
report.available_counts = {
|
||||
nodes: full.graph.nodes.length,
|
||||
edges: full.graph.edges.length,
|
||||
};
|
||||
report.oracle_sha256 = sha256(await readFile(values.oracle));
|
||||
report.status = "passed";
|
||||
} catch (cause) {
|
||||
report.status = "failed";
|
||||
report.failure = String(cause?.stack ?? cause);
|
||||
process.exitCode = 1;
|
||||
} finally {
|
||||
if (
|
||||
!(await finalizeRetainedQueryReceipt({
|
||||
report,
|
||||
client,
|
||||
transport,
|
||||
account,
|
||||
serverLog,
|
||||
serverLogFinished,
|
||||
output: values.output,
|
||||
transcript,
|
||||
}))
|
||||
)
|
||||
process.exitCode = 1;
|
||||
}
|
||||
Reference in new issue
Block a user