mirror of
https://github.com/morluto/rea.git
synced 2026-10-26 17:35:11 +00:00
perf(analysis-view): reuse validated immutable parents
Reuse successful format validation for exact authenticated immutable native and imported JavaScript results. Preserve schema normalization and alias topology before borrowing source data; owned JavaScript graph wrappers retain their existing metadata-normalization path. Validated with 130 source tests, 36 compiled CLI/MCP boundary tests, 27 default-deadline integration tests, full checks/docs/build, and paired real retained-Evidence tasks. The report preserves cold costs, previous negative results, incomplete resource-stopped runs, and the limits of offline token estimates. Fixes #4810 Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com> Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
This commit is contained in:
13 files changed
+1473
-22
No files matched your search
@@ -1,5 +1,10 @@
|
||||
# Analysis efficiency measurements
|
||||
|
||||
For immutable imported native/JavaScript parents, see the
|
||||
[validation-reuse checkpoint](immutable-parent-validation.md). It measures the
|
||||
owning format change with the existing retained-query driver, including first
|
||||
view costs, small cases, unchanged token estimates, and noisy whole-task results.
|
||||
|
||||
For authentic old Evidence and mixed investigations, see the
|
||||
[retained-query checkpoint](retained-investigation-workflows.md).
|
||||
`retained-investigation.mjs` compares existing complete delivery with selected
|
||||
|
||||
@@ -0,0 +1,654 @@
|
||||
{
|
||||
"issue": "https://github.com/morluto/rea/issues/4810",
|
||||
"baseline": "2c4338db1fdb7773e05376301c558cff2a0b4b80",
|
||||
"candidate": "3930a640fb8387cbd3079eee13fb90f82d1881dc",
|
||||
"units": {
|
||||
"latency": "milliseconds",
|
||||
"elapsed_cpu": "seconds",
|
||||
"memory": "kernel cgroup memory.peak bytes, including charged file cache and descendants"
|
||||
},
|
||||
"scope": "Three alternating before/after pairs per authentic retained native/JavaScript/small workflow; fresh stdio servers, three traversals of three two-row pages; catalog/import/oracles/export/close included in whole cost. OS caches uncontrolled. No actual model trial.",
|
||||
"resource_bounds": {
|
||||
"cpu_count": 1,
|
||||
"hard_memory_bytes": 8589934592,
|
||||
"early_stop_bytes": 7516192768,
|
||||
"swap_bytes": 0,
|
||||
"tasks_max": 192,
|
||||
"host_start_available_bytes_min": 17179869184,
|
||||
"host_stop_available_bytes_min": 8589934592,
|
||||
"observer_sha256": "6f089e645459eba80c5d73e39c21606c78173a44daf49fabc3b88c4631ce51c6",
|
||||
"guard_sha256s": [
|
||||
"760d541c60f4c0538f9c015964196e3be7bba12316f6085903244354e11aa992",
|
||||
"d2047721e50dd244900eb514d07025386e60908f77bb546bee5684baa1da2f19"
|
||||
],
|
||||
"guard_observability_note": "The newer private guard only records existing host-available-memory readings and its own cgroup path. Resource thresholds, CPU affinity, observer and stop/cleanup logic are unchanged."
|
||||
},
|
||||
"resource_accounting": "Final complete corpus tasks exit0/cleanup verified/oom0 at payload exit while observer remains alive. Resource-stopped and earlier failed attempts are recorded separately; terminal whole-scope counters are unavailable. Weak cache metadata/private heap not measured independently.",
|
||||
"catalog": {
|
||||
"tools": 207,
|
||||
"bytes": 3158368,
|
||||
"sha256": "1d6c9a679b7c935318f5ea59d5362ef0a06945b3f4a4f210c86997ec15ba4f4c",
|
||||
"equal_across_all_18_runs": true
|
||||
},
|
||||
"facts": {
|
||||
"native": {
|
||||
"answer_sha256": "4e28c2c2a25620576d27abe846a05835ec49386b889779e33410c31971523b54",
|
||||
"calls": 25,
|
||||
"query_structured_content_equal": true,
|
||||
"original_records_preserved": true,
|
||||
"closed_session_cleared": true
|
||||
},
|
||||
"javascript": {
|
||||
"answer_sha256": "0fe3ad249eb1a7233a4966a56652c173667c48fdaf395b6c08fc6901ef3b46f7",
|
||||
"calls": 28,
|
||||
"query_structured_content_equal": true,
|
||||
"original_records_preserved": true,
|
||||
"closed_session_cleared": true
|
||||
},
|
||||
"small": {
|
||||
"answer_sha256": "9ca5874ce7bb610e39632157304fe4f52b09401d5fbd97ec981ad0588b8da485",
|
||||
"calls": 19,
|
||||
"query_structured_content_equal": true,
|
||||
"original_records_preserved": true,
|
||||
"closed_session_cleared": true
|
||||
}
|
||||
},
|
||||
"runs": [
|
||||
{
|
||||
"name": "topology-paired-after-javascript-1",
|
||||
"first_page_ms": 2720.542831999999,
|
||||
"eight_later_pages_ms": 82.69408900000417,
|
||||
"elapsed_seconds": 20.713,
|
||||
"family_cpu_seconds": 20.50093,
|
||||
"family_peak_bytes": 1311358976,
|
||||
"import_ms": 10970.411619,
|
||||
"catalog_ms": 1194.7045099999996,
|
||||
"guard_sha256": "760d541c60f4c0538f9c015964196e3be7bba12316f6085903244354e11aa992"
|
||||
},
|
||||
{
|
||||
"name": "topology-paired-after-javascript-2",
|
||||
"first_page_ms": 1978.732818999999,
|
||||
"eight_later_pages_ms": 84.12934999999197,
|
||||
"elapsed_seconds": 20.011,
|
||||
"family_cpu_seconds": 19.733879,
|
||||
"family_peak_bytes": 1349349376,
|
||||
"import_ms": 10382.461203,
|
||||
"catalog_ms": 1436.8567430000003,
|
||||
"guard_sha256": "760d541c60f4c0538f9c015964196e3be7bba12316f6085903244354e11aa992"
|
||||
},
|
||||
{
|
||||
"name": "topology-paired-after-javascript-4",
|
||||
"first_page_ms": 1864.8931889999985,
|
||||
"eight_later_pages_ms": 75.18833300000188,
|
||||
"elapsed_seconds": 23.023,
|
||||
"family_cpu_seconds": 22.22643,
|
||||
"family_peak_bytes": 1410211840,
|
||||
"import_ms": 10857.059245,
|
||||
"catalog_ms": 2247.621502,
|
||||
"guard_sha256": "760d541c60f4c0538f9c015964196e3be7bba12316f6085903244354e11aa992"
|
||||
},
|
||||
{
|
||||
"name": "topology-paired-after-native-1",
|
||||
"first_page_ms": 216.87685000000056,
|
||||
"eight_later_pages_ms": 85.03580100000181,
|
||||
"elapsed_seconds": 17.253,
|
||||
"family_cpu_seconds": 17.002728,
|
||||
"family_peak_bytes": 1003585536,
|
||||
"import_ms": 10361.605091,
|
||||
"catalog_ms": 1289.5255869999992,
|
||||
"guard_sha256": "760d541c60f4c0538f9c015964196e3be7bba12316f6085903244354e11aa992"
|
||||
},
|
||||
{
|
||||
"name": "topology-paired-after-native-2",
|
||||
"first_page_ms": 209.0475239999996,
|
||||
"eight_later_pages_ms": 89.01302899999973,
|
||||
"elapsed_seconds": 14.383,
|
||||
"family_cpu_seconds": 14.091764,
|
||||
"family_peak_bytes": 1031806976,
|
||||
"import_ms": 7696.751557,
|
||||
"catalog_ms": 1175.233292,
|
||||
"guard_sha256": "760d541c60f4c0538f9c015964196e3be7bba12316f6085903244354e11aa992"
|
||||
},
|
||||
{
|
||||
"name": "topology-paired-after-native-3",
|
||||
"first_page_ms": 203.10824999999932,
|
||||
"eight_later_pages_ms": 65.1013469999998,
|
||||
"elapsed_seconds": 14.061,
|
||||
"family_cpu_seconds": 13.773228,
|
||||
"family_peak_bytes": 1098002432,
|
||||
"import_ms": 7967.397724,
|
||||
"catalog_ms": 1246.0458209999997,
|
||||
"guard_sha256": "760d541c60f4c0538f9c015964196e3be7bba12316f6085903244354e11aa992"
|
||||
},
|
||||
{
|
||||
"name": "topology-paired-before-javascript-1",
|
||||
"first_page_ms": 1681.2564180000008,
|
||||
"eight_later_pages_ms": 10378.131188999992,
|
||||
"elapsed_seconds": 31.213,
|
||||
"family_cpu_seconds": 30.872607,
|
||||
"family_peak_bytes": 1618685952,
|
||||
"import_ms": 11051.493961999999,
|
||||
"catalog_ms": 1508.7439510000004,
|
||||
"guard_sha256": "760d541c60f4c0538f9c015964196e3be7bba12316f6085903244354e11aa992"
|
||||
},
|
||||
{
|
||||
"name": "topology-paired-before-javascript-2",
|
||||
"first_page_ms": 2441.075147000003,
|
||||
"eight_later_pages_ms": 15534.410540000004,
|
||||
"elapsed_seconds": 40.856,
|
||||
"family_cpu_seconds": 40.485251,
|
||||
"family_peak_bytes": 1364238336,
|
||||
"import_ms": 15032.366213,
|
||||
"catalog_ms": 1319.6895839999997,
|
||||
"guard_sha256": "760d541c60f4c0538f9c015964196e3be7bba12316f6085903244354e11aa992"
|
||||
},
|
||||
{
|
||||
"name": "topology-paired-before-javascript-4",
|
||||
"first_page_ms": 2479.6895360000017,
|
||||
"eight_later_pages_ms": 16864.908822999998,
|
||||
"elapsed_seconds": 39.629,
|
||||
"family_cpu_seconds": 39.267656,
|
||||
"family_peak_bytes": 1403023360,
|
||||
"import_ms": 12437.073842999998,
|
||||
"catalog_ms": 1212.8024130000003,
|
||||
"guard_sha256": "760d541c60f4c0538f9c015964196e3be7bba12316f6085903244354e11aa992"
|
||||
},
|
||||
{
|
||||
"name": "topology-paired-before-native-1",
|
||||
"first_page_ms": 170.1303939999998,
|
||||
"eight_later_pages_ms": 469.8933869999928,
|
||||
"elapsed_seconds": 19.996,
|
||||
"family_cpu_seconds": 19.55106,
|
||||
"family_peak_bytes": 1063051264,
|
||||
"import_ms": 11242.171146,
|
||||
"catalog_ms": 1922.7818150000003,
|
||||
"guard_sha256": "760d541c60f4c0538f9c015964196e3be7bba12316f6085903244354e11aa992"
|
||||
},
|
||||
{
|
||||
"name": "topology-paired-before-native-2",
|
||||
"first_page_ms": 183.56196199999977,
|
||||
"eight_later_pages_ms": 393.4744759999994,
|
||||
"elapsed_seconds": 15.26,
|
||||
"family_cpu_seconds": 15.008304,
|
||||
"family_peak_bytes": 1058492416,
|
||||
"import_ms": 8713.099498,
|
||||
"catalog_ms": 1272.265849,
|
||||
"guard_sha256": "760d541c60f4c0538f9c015964196e3be7bba12316f6085903244354e11aa992"
|
||||
},
|
||||
{
|
||||
"name": "topology-paired-before-native-3",
|
||||
"first_page_ms": 244.26586400000087,
|
||||
"eight_later_pages_ms": 517.9172599999911,
|
||||
"elapsed_seconds": 19.083,
|
||||
"family_cpu_seconds": 18.752553,
|
||||
"family_peak_bytes": 1109987328,
|
||||
"import_ms": 9948.476274999999,
|
||||
"catalog_ms": 1879.787958,
|
||||
"guard_sha256": "760d541c60f4c0538f9c015964196e3be7bba12316f6085903244354e11aa992"
|
||||
},
|
||||
{
|
||||
"name": "topology-after-small-1",
|
||||
"first_page_ms": 226.6032009999999,
|
||||
"eight_later_pages_ms": 57.69190299999718,
|
||||
"elapsed_seconds": 12.312,
|
||||
"family_cpu_seconds": 12.043895,
|
||||
"family_peak_bytes": 1056452608,
|
||||
"import_ms": 6572.925846000001,
|
||||
"catalog_ms": 1173.0129960000004,
|
||||
"guard_sha256": "760d541c60f4c0538f9c015964196e3be7bba12316f6085903244354e11aa992"
|
||||
},
|
||||
{
|
||||
"name": "topology-after-small-2",
|
||||
"first_page_ms": 556.2178050000002,
|
||||
"eight_later_pages_ms": 96.23635600000307,
|
||||
"elapsed_seconds": 16.246,
|
||||
"family_cpu_seconds": 16.068419,
|
||||
"family_peak_bytes": 1002749952,
|
||||
"import_ms": 9666.640497,
|
||||
"catalog_ms": 1821.1315130000003,
|
||||
"guard_sha256": "760d541c60f4c0538f9c015964196e3be7bba12316f6085903244354e11aa992"
|
||||
},
|
||||
{
|
||||
"name": "topology-after-small-4",
|
||||
"first_page_ms": 111.79372199999852,
|
||||
"eight_later_pages_ms": 62.58284999999887,
|
||||
"elapsed_seconds": 11.901,
|
||||
"family_cpu_seconds": 11.707784,
|
||||
"family_peak_bytes": 976646144,
|
||||
"import_ms": 6473.91882,
|
||||
"catalog_ms": 1203.5203539999998,
|
||||
"guard_sha256": "d2047721e50dd244900eb514d07025386e60908f77bb546bee5684baa1da2f19"
|
||||
},
|
||||
{
|
||||
"name": "topology-before-small-1",
|
||||
"first_page_ms": 242.57812199999898,
|
||||
"eight_later_pages_ms": 299.467095,
|
||||
"elapsed_seconds": 17.585,
|
||||
"family_cpu_seconds": 17.312434,
|
||||
"family_peak_bytes": 1066864640,
|
||||
"import_ms": 9102.074585,
|
||||
"catalog_ms": 1829.971928,
|
||||
"guard_sha256": "760d541c60f4c0538f9c015964196e3be7bba12316f6085903244354e11aa992"
|
||||
},
|
||||
{
|
||||
"name": "topology-before-small-2",
|
||||
"first_page_ms": 133.3346359999996,
|
||||
"eight_later_pages_ms": 122.92684500000178,
|
||||
"elapsed_seconds": 14.302,
|
||||
"family_cpu_seconds": 13.936122,
|
||||
"family_peak_bytes": 1062400000,
|
||||
"import_ms": 7254.973107,
|
||||
"catalog_ms": 1170.888121,
|
||||
"guard_sha256": "760d541c60f4c0538f9c015964196e3be7bba12316f6085903244354e11aa992"
|
||||
},
|
||||
{
|
||||
"name": "topology-before-small-4",
|
||||
"first_page_ms": 100.81227200000103,
|
||||
"eight_later_pages_ms": 76.37957900000038,
|
||||
"elapsed_seconds": 12.0,
|
||||
"family_cpu_seconds": 11.765305,
|
||||
"family_peak_bytes": 983314432,
|
||||
"import_ms": 6614.461618999999,
|
||||
"catalog_ms": 1138.6469730000003,
|
||||
"guard_sha256": "d2047721e50dd244900eb514d07025386e60908f77bb546bee5684baa1da2f19"
|
||||
}
|
||||
],
|
||||
"token_estimates": {
|
||||
"tokenizer": "tiktoken",
|
||||
"version": "0.12.0",
|
||||
"encoding": "cl100k_base",
|
||||
"kind": "offline raw transport serialization estimate; one complete catalog plus each call name/arguments/reply exactly once; excludes initialization/framing, prompts, client rendering/discovery/replay, continuations and actual model usage; not an agent-token measurement",
|
||||
"runs": [
|
||||
{
|
||||
"run": "topology-paired-before-native-1",
|
||||
"catalog_tokens_estimated": 805215,
|
||||
"named_call_arguments_tokens_estimated": 1200,
|
||||
"reply_tokens_estimated": 43321,
|
||||
"total_tokens_estimated": 849736,
|
||||
"calls": 25
|
||||
},
|
||||
{
|
||||
"run": "topology-paired-after-native-1",
|
||||
"catalog_tokens_estimated": 805215,
|
||||
"named_call_arguments_tokens_estimated": 1200,
|
||||
"reply_tokens_estimated": 43321,
|
||||
"total_tokens_estimated": 849736,
|
||||
"calls": 25
|
||||
},
|
||||
{
|
||||
"run": "topology-paired-before-javascript-1",
|
||||
"catalog_tokens_estimated": 805215,
|
||||
"named_call_arguments_tokens_estimated": 1354,
|
||||
"reply_tokens_estimated": 49815,
|
||||
"total_tokens_estimated": 856384,
|
||||
"calls": 28
|
||||
},
|
||||
{
|
||||
"run": "topology-paired-after-javascript-1",
|
||||
"catalog_tokens_estimated": 805215,
|
||||
"named_call_arguments_tokens_estimated": 1354,
|
||||
"reply_tokens_estimated": 49815,
|
||||
"total_tokens_estimated": 856384,
|
||||
"calls": 28
|
||||
},
|
||||
{
|
||||
"run": "topology-before-small-1",
|
||||
"catalog_tokens_estimated": 805215,
|
||||
"named_call_arguments_tokens_estimated": 1118,
|
||||
"reply_tokens_estimated": 33970,
|
||||
"total_tokens_estimated": 840303,
|
||||
"calls": 19
|
||||
},
|
||||
{
|
||||
"run": "topology-after-small-1",
|
||||
"catalog_tokens_estimated": 805215,
|
||||
"named_call_arguments_tokens_estimated": 1118,
|
||||
"reply_tokens_estimated": 33970,
|
||||
"total_tokens_estimated": 840303,
|
||||
"calls": 19
|
||||
}
|
||||
]
|
||||
},
|
||||
"mixed_verification": [
|
||||
{
|
||||
"scenario": "native",
|
||||
"heads_checked": 64,
|
||||
"pages": 32,
|
||||
"revised_unknown_id": "unk_057c8f2dbf30ec98851a66ee054366bc6e2e168874581e6f99cb80673d0ff9b8",
|
||||
"revision_checked": 2,
|
||||
"stale_update_rejected": true,
|
||||
"tampered_import_rejected_atomically": true,
|
||||
"unknowns_after_close": 0,
|
||||
"old_reference_refused": true
|
||||
},
|
||||
{
|
||||
"scenario": "javascript",
|
||||
"heads_checked": 64,
|
||||
"pages": 32,
|
||||
"revised_unknown_id": "unk_057c8f2dbf30ec98851a66ee054366bc6e2e168874581e6f99cb80673d0ff9b8",
|
||||
"revision_checked": 2,
|
||||
"stale_update_rejected": true,
|
||||
"tampered_import_rejected_atomically": true,
|
||||
"unknowns_after_close": 0,
|
||||
"old_reference_refused": true
|
||||
}
|
||||
],
|
||||
"cli_parity": [
|
||||
{
|
||||
"scenario": "native",
|
||||
"exact_json_equal": true,
|
||||
"exact_bytes_equal": true,
|
||||
"bytes": 7316,
|
||||
"sha256_before": "eada9068db3b499da332b2c5226fdd3a48909ebcaa290fd72166325e2f664fca",
|
||||
"sha256_after": "eada9068db3b499da332b2c5226fdd3a48909ebcaa290fd72166325e2f664fca",
|
||||
"evidence_id": "ev_521a58e81427bb4b2c01b6a291bdfd7698068ee0eba371696daa9cc5ab32f3f0",
|
||||
"evidence_links": [
|
||||
"ev_14b4582084e425b597bea6246f0df94ab4856ac3ab9502c96551147210ca9c8b"
|
||||
]
|
||||
},
|
||||
{
|
||||
"scenario": "javascript",
|
||||
"exact_json_equal": true,
|
||||
"exact_bytes_equal": true,
|
||||
"bytes": 8876,
|
||||
"sha256_before": "1dc023e5ece8f9c8cea736cb0db4d4f60c9418f1573e1e46ff0ac55a4273cb5a",
|
||||
"sha256_after": "1dc023e5ece8f9c8cea736cb0db4d4f60c9418f1573e1e46ff0ac55a4273cb5a",
|
||||
"evidence_id": "ev_97cac46d5139f6c48c3142798087814dcf36a37a9c99f7253668576bd3ae282a",
|
||||
"evidence_links": [
|
||||
"ev_dd2ffe5ab2faa6e07e422886f64a87f99e35a0a1dce1acb5351b7a81cb2c9a11"
|
||||
]
|
||||
},
|
||||
{
|
||||
"scenario": "small",
|
||||
"exact_json_equal": true,
|
||||
"exact_bytes_equal": true,
|
||||
"bytes": 7181,
|
||||
"sha256_before": "00dd070690e7b113e0cad73f9f7bcda6f42c515f8fdcc5173e8815bd3748b084",
|
||||
"sha256_after": "00dd070690e7b113e0cad73f9f7bcda6f42c515f8fdcc5173e8815bd3748b084",
|
||||
"evidence_id": "ev_3dcbcc5b2002f1e6caf7df005e3fcfd06a3afe14ccd72fff924c8e76d8498914",
|
||||
"evidence_links": [
|
||||
"ev_775d6cc6d0de96402b6e57a4258a0b78c337cfb1b7760aa22c39c3ee450c7dff"
|
||||
]
|
||||
}
|
||||
],
|
||||
"stopped_source_validation": {
|
||||
"reason": "Started during initial before-javascript-3; stopped only the new owned validation scope, then retried serially. Baseline repetition3 retained but excluded from uncontended exploratory comparisons; all final cohort runs retained.",
|
||||
"status": "incomplete, shell reported exit241; no payload-completion receipt",
|
||||
"elapsed_seconds": 17.272,
|
||||
"family_cpu_seconds_last_observed": 12.204013,
|
||||
"peak_cgroup_bytes_sampled": 589766656,
|
||||
"final_scope_counters": null
|
||||
},
|
||||
"exploratory_costs": {
|
||||
"completed_tasks": 21,
|
||||
"elapsed_seconds_total": 649.262,
|
||||
"family_cpu_seconds_total": 599.762599,
|
||||
"family_peak_bytes_max": 1741795328,
|
||||
"comparison_status": "Earlier candidate and exploratory inputs; excluded from final comparison, raw individual reports retained in private audit cache.",
|
||||
"contended_baseline_task": {
|
||||
"name": "before-javascript-3",
|
||||
"revision": "2c4338db1fdb7773e05376301c558cff2a0b4b80",
|
||||
"status": "passed",
|
||||
"elapsed_seconds": 100.722,
|
||||
"family_cpu_seconds": 82.656181,
|
||||
"family_peak_bytes": 1290514432,
|
||||
"excluded_from_final_comparison": true
|
||||
}
|
||||
},
|
||||
"runtime_entry_sha256": {
|
||||
"before": "1586cea18f4d4702c949502cfc143a7dfd2c28414779e190ce8a42fedf90bd74",
|
||||
"after": "e05c70bed21c680445a5c6e314a16cce6f22f3909f93388bf602338062e63d1a"
|
||||
},
|
||||
"inputs": {
|
||||
"native": {
|
||||
"bundle_sha256": "180c6fe6d3706d9272c8bb33503c2dca6f145d5061e102e3457dd5f2d754b5c9",
|
||||
"bundle_bytes": 1745224,
|
||||
"source_evidence_id": "ev_14b4582084e425b597bea6246f0df94ab4856ac3ab9502c96551147210ca9c8b"
|
||||
},
|
||||
"javascript": {
|
||||
"bundle_sha256": "747ff602093a24d4876e3eb47da3f7ef68716a10ac7c09938f43c3be5c33e31d",
|
||||
"bundle_bytes": 21058430,
|
||||
"source_evidence_id": "ev_dd2ffe5ab2faa6e07e422886f64a87f99e35a0a1dce1acb5351b7a81cb2c9a11"
|
||||
},
|
||||
"small": {
|
||||
"bundle_sha256": "44ca561c2455706897d2ae8f51f45693e7276badd5e8454ec31d8c1b0cfb77eb",
|
||||
"bundle_bytes": 73051,
|
||||
"source_evidence_id": "ev_775d6cc6d0de96402b6e57a4258a0b78c337cfb1b7760aa22c39c3ee450c7dff"
|
||||
}
|
||||
},
|
||||
"validation": {
|
||||
"source": {
|
||||
"executed": "130 source tests across5files; check:fast; build:cached",
|
||||
"elapsed_seconds": 334.264,
|
||||
"family_cpu_seconds": 326.441674,
|
||||
"family_peak_bytes": 4487696384,
|
||||
"payload_exit_code": 0,
|
||||
"payload_cleanup_verified": true,
|
||||
"oom_events_at_payload_exit": 0
|
||||
},
|
||||
"compiled_boundary": {
|
||||
"executed": "36 CLI/MCP tests across4files",
|
||||
"elapsed_seconds": 93.343,
|
||||
"family_cpu_seconds": 91.926502,
|
||||
"family_peak_bytes": 2190270464,
|
||||
"payload_exit_code": 0,
|
||||
"payload_cleanup_verified": true,
|
||||
"oom_events_at_payload_exit": 0
|
||||
},
|
||||
"full_checks": {
|
||||
"source": "3930a640fb8387cbd3079eee13fb90f82d1881dc",
|
||||
"executed": "npm run check; npm run docs:check (five README consistency tests)",
|
||||
"elapsed_seconds": 45.552,
|
||||
"family_cpu_seconds": 43.309041,
|
||||
"family_peak_bytes": 1127768064,
|
||||
"payload_exit_code": 0,
|
||||
"payload_cleanup_verified": true,
|
||||
"oom_events_at_payload_exit": 0
|
||||
},
|
||||
"integration_current": {
|
||||
"source": "3930a640fb8387cbd3079eee13fb90f82d1881dc",
|
||||
"executed": "npm run test:focused -- three latest-main integration files; default test deadlines; 27 passed",
|
||||
"elapsed_seconds": 62.437,
|
||||
"family_cpu_seconds": 61.506209,
|
||||
"family_peak_bytes": 1846538240,
|
||||
"payload_exit_code": 0,
|
||||
"payload_cleanup_verified": true,
|
||||
"oom_events_at_payload_exit": 0
|
||||
}
|
||||
},
|
||||
"comparison_probe": {
|
||||
"scope": "Candidate-only internal JSON-owner regression, not baseline public-provider reproduction. Reflect.ownKeys instrumentation aborts at10000 inspections rather than consuming exponential CPU.",
|
||||
"objects_per_graph": 27,
|
||||
"before_candidate": "e30a94a4de6a3c4ec7c577c1551d2b448d82081e",
|
||||
"before_inspections": 10001,
|
||||
"before_status": "probe inspection budget exhausted",
|
||||
"after_candidate": "646312dad75e2cb2becb0dd4139b99a1c04d6bcc",
|
||||
"after_inspections": 54,
|
||||
"after_borrowed": true
|
||||
},
|
||||
"main_integration": {
|
||||
"main_commit": "bb212ffecb47e129cc569b9867b2b6e7c9b65685",
|
||||
"merge_commit": "71e0b2739a9cf9903e1cfa63dc5a5b576040fea7",
|
||||
"production_files_unchanged_from_measured_candidate": true,
|
||||
"scope": "Historical integration at71e0b2739 with production source646312dad; all attempts retained. Current source-specific checks are listed separately.",
|
||||
"attempts": [
|
||||
{
|
||||
"case": "initial_feature",
|
||||
"entrypoint": "npm run test:focused",
|
||||
"test_timeout_override_ms": null,
|
||||
"tests_passed": 26,
|
||||
"tests_failed": 1,
|
||||
"tests_skipped": 0,
|
||||
"payload_exit_code": 1,
|
||||
"elapsed_seconds": 139.805,
|
||||
"family_cpu_seconds": 125.252141,
|
||||
"family_peak_bytes": 1994223616,
|
||||
"payload_cleanup_verified": true,
|
||||
"oom_events_at_payload_exit": 0
|
||||
},
|
||||
{
|
||||
"case": "isolated_main",
|
||||
"entrypoint": "vitest run; one named test",
|
||||
"test_timeout_override_ms": null,
|
||||
"tests_passed": 1,
|
||||
"tests_failed": 0,
|
||||
"tests_skipped": 10,
|
||||
"payload_exit_code": 0,
|
||||
"elapsed_seconds": 25.96,
|
||||
"family_cpu_seconds": 25.502365,
|
||||
"family_peak_bytes": 1221394432,
|
||||
"payload_cleanup_verified": true,
|
||||
"oom_events_at_payload_exit": 0
|
||||
},
|
||||
{
|
||||
"case": "direct_main",
|
||||
"entrypoint": "vitest run; three files",
|
||||
"test_timeout_override_ms": null,
|
||||
"tests_passed": 27,
|
||||
"tests_failed": 0,
|
||||
"tests_skipped": 0,
|
||||
"payload_exit_code": 0,
|
||||
"elapsed_seconds": 68.868,
|
||||
"family_cpu_seconds": 68.020337,
|
||||
"family_peak_bytes": 1605603328,
|
||||
"payload_cleanup_verified": true,
|
||||
"oom_events_at_payload_exit": 0
|
||||
},
|
||||
{
|
||||
"case": "extended_feature",
|
||||
"entrypoint": "vitest run; three files",
|
||||
"test_timeout_override_ms": 120000,
|
||||
"tests_passed": 27,
|
||||
"tests_failed": 0,
|
||||
"tests_skipped": 0,
|
||||
"payload_exit_code": 0,
|
||||
"elapsed_seconds": 75.866,
|
||||
"family_cpu_seconds": 75.193788,
|
||||
"family_peak_bytes": 1937674240,
|
||||
"payload_cleanup_verified": true,
|
||||
"oom_events_at_payload_exit": 0
|
||||
},
|
||||
{
|
||||
"case": "exact_main",
|
||||
"entrypoint": "npm run test:focused",
|
||||
"test_timeout_override_ms": null,
|
||||
"tests_passed": 27,
|
||||
"tests_failed": 0,
|
||||
"tests_skipped": 0,
|
||||
"payload_exit_code": 0,
|
||||
"elapsed_seconds": 66.443,
|
||||
"family_cpu_seconds": 65.304325,
|
||||
"family_peak_bytes": 2055409664,
|
||||
"payload_cleanup_verified": true,
|
||||
"oom_events_at_payload_exit": 0
|
||||
},
|
||||
{
|
||||
"case": "exact_feature",
|
||||
"entrypoint": "npm run test:focused",
|
||||
"test_timeout_override_ms": null,
|
||||
"tests_passed": 27,
|
||||
"tests_failed": 0,
|
||||
"tests_skipped": 0,
|
||||
"payload_exit_code": 0,
|
||||
"elapsed_seconds": 66.878,
|
||||
"family_cpu_seconds": 66.012242,
|
||||
"family_peak_bytes": 1907380224,
|
||||
"payload_cleanup_verified": true,
|
||||
"oom_events_at_payload_exit": 0
|
||||
}
|
||||
],
|
||||
"measured_production_commit": "646312dad75e2cb2becb0dd4139b99a1c04d6bcc"
|
||||
},
|
||||
"small_cache_diagnostic": {
|
||||
"scope": "Additional real public MCP run with return-preserving WeakSet method instrumentation. Timings excluded from before/after comparison. Owner identified from captured add stack; raw trace retained privately.",
|
||||
"format_owner": "safeParseOwnedBinaryLayout",
|
||||
"source_objects": 1,
|
||||
"successful_admissions": 1,
|
||||
"misses": 1,
|
||||
"hits": 8,
|
||||
"queries": 9,
|
||||
"original_records_preserved": true,
|
||||
"closed_session_cleared": true,
|
||||
"small_workflow_latency_regression_retained": true,
|
||||
"measured_production_commit": "646312dad75e2cb2becb0dd4139b99a1c04d6bcc"
|
||||
},
|
||||
"previous_checkpoints": [
|
||||
{
|
||||
"production_commit": "e30a94a4de6a3c4ec7c577c1551d2b448d82081e",
|
||||
"report_commit": "3f271efdef8d71020bdcfd7b2d3d9817bf0a6ad8",
|
||||
"report_url": "https://github.com/morluto/rea/blob/3f271efdef8d71020bdcfd7b2d3d9817bf0a6ad8/experiments/analysis-efficiency/immutable-parent-validation.json",
|
||||
"completed_paired_tasks": 18,
|
||||
"elapsed_seconds_total": 498.234,
|
||||
"family_cpu_seconds_total": 477.513448,
|
||||
"family_peak_bytes_max": 1519980544,
|
||||
"scope": "Historical candidate before shared-node comparison memoization; every result retained, including slow npm repetition3. Not the final comparison."
|
||||
},
|
||||
{
|
||||
"production_commit": "646312dad75e2cb2becb0dd4139b99a1c04d6bcc",
|
||||
"report_commit": "fc55be1e05555c6b295209ef5f354617f663b8f8",
|
||||
"report_url": "https://github.com/morluto/rea/blob/fc55be1e05555c6b295209ef5f354617f663b8f8/experiments/analysis-efficiency/immutable-parent-validation.json",
|
||||
"completed_paired_tasks": 18,
|
||||
"elapsed_seconds_total": 483.511,
|
||||
"family_cpu_seconds_total": 458.990461,
|
||||
"family_peak_bytes_max": 1598119936,
|
||||
"scope": "Historical candidate before shared-node comparison memoization; every result retained, including slow npm repetition3. Not the final comparison."
|
||||
}
|
||||
],
|
||||
"review_normalization_fix": {
|
||||
"comment": "https://github.com/morluto/rea/pull/4962#discussion_r4305886491",
|
||||
"before_source": "646312dad75e2cb2becb0dd4139b99a1c04d6bcc",
|
||||
"before_pr_head": "fc55be1e05555c6b295209ef5f354617f663b8f8",
|
||||
"expected_failing_regressions": 4,
|
||||
"after_source": "3930a640fb8387cbd3079eee13fb90f82d1881dc",
|
||||
"source_tests_passed": 130,
|
||||
"structural_fix": "Two-way node identity mapping visits preserved descendants; already-owned JavaScript graph path keeps parsed metadata and avoids whole-graph comparison. Only the expensive imported fallback admits the full-result cache.",
|
||||
"red_probe_cost": {
|
||||
"elapsed_seconds": 5.332,
|
||||
"family_cpu_seconds": 4.412336,
|
||||
"family_peak_bytes": 438231040,
|
||||
"payload_exit_code": 1,
|
||||
"payload_cleanup_verified": true,
|
||||
"expected_failures": 4
|
||||
},
|
||||
"current_dag_probe": {
|
||||
"source": "3930a640fb8387cbd3079eee13fb90f82d1881dc",
|
||||
"objects_per_graph": 27,
|
||||
"inspections": 54,
|
||||
"borrowed": true
|
||||
}
|
||||
},
|
||||
"resource_stops": [
|
||||
{
|
||||
"task": "topology-paired-after-javascript-3",
|
||||
"status": "incomplete",
|
||||
"reason": "host_available_memory_early_stop",
|
||||
"elapsed_seconds": 15.048,
|
||||
"family_cpu_seconds_last_observed": 14.048702,
|
||||
"peak_cgroup_bytes_sampled": 947695616,
|
||||
"payload_completion_counters": null,
|
||||
"owned_scope_after_stop": "inactive/dead",
|
||||
"replacement": "fresh JavaScript pair4, after then before; final pairs1,2,4"
|
||||
},
|
||||
{
|
||||
"task": "topology-before-small-3",
|
||||
"status": "incomplete",
|
||||
"reason": "host_available_memory_early_stop",
|
||||
"elapsed_seconds": 11.83,
|
||||
"family_cpu_seconds_last_observed": 11.535779,
|
||||
"peak_cgroup_bytes_sampled": 947818496,
|
||||
"payload_completion_counters": null,
|
||||
"replacement": "fresh small pair4 after memory recovered; final small pairs1,2,4"
|
||||
}
|
||||
],
|
||||
"unpaired_completed_attempts": [
|
||||
{
|
||||
"task": "topology-paired-before-javascript-3",
|
||||
"status": "passed",
|
||||
"original_records_preserved": true,
|
||||
"elapsed_seconds": 38.862,
|
||||
"family_cpu_seconds": 38.570479,
|
||||
"family_peak_bytes": 1390338048,
|
||||
"reason_not_in_paired_summary": "Its paired candidate was resource-stopped; kept separately, with both sides rerun as pair4."
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,249 @@
|
||||
# Reuse successful immutable parent validation
|
||||
|
||||
This checkpoint addresses [#4810](https://github.com/morluto/rea/issues/4810),
|
||||
found in the [retained-investigation audit](retained-investigation-workflows.md).
|
||||
It changes the shared format interpretation used by CLI and MCP views, without
|
||||
adding a tool, changing a schema, projecting away source data, or rerunning a
|
||||
provider.
|
||||
|
||||
## Question, ownership, and compatibility
|
||||
|
||||
The task resumes authentic exported Evidence in a fresh production stdio MCP
|
||||
server, discovers its source, reads six original symbols or modules in two-row
|
||||
pages, repeats that traversal three times, queries the existing Unknowns,
|
||||
exports the investigation, and closes it. The original source records must
|
||||
survive the round trip exactly. Pages are compared to original retained facts.
|
||||
Independent ELF byte decoding checks symbol names, values, and ranges; JavaScript
|
||||
checks cover selected source bytes and the `lib/cli.js` module identity, rather
|
||||
than independently proving the entire application inventory or semantic graph.
|
||||
|
||||
Imported JavaScript graphs lack the producer's realm-private format proofs.
|
||||
Previously each view repeated full schema, graph/hash, and cross-graph binding
|
||||
validation. Native layout views likewise parsed the entire layout for every
|
||||
page. An immutable Evidence root alone was insufficient: its selected normalized
|
||||
result did not carry the JSON owner's private sealing proof.
|
||||
|
||||
The JSON owner now transfers that proof to a selected own member without another
|
||||
walk or per-node registration. Native layouts and imported JavaScript results
|
||||
record a **successful full
|
||||
validation of the exact immutable source** in their owning WeakSets. Already
|
||||
owned JavaScript graphs keep the existing metadata normalization and binding
|
||||
checks, returning parsed metadata with their original graph references. That
|
||||
path avoids rewalking a large owned graph just to borrow its wrapper. There is
|
||||
no second
|
||||
retained parsed graph, strong cache, cross-process cache, or live-state cache.
|
||||
The schema implementation is fixed for the process; a different source object
|
||||
must validate independently. Weak entries do not keep Evidence alive, and session
|
||||
lookup continues to gate access after close.
|
||||
|
||||
Cache admission follows full validation and requires that parsing preserved the
|
||||
source representation. A shared iterative JSON comparison checks own data keys,
|
||||
scalar values, prototypes, and enumerability without calling accessors. Object
|
||||
member order is immaterial; array order remains exact. Two-way identity maps
|
||||
visit preserved descendants once and reject source-node splits or merges. Both
|
||||
comparison maps are temporary and discarded on return. Parser defaults,
|
||||
hidden-field removal, or other normalization retain the parser's
|
||||
result and do not cache the source. Writable/configurable descriptor differences
|
||||
are immaterial: the source is sealed while the temporary parse is mutable.
|
||||
The JSON sealing boundary rejects proxies before reflection, so arbitrary
|
||||
mutable proxy behavior cannot acquire an immutable proof. Mutable, merely
|
||||
Object.freeze'd, failed, and abandoned inputs do not qualify for successful
|
||||
format reuse.
|
||||
|
||||
No Evidence identity, source linkage, coverage, unknown, omission, error, or
|
||||
caller-selected page changes are intended. Complete source Evidence remains
|
||||
accessible through the existing contracts. The first imported view still pays for full
|
||||
validation and the representation comparison; a one-off CLI invocation cannot
|
||||
benefit from a cache in a previous process.
|
||||
|
||||
## Inputs and measurement scope
|
||||
|
||||
Baseline: `2c4338db1fdb7773e05376301c558cff2a0b4b80` (REA 6.6.0).
|
||||
Node 24.18.0, npm 11.16.0, Linux x86-64 (AMD EPYC 7B12, kernel 6.1.0-53-cloud).
|
||||
The ELF producer reports pwntools 4.15.0, pyelftools 0.33, and Unicorn 2.1.2.
|
||||
The baseline's built runtime was preserved
|
||||
before production changes. Candidate and baseline use the same locked packages.
|
||||
|
||||
- SQLite `/usr/lib/x86_64-linux-gnu/libsqlite3.so.0.8.6`: SHA-256
|
||||
`2e6eef9a727f081f0d453b4e5e6cbd8b9ef8b6f86cbf7681cbad444d3b0b55c8`;
|
||||
26 sections, 1,479 symbols, 2,963 relocations. Original history: 1,745,224 bytes.
|
||||
- Shipped npm 11.16.0: inventory digest
|
||||
`f8e576a7d162542c616168b25a95a5f62b9accf29467cc37e149905ad4b8e603`;
|
||||
five explicitly selected paths (`package.json`, `bin/npm-cli.js`, `lib/cli.js`,
|
||||
`lib/cli/entry.js`, `lib/commands/install.js`), 2,822 application nodes,
|
||||
1,796 semantic nodes, 13,682 semantic unknowns. The 1,416 unselected files stay
|
||||
unknown. Original history: 21,058,430 bytes.
|
||||
- Small case `/usr/bin/true`: SHA-256
|
||||
`c79bf44242829108e323378531f4ac839513ca1fba45efd6583643526e1e9fd2`;
|
||||
31 sections, 53 symbols, 66 relocations. Produced through the real production
|
||||
`inspect_binary_layout` MCP operation. An independent raw ELF reader checked
|
||||
every symbol name, value, and source range plus section/relocation counts.
|
||||
|
||||
The two larger histories were produced and independently checked in the linked
|
||||
previous audit. They are historical Evidence, not a claim of fresh provider
|
||||
analysis during a resumed view. All tasks here are static; no target executable
|
||||
is run.
|
||||
|
||||
Each completed comparison includes startup, the complete 207-tool catalog,
|
||||
import, queries, oracle checks, export, close, receipt writing, and SDK cleanup.
|
||||
RPC timings exclude subsequent client-side oracle/receipt work. Fresh servers
|
||||
separate first-view validation from later pages; OS file caches are uncontrolled.
|
||||
Runs alternate baseline/candidate order. One CPU, 8 GiB hard family memory,
|
||||
7 GiB early stop, no swap, and a 192-task ceiling bound each serial task.
|
||||
Resource counters are observed at payload exit while the observer remains alive;
|
||||
final whole-scope counters are unavailable. Kernel cgroup peaks include charged
|
||||
file cache and descendants, rather than just the server's JS heap.
|
||||
|
||||
## Results and executed validation
|
||||
|
||||
Candidate production code: `3930a640fb8387cbd3079eee13fb90f82d1881dc`.
|
||||
The [curated receipt](immutable-parent-validation.json) retains every final run,
|
||||
offline token estimates, compatibility checks, and costs of previous attempts.
|
||||
Values below are medians with min–max ranges across three fresh alternating pairs.
|
||||
|
||||
| Source | First page, before → after (ms) | Eight later pages, before → after (ms) |
|
||||
| ---------- | ----------------------------------------------------- | -------------------------------------------------- |
|
||||
| SQLite ELF | 183.56 [170.13–244.27] → 209.05 [203.11–216.88] | 469.89 [393.47–517.92] → 85.04 [65.10–89.01] |
|
||||
| npm | 2441.08 [1681.26–2479.69] → 1978.73 [1864.89–2720.54] | 15534.41 [10378.13–16864.91] → 82.69 [75.19–84.13] |
|
||||
| Small ELF | 133.33 [100.81–242.58] → 226.60 [111.79–556.22] | 122.93 [76.38–299.47] → 62.58 [57.69–96.24] |
|
||||
|
||||
| Source / runtime | Whole elapsed (s) | Family CPU (s) | Kernel family peak (MiB) |
|
||||
| ------------------- | ------------------- | ------------------- | ------------------------ |
|
||||
| SQLite ELF / before | 19.08 [15.26–20.00] | 18.75 [15.01–19.55] | 1013.8 [1009.5–1058.6] |
|
||||
| SQLite ELF / after | 14.38 [14.06–17.25] | 14.09 [13.77–17.00] | 984.0 [957.1–1047.1] |
|
||||
| npm / before | 39.63 [31.21–40.86] | 39.27 [30.87–40.49] | 1338.0 [1301.0–1543.7] |
|
||||
| npm / after | 20.71 [20.01–23.02] | 20.50 [19.73–22.23] | 1286.8 [1250.6–1344.9] |
|
||||
| Small ELF / before | 14.30 [12.00–17.59] | 13.94 [11.77–17.31] | 1013.2 [937.8–1017.4] |
|
||||
| Small ELF / after | 12.31 [11.90–16.25] | 12.04 [11.71–16.07] | 956.3 [931.4–1007.5] |
|
||||
|
||||
| Source | Paired warm-read ratio, before / after | Paired first-view change, after − before (ms) |
|
||||
| ---------- | -------------------------------------- | --------------------------------------------- |
|
||||
| SQLite ELF | 5.53× [4.42–7.96] | +25.49 [-41.16–+46.75] |
|
||||
| npm | 184.65× [125.50–224.30] | -462.34 [-614.80–+1039.29] |
|
||||
| Small ELF | 1.28× [1.22–5.19] | +10.98 [-15.97–+422.88] |
|
||||
|
||||
Each pair matches the before/after runs with the same repetition number.
|
||||
The paired effect can differ from the ratio of marginal medians when the host
|
||||
speed changes. Positive first-view changes mean higher latency.
|
||||
|
||||
The repeated-read benefit is measured per source and pair above. First-view
|
||||
validation
|
||||
also performs the representation comparison and can be more expensive. Whole
|
||||
task, CPU and memory figures above are measured SDK workflow costs, not a
|
||||
general provider-analysis or agent-investigation speedup. The previous e30
|
||||
checkpoint had a slow npm repetition on both sides, including a 65-second
|
||||
candidate task; its cause was not profiled. The original checkpoint and every
|
||||
fresh run remain retained. The previous `646312dad` checkpoint also had slower
|
||||
small-case warm reads in two of three pairs (135.54 → 373.14 ms marginal
|
||||
medians). Those negative results remain available in its linked historical
|
||||
receipt; the current cohort improves all three small pairs, without establishing
|
||||
a general small-input or whole-task benefit. No model-token saving, general memory improvement,
|
||||
or complex-software investigation advantage is established.
|
||||
|
||||
All 18 fresh workflow runs preserve original records and canonical answer
|
||||
digests. Every corresponding query's structured result and complete catalog
|
||||
is equal. The three public CLI source pairs are also byte-for-byte identical.
|
||||
The two additional mixed-history tasks preserve all 97 original records, check
|
||||
64 follow-up heads over 32 pages and revision two, reject stale updates and
|
||||
tampered imports atomically, clear Unknowns on close, and refuse old references.
|
||||
|
||||
Offline `tiktoken` 0.12.0 / `cl100k_base` estimates for one pass over the complete
|
||||
catalog, named-call arguments and replies are **unchanged** within each pair:
|
||||
849,736 native, 856,384 JavaScript, and 840,303 small-case tokens. The catalog
|
||||
contributes 805,215 tokens alone.
|
||||
These are raw transport serialization estimates, not actual model context or
|
||||
usage; initialization/framing, prompts, client discovery, rendering/replay,
|
||||
and continuations are unmeasured. Metadata paths differ from the historical
|
||||
checkpoint; compare equivalent runs within each checkpoint.
|
||||
|
||||
Executed checks at the final production code:
|
||||
|
||||
- `npm run check` and `npm run docs:check`: pass, including five README
|
||||
consistency tests.
|
||||
- Three latest-main integration files through `npm run test:focused`, using
|
||||
default deadlines: 27 tests pass at the current production source.
|
||||
- 130 source tests across immutable JSON, native/JavaScript format validation,
|
||||
and the shared application view service; `npm run check:fast` and
|
||||
`npm run build:cached` pass.
|
||||
- Four compiled CLI/MCP files (`evidenceBundleInline`, `serverUnknownsMcp`,
|
||||
`analysisViewMcp`, and `analysisViewCli`): 36 tests pass.
|
||||
- Real public stdio MCP: 18 fresh paired tasks and two additional mixed tasks;
|
||||
public CLI: six invocations, with three exact output pairs. These corpus
|
||||
workflows and four-file boundary checks have exit zero, verified payload
|
||||
cleanup and zero observed payload-exit OOM events.
|
||||
|
||||
Internal and automated review reproduced **candidate-only** defects before integration.
|
||||
Generic deep equality ignored hidden fields removed by parsing; the JSON owner
|
||||
comparison now preserves that normalization. Review also reproduced aliases crossing
|
||||
preserved subtrees and cloned metadata; four regressions failed against the prior
|
||||
source. The comparer now visits preserved descendants with two-way identity mapping.
|
||||
Already-owned JavaScript graphs keep their parsed-metadata fast path, so the generic
|
||||
comparison and full-result cache only apply to the expensive imported fallback. Its
|
||||
initial iterative traversal
|
||||
also repeatedly expanded shared nodes: a valid 27-object graph exhausted an
|
||||
instrumented 10,000-inspection budget. Tracking source-to-parsed node pairs
|
||||
reduces that same compiled probe to 54 inspections and successful borrowing.
|
||||
A parser that splits or merges source nodes keeps its parsed result rather than
|
||||
expanding repeated paths. Deterministic regressions enforce the inspection
|
||||
budget instead of relying on timing or letting a regression consume exponential
|
||||
CPU. These are internal JSON-owner probes, not claimed baseline public-provider
|
||||
reproductions.
|
||||
|
||||
The initial exploratory cohort and historical e30 paired checkpoint are not
|
||||
the final comparison. One earlier source-check attempt was mistakenly started
|
||||
during an initial baseline run; the newly started owned scope was stopped after
|
||||
17.27 s and that check remains incomplete (exit 241). Observed counters, the
|
||||
contended baseline, subsequent successful retries, and prior checkpoints remain
|
||||
retained; no OOM or terminal-counter claim is inferred from that stopped task.
|
||||
Two later tasks stopped because the host-available-memory guard reported its
|
||||
early-stop condition: candidate JavaScript repetition three and baseline small
|
||||
repetition three. Both incomplete attempts and the unmatched completed
|
||||
JavaScript baseline remain separate. After memory recovered, both sides were
|
||||
rerun as fresh pair four; final JavaScript and small samples use pairs 1, 2, 4.
|
||||
Neither interrupted task is a completed comparison. The guard later gained
|
||||
read-only host-memory/cgroup receipt fields; all resource thresholds and its
|
||||
observer/stop logic remained unchanged.
|
||||
|
||||
Real Ghidra, Hopper, JEB, fresh JavaScript producer performance, and actual
|
||||
agent trials are outside this checkpoint. Private WeakSet metadata/heap retention
|
||||
was not measured independently; the absence of a strong retained parsed graph
|
||||
is source-derived. The comparison maps exist only during the first
|
||||
interpretation; measured family
|
||||
peaks include their temporary cost.
|
||||
|
||||
## Latest-main integration and an unexplained timeout
|
||||
|
||||
Merge `71e0b2739` brings in main `bb212ffec`'s three test/config/guide fixes.
|
||||
The current source passes all 27 tests in those three integration files through
|
||||
`npm run test:focused` with the default deadlines. The earlier `646312dad`
|
||||
checkpoint initially passed 26 and timed out the live identity/catalog test at
|
||||
30 seconds. Matching official-command controls subsequently passed all 27 on
|
||||
both unmodified main and that candidate. Direct/extended-deadline diagnostics
|
||||
are recorded separately. Their passing results do not establish the cause of
|
||||
the original timeout, which remains preserved and unexplained. No test-policy
|
||||
or product file was changed to obtain a pass.
|
||||
|
||||
## Reproduction and retained evidence
|
||||
|
||||
Use `retained-investigation.mjs` from this directory with an authentic exported
|
||||
bundle and its original source Evidence ID. Build both selected runtime revisions
|
||||
with the pinned toolchain, preserving a separate baseline. For each runtime run:
|
||||
|
||||
```sh
|
||||
python3 experiments/analysis-efficiency/resource-run.py --observe-payload-exit -- \
|
||||
node experiments/analysis-efficiency/retained-investigation.mjs \
|
||||
--runtime-root /absolute/runtime --revision REVISION \
|
||||
--bundle /absolute/history.json --evidence-id ORIGINAL_EVIDENCE_ID \
|
||||
--scenario native --delivery focused --output /absolute/new-receipt-directory
|
||||
```
|
||||
|
||||
Use `--scenario javascript` for the npm history, and `--verify-mixed` with an
|
||||
actual mixed investigation. Reuse the same bytes and source identity on both
|
||||
sides. Do not regenerate parent identities or synthesize bundle records.
|
||||
The checked-in wrapper defaults to a 4 GiB hard / 3 GiB early stop. This
|
||||
checkpoint used a private resource-only copy with 8 GiB / 7 GiB thresholds,
|
||||
a 16 GiB available-memory start floor and 8 GiB host early stop. Its source
|
||||
and unchanged observer hashes are recorded in the curated receipt. This local
|
||||
resource policy does not change the public workflow or global wrapper defaults.
|
||||
Raw histories, transcripts, oracle outputs, and resource logs stay in the private
|
||||
local audit cache; only curated receipts are committed.
|
||||
@@ -25,6 +25,36 @@ import { EvidenceIntegrityError } from "../../domain/evidenceErrors.js";
|
||||
import { err, ok } from "../../domain/result.js";
|
||||
import { EvidenceLedger } from "../investigation/EvidenceLedger.js";
|
||||
import { WEB_NETWORK_CAPTURE_LIMITS } from "../../domain/webNetworkCapture.js";
|
||||
import { isImmutableJsonSnapshot } from "../../domain/immutableJson.js";
|
||||
|
||||
it.each(["layout", "javascript"])(
|
||||
"carries the retained %s source snapshot into repeatable shared views",
|
||||
(kind) => {
|
||||
const parent =
|
||||
kind === "layout"
|
||||
? analysisViewLayoutEvidence()
|
||||
: analysisViewJavaScriptEvidence();
|
||||
const ledger = new EvidenceLedger();
|
||||
const recorded = ledger.record(parent);
|
||||
if (!recorded.ok) throw recorded.error;
|
||||
const borrowed = ledger.forAnalysis(parent.evidence_id)!;
|
||||
const original = JSON.stringify(borrowed);
|
||||
const input = {
|
||||
source: { kind: "retained-evidence", evidence_id: parent.evidence_id },
|
||||
view: { kind: "summary" },
|
||||
} as const;
|
||||
const first = inspectAnalysisView(input, (id) => ledger.forAnalysis(id));
|
||||
if (!first.ok) throw first.error;
|
||||
const repeated = inspectAnalysisView(input, (id) => ledger.forAnalysis(id));
|
||||
if (!repeated.ok) throw repeated.error;
|
||||
expect(repeated.value.normalized_result).toEqual(
|
||||
first.value.normalized_result,
|
||||
);
|
||||
expect(isImmutableJsonSnapshot(borrowed.normalized_result)).toBe(true);
|
||||
expect(JSON.stringify(borrowed)).toBe(original);
|
||||
expect(ledger.forAnalysis(parent.evidence_id)).toBe(borrowed);
|
||||
},
|
||||
);
|
||||
|
||||
it.each(["/fixtures/unknown.exe", ""])(
|
||||
"preserves recorded empty native identity fields: path=%j",
|
||||
|
||||
@@ -21,7 +21,10 @@ import {
|
||||
type InspectAnalysisViewInput,
|
||||
} from "../../domain/analysisView/analysisView.js";
|
||||
import { jsonValueSchema } from "../../domain/jsonValue.js";
|
||||
import { isImmutableJsonSnapshot } from "../../domain/immutableJson.js";
|
||||
import {
|
||||
immutableJsonSnapshotMember,
|
||||
isImmutableJsonSnapshot,
|
||||
} from "../../domain/immutableJson.js";
|
||||
import { err, ok, type Result } from "../../domain/result.js";
|
||||
import { WebNetworkApiSurfaceAccumulator } from "../../domain/analysisView/WebNetworkApiSurfaceAccumulator.js";
|
||||
import { admitAnalysisViewData } from "../../domain/analysisView/analysisViewDataAdmission.js";
|
||||
@@ -70,7 +73,9 @@ export const inspectAnalysisViewValidated = (
|
||||
{
|
||||
evidenceId: parent.evidence_id,
|
||||
operation: parent.operation,
|
||||
normalizedResult: parent.normalized_result,
|
||||
normalizedResult:
|
||||
immutableJsonSnapshotMember(parent, "normalized_result") ??
|
||||
parent.normalized_result,
|
||||
// Sealing an Evidence root deep-freezes it; only the root is registered.
|
||||
sealedResult: isImmutableJsonSnapshot(parent),
|
||||
subject: parent.subject,
|
||||
|
||||
@@ -10,10 +10,7 @@ import {
|
||||
} from "./javascriptFactsView.js";
|
||||
import { javaScriptSourceSelectionSchema } from "../javascript/javascriptSourceSelection.js";
|
||||
|
||||
import {
|
||||
AnalysisInputError,
|
||||
AnalysisUnsupportedTargetError,
|
||||
} from "../analysisErrorCore.js";
|
||||
import { AnalysisUnsupportedTargetError } from "../analysisErrorCore.js";
|
||||
import {
|
||||
INSPECT_ANALYSIS_VIEW_OPERATION,
|
||||
analysisViewInputError,
|
||||
@@ -24,7 +21,7 @@ import { evidenceSchema, type Evidence } from "../evidence.js";
|
||||
import { jsonObjectSchema, jsonValueSchema } from "../jsonValue.js";
|
||||
import { err, ok, type Result } from "../result.js";
|
||||
import type { AnalysisError } from "../analysisErrorBase.js";
|
||||
import { binaryLayoutSchema } from "../native/binaryLayout.js";
|
||||
import { safeParseOwnedBinaryLayout } from "../native/binaryLayout.js";
|
||||
import { functionDossierSchema } from "../hopperValues.js";
|
||||
import {
|
||||
artifactGraphManifestSchema,
|
||||
@@ -741,8 +738,8 @@ const parseJavaScriptParent = (
|
||||
input: unknown,
|
||||
): Result<JavaScriptApplicationAnalysisResult, AnalysisError> => {
|
||||
try {
|
||||
// Only the existing realm-private graph proofs permit reuse. Imported or
|
||||
// merely frozen graphs still receive full canonical validation.
|
||||
// The format owner authenticates imported results completely before reuse;
|
||||
// mutable or merely frozen inputs still receive canonical validation.
|
||||
const steps = parseOwnedJavaScriptApplicationAnalysisSteps(input);
|
||||
let step = steps.next();
|
||||
while (!step.done) step = steps.next();
|
||||
@@ -841,7 +838,7 @@ export const projectAnalysisView = (
|
||||
return projected.ok ? ok(sealAnalysisView(projected.value)) : projected;
|
||||
}
|
||||
if (parent.operation === "inspect_binary_layout") {
|
||||
const layout = binaryLayoutSchema.safeParse(parent.normalizedResult);
|
||||
const layout = safeParseOwnedBinaryLayout(parent.normalizedResult);
|
||||
if (!layout.success)
|
||||
return err(
|
||||
analysisViewInputError([
|
||||
|
||||
@@ -1,12 +1,55 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
|
||||
import type { JsonValue } from "./jsonValue.js";
|
||||
import {
|
||||
freezeJsonSnapshot,
|
||||
canBorrowParsedJsonSnapshot,
|
||||
immutableJsonSnapshotMember,
|
||||
isImmutableJsonSnapshot,
|
||||
} from "./immutableJson.js";
|
||||
|
||||
describe("owned immutable JSON snapshots", () => {
|
||||
it("borrows only the selected own child of a completed sealed root", () => {
|
||||
const value = freezeJsonSnapshot({
|
||||
selected: { values: [1, 2] },
|
||||
other: { value: 3 },
|
||||
absent: null,
|
||||
});
|
||||
expect(isImmutableJsonSnapshot(value.selected)).toBe(false);
|
||||
expect(immutableJsonSnapshotMember(value, "selected")).toBe(value.selected);
|
||||
expect(isImmutableJsonSnapshot(value.selected)).toBe(true);
|
||||
expect(isImmutableJsonSnapshot(value.other)).toBe(false);
|
||||
expect(immutableJsonSnapshotMember(value.selected, "values")).toBe(
|
||||
value.selected.values,
|
||||
);
|
||||
expect(isImmutableJsonSnapshot(value.selected.values)).toBe(true);
|
||||
expect(immutableJsonSnapshotMember(value, "absent")).toBeNull();
|
||||
expect(immutableJsonSnapshotMember(value, "missing")).toBeUndefined();
|
||||
expect(immutableJsonSnapshotMember(value, "toString")).toBeUndefined();
|
||||
expect(Reflect.set(value.selected.values, 0, 9)).toBe(false);
|
||||
});
|
||||
|
||||
it("does not borrow children from unowned roots or invoke their accessors", () => {
|
||||
let accessed = false;
|
||||
const child = Object.freeze({ value: 1 });
|
||||
const root = Object.freeze({ child });
|
||||
expect(immutableJsonSnapshotMember(root, "child")).toBeUndefined();
|
||||
expect(isImmutableJsonSnapshot(child)).toBe(false);
|
||||
expect(
|
||||
immutableJsonSnapshotMember(
|
||||
{
|
||||
get child() {
|
||||
accessed = true;
|
||||
return child;
|
||||
},
|
||||
},
|
||||
"child",
|
||||
),
|
||||
).toBeUndefined();
|
||||
expect(accessed).toBe(false);
|
||||
expect(immutableJsonSnapshotMember(null, "child")).toBeUndefined();
|
||||
});
|
||||
|
||||
it("seals reachable data and preserves repeated references", () => {
|
||||
const shared = { values: [1, "observed"] };
|
||||
const value = { left: shared, right: shared };
|
||||
@@ -61,4 +104,166 @@ describe("owned immutable JSON snapshots", () => {
|
||||
"JSON values",
|
||||
);
|
||||
});
|
||||
|
||||
it("rejects proxy snapshots before reflecting on their mutable behavior", () => {
|
||||
let reflected = false;
|
||||
const proxy = new Proxy(
|
||||
{ value: 1 },
|
||||
{
|
||||
getPrototypeOf() {
|
||||
reflected = true;
|
||||
throw new Error("must not reflect on a proxy");
|
||||
},
|
||||
},
|
||||
);
|
||||
const root = { child: proxy };
|
||||
for (const input of [proxy, root]) {
|
||||
expect(() => freezeJsonSnapshot(input)).toThrow("proxies");
|
||||
expect(isImmutableJsonSnapshot(input)).toBe(false);
|
||||
}
|
||||
expect(Object.isFrozen(root)).toBe(false);
|
||||
expect(reflected).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("borrowing a parsed JSON snapshot", () => {
|
||||
it("accepts reordered object fields but preserves arrays and exact scalars", () => {
|
||||
const source = freezeJsonSnapshot({ second: [true, null], first: -0 });
|
||||
expect(
|
||||
canBorrowParsedJsonSnapshot(source, { first: -0, second: [true, null] }),
|
||||
).toBe(true);
|
||||
expect(
|
||||
canBorrowParsedJsonSnapshot(source, { first: 0, second: [true, null] }),
|
||||
).toBe(false);
|
||||
expect(
|
||||
canBorrowParsedJsonSnapshot(source, { first: -0, second: [null, true] }),
|
||||
).toBe(false);
|
||||
expect(
|
||||
canBorrowParsedJsonSnapshot(Object.freeze({ value: 1 }), { value: 1 }),
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
it("preserves parser defaults, hidden-field removal and enumerability changes", () => {
|
||||
const source = freezeJsonSnapshot({ observed: 1 });
|
||||
expect(
|
||||
canBorrowParsedJsonSnapshot(source, { observed: 1, defaulted: 0 }),
|
||||
).toBe(false);
|
||||
const hidden = { observed: 1 };
|
||||
Object.defineProperty(hidden, "unmodeled", { value: 2, enumerable: false });
|
||||
freezeJsonSnapshot(hidden);
|
||||
expect(canBorrowParsedJsonSnapshot(hidden, { observed: 1 })).toBe(false);
|
||||
const nonenumerable = { observed: 1 };
|
||||
Object.defineProperty(nonenumerable, "observed", { enumerable: false });
|
||||
freezeJsonSnapshot(nonenumerable);
|
||||
expect(canBorrowParsedJsonSnapshot(nonenumerable, { observed: 1 })).toBe(
|
||||
false,
|
||||
);
|
||||
});
|
||||
|
||||
it("does not evaluate accessors introduced by a parsed representation", () => {
|
||||
let accessed = false;
|
||||
const source = freezeJsonSnapshot({ observed: 1 });
|
||||
expect(
|
||||
canBorrowParsedJsonSnapshot(source, {
|
||||
get observed() {
|
||||
accessed = true;
|
||||
return 1;
|
||||
},
|
||||
}),
|
||||
).toBe(false);
|
||||
expect(accessed).toBe(false);
|
||||
});
|
||||
|
||||
it("declines parser-introduced proxies without reflecting on them", () => {
|
||||
let reflected = false;
|
||||
const parsed = new Proxy(
|
||||
{ value: 1 },
|
||||
{
|
||||
getPrototypeOf() {
|
||||
reflected = true;
|
||||
throw new Error("must not reflect on a proxy");
|
||||
},
|
||||
},
|
||||
);
|
||||
expect(
|
||||
canBorrowParsedJsonSnapshot(freezeJsonSnapshot({ value: 1 }), parsed),
|
||||
).toBe(false);
|
||||
expect(reflected).toBe(false);
|
||||
});
|
||||
|
||||
it("compares deeply nested snapshots without depending on the call stack", () => {
|
||||
let source: JsonValue = 1;
|
||||
let parsed: JsonValue = 1;
|
||||
for (let depth = 0; depth < 6_000; depth += 1) {
|
||||
source = { child: source };
|
||||
parsed = { child: parsed };
|
||||
}
|
||||
freezeJsonSnapshot(source);
|
||||
expect(canBorrowParsedJsonSnapshot(source, parsed)).toBe(true);
|
||||
});
|
||||
|
||||
it("compares shared snapshot graphs without repeatedly expanding their paths", () => {
|
||||
let source: JsonValue = { value: 1 };
|
||||
for (let depth = 0; depth < 26; depth += 1)
|
||||
source = { left: source, right: source };
|
||||
const parsed = structuredClone(source);
|
||||
freezeJsonSnapshot(source);
|
||||
const ownKeys = Reflect.ownKeys;
|
||||
let inspections = 0;
|
||||
const observed = vi
|
||||
.spyOn(Reflect, "ownKeys")
|
||||
.mockImplementation((value) => {
|
||||
// Fail deterministically instead of consuming exponential CPU on regressions.
|
||||
if (++inspections > 10_000)
|
||||
throw new Error("Shared JSON comparison exceeded inspection budget");
|
||||
return ownKeys(value);
|
||||
});
|
||||
let borrowed: boolean;
|
||||
try {
|
||||
borrowed = canBorrowParsedJsonSnapshot(source, parsed);
|
||||
} finally {
|
||||
observed.mockRestore();
|
||||
}
|
||||
expect(borrowed).toBe(true);
|
||||
});
|
||||
|
||||
it("keeps the parsed result when a parser splits shared source nodes", () => {
|
||||
const shared = { value: 1 };
|
||||
const source = freezeJsonSnapshot({ left: shared, right: shared });
|
||||
expect(
|
||||
canBorrowParsedJsonSnapshot(source, {
|
||||
left: { value: 1 },
|
||||
right: { value: 1 },
|
||||
}),
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
it.each(["direct", "nested"])(
|
||||
"detects shared-node splits across a %s preserved subtree",
|
||||
(kind) => {
|
||||
const shared = { value: 1 };
|
||||
const source = freezeJsonSnapshot({
|
||||
preserved: kind === "direct" ? shared : { nested: shared },
|
||||
cloned: shared,
|
||||
});
|
||||
expect(
|
||||
canBorrowParsedJsonSnapshot(source, {
|
||||
preserved: source.preserved,
|
||||
cloned: { value: 1 },
|
||||
}),
|
||||
).toBe(false);
|
||||
expect(canBorrowParsedJsonSnapshot(source, source)).toBe(true);
|
||||
},
|
||||
);
|
||||
|
||||
it("keeps the parsed result when a parser merges distinct source nodes", () => {
|
||||
const source = freezeJsonSnapshot({
|
||||
left: { value: 1 },
|
||||
right: { value: 1 },
|
||||
});
|
||||
const shared = { value: 1 };
|
||||
expect(
|
||||
canBorrowParsedJsonSnapshot(source, { left: shared, right: shared }),
|
||||
).toBe(false);
|
||||
});
|
||||
});
|
||||
@@ -1,3 +1,5 @@
|
||||
import { isProxy } from "node:util/types";
|
||||
|
||||
import type { JsonValue } from "./jsonValue.js";
|
||||
|
||||
const immutableSnapshots = new WeakSet<object>();
|
||||
@@ -14,6 +16,87 @@ export const isImmutableJsonSnapshot = (value: unknown): value is JsonValue =>
|
||||
(typeof value === "number" && Number.isFinite(value)) ||
|
||||
(typeof value === "object" && immutableSnapshots.has(value));
|
||||
|
||||
/** Borrow one own member of a completed snapshot, carrying its sealing proof. */
|
||||
export const immutableJsonSnapshotMember = (
|
||||
snapshot: unknown,
|
||||
key: string,
|
||||
): JsonValue | undefined => {
|
||||
if (
|
||||
typeof snapshot !== "object" ||
|
||||
snapshot === null ||
|
||||
!immutableSnapshots.has(snapshot)
|
||||
)
|
||||
return undefined;
|
||||
const descriptor = Object.getOwnPropertyDescriptor(snapshot, key);
|
||||
if (descriptor === undefined || !Object.hasOwn(descriptor, "value"))
|
||||
return undefined;
|
||||
// Completed sealing checked and froze every reachable member. Register only
|
||||
// the selected child; do not traverse it again or add per-node weak entries.
|
||||
const member = descriptor.value as JsonValue;
|
||||
if (typeof member === "object" && member !== null)
|
||||
immutableSnapshots.add(member);
|
||||
return member;
|
||||
};
|
||||
|
||||
/** Match a completed snapshot to its parsed representation without recursion. */
|
||||
export const canBorrowParsedJsonSnapshot = <Parsed extends JsonValue>(
|
||||
source: unknown,
|
||||
parsed: Parsed,
|
||||
): source is Parsed => {
|
||||
if (!isImmutableJsonSnapshot(source)) return false;
|
||||
if (Object.is(source, parsed)) return true;
|
||||
const pending: unknown[] = [source, parsed];
|
||||
const compared = new Map<object, object>();
|
||||
const sources = new Map<object, object>();
|
||||
while (pending.length > 0) {
|
||||
const right = pending.pop();
|
||||
const left = pending.pop();
|
||||
if (
|
||||
typeof left !== "object" ||
|
||||
left === null ||
|
||||
typeof right !== "object" ||
|
||||
right === null
|
||||
) {
|
||||
if (!Object.is(left, right)) return false;
|
||||
continue;
|
||||
}
|
||||
if (
|
||||
isProxy(right) ||
|
||||
Object.getPrototypeOf(left) !== Object.getPrototypeOf(right)
|
||||
)
|
||||
return false;
|
||||
const previous = compared.get(left);
|
||||
if (previous !== undefined) {
|
||||
if (previous !== right) return false;
|
||||
continue;
|
||||
}
|
||||
if (sources.has(right)) return false;
|
||||
// Preserve sharing in both directions, including descendants of an
|
||||
// identical subtree that may also appear in a separately parsed field.
|
||||
compared.set(left, right);
|
||||
sources.set(right, left);
|
||||
const keys = Reflect.ownKeys(left);
|
||||
if (keys.length !== Reflect.ownKeys(right).length) return false;
|
||||
for (const key of keys) {
|
||||
// A parser may drop hidden fields or normalize enumerability. Generic
|
||||
// deep equality omits these differences and can borrow unparsed data.
|
||||
if (typeof key !== "string") return false;
|
||||
const a = Object.getOwnPropertyDescriptor(left, key);
|
||||
const b = Object.getOwnPropertyDescriptor(right, key);
|
||||
if (
|
||||
a === undefined ||
|
||||
b === undefined ||
|
||||
!Object.hasOwn(a, "value") ||
|
||||
!Object.hasOwn(b, "value") ||
|
||||
a.enumerable !== b.enumerable
|
||||
)
|
||||
return false;
|
||||
pending.push(a.value as unknown, b.value as unknown);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
/** Seal an already parsed, owned JSON snapshot for immutable zero-copy reuse. */
|
||||
export const freezeJsonSnapshot = <T extends JsonValue>(value: T): T => {
|
||||
const pending: SnapshotEntry[] = [{ value, exiting: false }];
|
||||
@@ -104,6 +187,8 @@ export function* freezeOwnedJsonSnapshotSteps<T extends JsonValue>(
|
||||
}
|
||||
|
||||
const pushSnapshotChildren = (item: object, pending: SnapshotEntry[]): void => {
|
||||
if (isProxy(item))
|
||||
throw new TypeError("Immutable JSON snapshots cannot contain proxies");
|
||||
const prototype: unknown = Object.getPrototypeOf(item);
|
||||
if (
|
||||
!Array.isArray(item) &&
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import {
|
||||
freezeOwnedJsonSnapshotSteps,
|
||||
immutableJsonSnapshotMember,
|
||||
isImmutableJsonSnapshot,
|
||||
} from "./immutableJson.js";
|
||||
import { jsonValueValidationIssue, type JsonValue } from "./jsonValue.js";
|
||||
@@ -23,6 +24,7 @@ describe("cooperative owned snapshot sealing", () => {
|
||||
expect(steps.next().done).toBe(false);
|
||||
expect(Object.isFrozen(value)).toBe(true);
|
||||
expect(isImmutableJsonSnapshot(value)).toBe(false);
|
||||
expect(immutableJsonSnapshotMember(value, "deferred")).toBeUndefined();
|
||||
const introduced = { value: 2 };
|
||||
deferred.child = introduced;
|
||||
expect(complete(steps)).toBe(value);
|
||||
@@ -49,6 +51,7 @@ describe("cooperative owned snapshot sealing", () => {
|
||||
expect(interrupted.next().done).toBe(false);
|
||||
interrupted.return(cancelled);
|
||||
expect(isImmutableJsonSnapshot(cancelled)).toBe(false);
|
||||
expect(immutableJsonSnapshotMember(cancelled, "values")).toBeUndefined();
|
||||
});
|
||||
|
||||
it("preserves aliases and rejects accessors, sparse values and cycles", () => {
|
||||
@@ -80,4 +83,26 @@ describe("cooperative owned snapshot sealing", () => {
|
||||
value.child.value = Number.NaN;
|
||||
expect(jsonValueValidationIssue(value)).toBe("JSON numbers must be finite");
|
||||
});
|
||||
|
||||
it("never authenticates roots containing proxies or invokes their traps", () => {
|
||||
let reflected = false;
|
||||
const proxy = new Proxy(
|
||||
{ value: 1 },
|
||||
{
|
||||
getPrototypeOf() {
|
||||
reflected = true;
|
||||
throw new Error("must not reflect on a proxy");
|
||||
},
|
||||
},
|
||||
);
|
||||
const root = { child: proxy };
|
||||
for (const input of [proxy, root]) {
|
||||
expect(() => complete(freezeOwnedJsonSnapshotSteps(input))).toThrow(
|
||||
"proxies",
|
||||
);
|
||||
expect(isImmutableJsonSnapshot(input)).toBe(false);
|
||||
expect(immutableJsonSnapshotMember(input, "child")).toBeUndefined();
|
||||
}
|
||||
expect(reflected).toBe(false);
|
||||
});
|
||||
});
|
||||
@@ -1,4 +1,5 @@
|
||||
import { z } from "zod";
|
||||
import { canBorrowParsedJsonSnapshot } from "../immutableJson.js";
|
||||
|
||||
import {
|
||||
javascriptApplicationGraphSchema,
|
||||
@@ -172,21 +173,43 @@ const ownedAnalysisResultShape = applicationAnalysisResultShape.extend({
|
||||
),
|
||||
});
|
||||
|
||||
// One successful interpretation per exact immutable source. Weak keys retain
|
||||
// neither a second graph nor a source beyond its Evidence lifetime.
|
||||
const validatedImmutableResults = new WeakSet<object>();
|
||||
const isValidatedImmutableResult = (
|
||||
input: unknown,
|
||||
): input is JavaScriptApplicationAnalysisResult =>
|
||||
typeof input === "object" &&
|
||||
input !== null &&
|
||||
validatedImmutableResults.has(input);
|
||||
|
||||
/** Reuse exact owned graphs while validating metadata and cross-graph bindings. */
|
||||
export function* parseOwnedJavaScriptApplicationAnalysisSteps(
|
||||
input: unknown,
|
||||
): Generator<void, JavaScriptApplicationAnalysisResult> {
|
||||
if (isValidatedImmutableResult(input)) return input;
|
||||
const prepared = ownedAnalysisResultShape.safeParse(input);
|
||||
if (!prepared.success)
|
||||
return javascriptApplicationAnalysisResultSchema.parse(input);
|
||||
const issues: BindingIssue[] = [];
|
||||
yield* checkApplicationBindingsSteps(prepared.data, {
|
||||
addIssue: (issue) => {
|
||||
issues.push(issue);
|
||||
},
|
||||
});
|
||||
if (issues.length > 0) throw new z.ZodError(issues);
|
||||
return prepared.data;
|
||||
if (prepared.success) {
|
||||
const issues: BindingIssue[] = [];
|
||||
yield* checkApplicationBindingsSteps(prepared.data, {
|
||||
addIssue: (issue) => {
|
||||
issues.push(issue);
|
||||
},
|
||||
});
|
||||
if (issues.length > 0) throw new z.ZodError(issues);
|
||||
// Keep this existing fast path and its metadata normalization. Rewalking
|
||||
// already owned graphs just to borrow the wrapper would add full-graph work.
|
||||
return prepared.data;
|
||||
}
|
||||
const result = javascriptApplicationAnalysisResultSchema.parse(input);
|
||||
// Borrow only if parsing preserved the exact representation. A future schema
|
||||
// default/normalization must still return its parsed value, without caching.
|
||||
// Admission follows full validation, including completion of yielding checks.
|
||||
if (canBorrowParsedJsonSnapshot(input, result)) {
|
||||
validatedImmutableResults.add(input);
|
||||
return input;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Parsed public JavaScript application analysis request. */
|
||||
|
||||
@@ -56,6 +56,101 @@ const issuesFrom = (action: () => unknown) => {
|
||||
throw new Error("Expected complete result validation to reject the input");
|
||||
};
|
||||
|
||||
describe("immutable JavaScript result reuse", () => {
|
||||
it("preserves parser copies shared across owned graphs and result metadata", () => {
|
||||
const owned = ownedExample();
|
||||
const input = freezeJsonSnapshot({
|
||||
...owned,
|
||||
limitations: owned.graph.limitations,
|
||||
});
|
||||
for (let repeat = 0; repeat < 2; repeat += 1) {
|
||||
const parsed = complete(
|
||||
parseOwnedJavaScriptApplicationAnalysisSteps(input),
|
||||
);
|
||||
expect(parsed).not.toBe(input);
|
||||
expect(parsed.graph).toBe(input.graph);
|
||||
expect(parsed.limitations).toEqual(input.graph.limitations);
|
||||
expect(parsed.limitations).not.toBe(parsed.graph.limitations);
|
||||
}
|
||||
});
|
||||
|
||||
it.each(["hidden-extra", "changed-enumerability"])(
|
||||
"preserves parsed results when the source has %s normalization",
|
||||
(kind) => {
|
||||
const input = example();
|
||||
if (kind === "hidden-extra")
|
||||
Object.defineProperty(input, "not_in_parsed_result", {
|
||||
value: { unvalidated: true },
|
||||
enumerable: false,
|
||||
});
|
||||
else Object.defineProperty(input, "input_path", { enumerable: false });
|
||||
freezeJsonSnapshot(input);
|
||||
const expected = javascriptApplicationAnalysisResultSchema.parse(input);
|
||||
for (let repeat = 0; repeat < 2; repeat += 1) {
|
||||
const parsed = complete(
|
||||
parseOwnedJavaScriptApplicationAnalysisSteps(input),
|
||||
);
|
||||
expect(parsed).not.toBe(input);
|
||||
expect(Object.hasOwn(parsed, "not_in_parsed_result")).toBe(false);
|
||||
expect(
|
||||
Object.getOwnPropertyDescriptor(parsed, "input_path")?.enumerable,
|
||||
).toBe(
|
||||
Object.getOwnPropertyDescriptor(expected, "input_path")?.enumerable,
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
it("borrows imported immutable results only after complete format validation", () => {
|
||||
const input = example();
|
||||
delete input.statistics.skipped_javascript_files;
|
||||
freezeJsonSnapshot(input);
|
||||
const result = complete(
|
||||
parseOwnedJavaScriptApplicationAnalysisSteps(input),
|
||||
);
|
||||
expect(result).toBe(input);
|
||||
expect(result).toEqual(
|
||||
javascriptApplicationAnalysisResultSchema.parse(input),
|
||||
);
|
||||
expect(result.statistics).not.toHaveProperty("skipped_javascript_files");
|
||||
expect(complete(parseOwnedJavaScriptApplicationAnalysisSteps(input))).toBe(
|
||||
input,
|
||||
);
|
||||
expect(Reflect.set(input.statistics, "findings", -1)).toBe(false);
|
||||
});
|
||||
|
||||
it("preserves a normalized representation instead of borrowing changed input", () => {
|
||||
const input = example();
|
||||
Object.setPrototypeOf(input, null);
|
||||
freezeJsonSnapshot(input);
|
||||
for (let repeat = 0; repeat < 2; repeat += 1) {
|
||||
const result = complete(
|
||||
parseOwnedJavaScriptApplicationAnalysisSteps(input),
|
||||
);
|
||||
expect(result).not.toBe(input);
|
||||
expect(Object.getPrototypeOf(result)).toBe(Object.prototype);
|
||||
expect(result).toEqual(
|
||||
javascriptApplicationAnalysisResultSchema.parse(input),
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
it.each(["mutable", "shallow-frozen"])(
|
||||
"rechecks changed %s metadata despite authenticated nested graphs",
|
||||
(kind) => {
|
||||
const input = ownedExample();
|
||||
if (kind === "shallow-frozen") Object.freeze(input);
|
||||
expect(
|
||||
complete(parseOwnedJavaScriptApplicationAnalysisSteps(input)),
|
||||
).not.toBe(input);
|
||||
input.statistics.findings = -1;
|
||||
expect(() =>
|
||||
complete(parseOwnedJavaScriptApplicationAnalysisSteps(input)),
|
||||
).toThrow(z.ZodError);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
describe("owned JavaScript graph validation", () => {
|
||||
it("reuses exact sealed graphs and preserves all public result fields", () => {
|
||||
const input = ownedExample();
|
||||
@@ -239,6 +334,7 @@ describe("owned JavaScript result bindings", () => {
|
||||
input.semantic_graph = complete(
|
||||
createImmutableJavaScriptSemanticGraphSteps(semantic),
|
||||
);
|
||||
freezeJsonSnapshot(input);
|
||||
const steps = parseOwnedJavaScriptApplicationAnalysisSteps(input);
|
||||
expect(steps.next().done).toBe(false);
|
||||
steps.return(input);
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
import { expect, it } from "vitest";
|
||||
import { binaryLayoutSchema, type BinaryLayout } from "./binaryLayout.js";
|
||||
import {
|
||||
binaryLayoutSchema,
|
||||
safeParseOwnedBinaryLayout,
|
||||
type BinaryLayout,
|
||||
} from "./binaryLayout.js";
|
||||
import { freezeJsonSnapshot } from "../immutableJson.js";
|
||||
|
||||
const report = () => ({
|
||||
artifact: { path: "/fixture.elf", sha256: "a".repeat(64), bytes: 128 },
|
||||
@@ -60,6 +65,55 @@ const report = () => ({
|
||||
limitations: [],
|
||||
});
|
||||
|
||||
it("reuses an exact validated immutable layout without a second retained copy", () => {
|
||||
const value = freezeJsonSnapshot(report());
|
||||
const parsed = safeParseOwnedBinaryLayout(value);
|
||||
expect(parsed.success).toBe(true);
|
||||
if (!parsed.success) throw parsed.error;
|
||||
expect(parsed.data).toBe(value);
|
||||
expect(safeParseOwnedBinaryLayout(value)).toEqual(parsed);
|
||||
expect(parsed.data).toEqual(binaryLayoutSchema.parse(value));
|
||||
expect(Reflect.set(value.sections[0]!, "index", 5)).toBe(false);
|
||||
});
|
||||
|
||||
it.each(["mutable", "shallow-frozen"])(
|
||||
"revalidates a changed %s layout instead of caching its former validity",
|
||||
(kind) => {
|
||||
const value = report();
|
||||
if (kind === "shallow-frozen") Object.freeze(value);
|
||||
const first = safeParseOwnedBinaryLayout(value);
|
||||
if (!first.success) throw first.error;
|
||||
expect(first.data).not.toBe(value);
|
||||
value.sections[0]!.header_location.offset = "0x80";
|
||||
const invalid = safeParseOwnedBinaryLayout(value);
|
||||
expect(invalid.success).toBe(false);
|
||||
expect(invalid).toEqual(binaryLayoutSchema.safeParse(value));
|
||||
},
|
||||
);
|
||||
|
||||
it("never treats sealing as format validity or caches a refused layout", () => {
|
||||
const value = report();
|
||||
value.sections[0]!.index = 10;
|
||||
freezeJsonSnapshot(value);
|
||||
expect(safeParseOwnedBinaryLayout(value).success).toBe(false);
|
||||
expect(safeParseOwnedBinaryLayout(value).success).toBe(false);
|
||||
});
|
||||
|
||||
it("does not borrow hidden fields that the format parser removed", () => {
|
||||
const value = report();
|
||||
Object.defineProperty(value, "not_in_parsed_result", {
|
||||
value: { unvalidated: true },
|
||||
enumerable: false,
|
||||
});
|
||||
freezeJsonSnapshot(value);
|
||||
for (let repeat = 0; repeat < 2; repeat += 1) {
|
||||
const parsed = safeParseOwnedBinaryLayout(value);
|
||||
if (!parsed.success) throw parsed.error;
|
||||
expect(parsed.data).not.toBe(value);
|
||||
expect(Object.hasOwn(parsed.data, "not_in_parsed_result")).toBe(false);
|
||||
}
|
||||
});
|
||||
|
||||
it("retains high linked values and NOBITS without inventing file backing", () => {
|
||||
const value = binaryLayoutSchema.parse(report());
|
||||
expect(value.entry_point.reported_value).toBe("0x20000000000001");
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { z } from "zod";
|
||||
import { canBorrowParsedJsonSnapshot } from "../immutableJson.js";
|
||||
|
||||
import { localPathStringSchema } from "../localPath.js";
|
||||
import { CANONICAL_BASE64_PATTERN } from "../stringPatterns.js";
|
||||
@@ -316,6 +317,28 @@ export type InspectBinaryLayoutInput = z.infer<
|
||||
>;
|
||||
export type BinaryLayout = z.infer<typeof binaryLayoutSchema>;
|
||||
|
||||
const validatedImmutableLayouts = new WeakSet<object>();
|
||||
|
||||
/** Validate once per exact immutable source; mutable inputs always revalidate. */
|
||||
export const safeParseOwnedBinaryLayout = (
|
||||
input: unknown,
|
||||
): z.ZodSafeParseResult<BinaryLayout> => {
|
||||
if (
|
||||
typeof input === "object" &&
|
||||
input !== null &&
|
||||
validatedImmutableLayouts.has(input)
|
||||
)
|
||||
return { success: true, data: input as BinaryLayout };
|
||||
const parsed = binaryLayoutSchema.safeParse(input);
|
||||
if (parsed.success && canBorrowParsedJsonSnapshot(input, parsed.data)) {
|
||||
// Preserve normalization if the schema changes: only unchanged, completely
|
||||
// validated data can be borrowed without retaining a duplicate layout.
|
||||
validatedImmutableLayouts.add(input);
|
||||
return { success: true, data: input };
|
||||
}
|
||||
return parsed;
|
||||
};
|
||||
|
||||
const validateRelocationReferences = (
|
||||
value: z.output<typeof binaryLayoutObjectSchema>,
|
||||
context: z.RefinementCtx,
|
||||
|
||||
Reference in new issue
Block a user