[PR #1730] [MERGED] fix(bun): report the real runtime and enforce worker memory budgets on Windows #4779

Closed
opened 2026-10-31 00:54:58 +00:00 by bsakaguchi · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/morluto/rea/pull/1730
Author: @xeropresence
Created: 10/11/2026
Status: ✅ Merged
Merged: 10/11/2026
Merged by: @morluto

Base: main ← Head: fix/bun-runtime-limits


📝 Commits (2)

  • 2480121 fix(bun): report the real runtime and enforce worker memory budgets on Windows
  • 169ca86 fix(windows): drain memory notifications after observing exit

📊 Changes

48 files changed (+1630 additions, -196 deletions)

View changed files

📝 .agents/skills/reverse-engineer-anything/references/javascript-applications.md (+4 -1)
📝 docs/installation.md (+12 -0)
📝 docs/javascript-artifact-reconstruction.md (+8 -1)
📝 docs/testing.md (+3 -1)
📝 docs/web-network-captures.md (+6 -0)
📝 docs/web-source-location.md (+12 -3)
📝 native/windows/README.md (+24 -1)
📝 native/windows/src/authority.hpp (+5 -1)
📝 native/windows/src/process.cc (+88 -9)
➕ scripts/lib/windows-job-memory-limit.mjs (+180 -0)
📝 scripts/lib/windows-protocol-input.mjs (+1 -1)
📝 scripts/verify-windows-native.mjs (+5 -0)
📝 src/application/Doctor.fixture.ts (+1 -1)
📝 src/application/Doctor.test.ts (+53 -15)
📝 src/application/Doctor.ts (+6 -2)
📝 src/application/DoctorDiagnostics.ts (+28 -3)
📝 src/application/WebSourceLocationService.test.ts (+46 -0)
📝 src/application/WebSourceLocationService.ts (+18 -3)
📝 src/browser/history/CaptureFailures.test.ts (+94 -0)
📝 src/browser/history/CaptureFailures.ts (+27 -3)

...and 28 more files

📄 Description

Summary

When REA runs on Bun, it describes a Node.js/V8 runtime it did not observe, and its JavaScript helper processes run with no memory bound. Bun reports process.versions.node (its Node.js compatibility version, e.g. 26.3.0) and V8-shaped getHeapStatistics() values, but it is not V8 and ignores V8 flags such as --max-old-space-size. REA relies on those flags to bound three helpers it starts on process.execPath: the JavaScript source analyzer (--max-old-space-size=<max_heap_mb>, default 1024), the source-map codec (192 MiB) and the HAR decoder (192 MiB old generation).

This PR:

  • Identifies the runtime once from process.versions (only Bun sets process.versions.bun) and records it truthfully. Under Bun, trace_web_source_location now works instead of always failing, and Evidence reports unenforced V8 heap limits as null (or omits them) instead of inventing numbers. rea doctor says it is running on Bun.
  • On Windows under Bun, gives each helper's owned Job Object a commit limit of its heap budget plus a 512 MiB Bun runtime reserve, and reports both the limit and reaching it. Reaching it returns the same memory resource_constraint failure Node's V8 out-of-memory path returns.

Node.js launches and outputs are unchanged, apart from a new details object on the doctor node check.

No issue is linked; this was found while running REA under Bun on Windows.

Problem and expected behavior

Smallest triggers, all under Bun 1.4.2 on Windows x64:

  1. rea trace-web-source-location <manifest> 0 <map> <url> 1 0 --json always failed. The codec reported runtime: { id: "node-v8", version: process.version, v8_heap_limit_bytes: getHeapStatistics().heap_size_limit }. Bun's synthetic heap limit (~470 MB) is above the decoder's 256 MiB check, so the decoder returned AnalysisOutputError: Codec reported a V8 heap limit exceeding its independent process budget. Even had it passed, the Evidence would claim a V8 runtime and an enforced heap limit that did not exist.
  2. JavaScript analysis resource failures put worker_old_space_mb and worker_heap_limit_bytes (Bun's heap_size_limit) into reported_limits and coverage limits. Bun enforces neither.
  3. inspect_web_network_capture limitations said "HAR uses a 192 MiB Node old-generation heap", and HAR memory failures reported old_generation_heap_mib: 192. Bun does not enforce that heap.
  4. rea doctor --json reported {"name":"node","detail":"26.3.0"} with no sign that the runtime was Bun.
  5. The helpers were unbounded. Uncapped measurements: analyzing a 1.7 MB bundle (@vue/compiler-sfc browser build) committed up to 50,537 MiB and then failed with a generic ProviderAdapterError. A 34,098-entry HAR, which exhausts the 192 MiB heap on Node.js, committed 1,026 MiB.

Expected: REA records the runtime it actually ran on, reports an unenforced limit as unavailable rather than as a number, and bounds each helper by the memory budget it already declares wherever the platform allows it.

Change and scope

Runtime identity

  • src/domain/runtimeVersion.ts: adds JavaScriptRuntime and javaScriptRuntime(process.versions), the single place that decides between Node.js and Bun. Adapters pass process.versions; the domain module does not read process.

  • Source-map trace (TraceSourceMap.ts, webSourceLocation.ts, SourceMapDecoder.ts, WebSourceLocationService.ts): runtime is a discriminated union:

    • { id: "node-v8", version, v8_heap_limit_bytes: <int> } (unchanged on Node.js)
    • { id: "bun", version, node_compatibility_version, v8_heap_limit_bytes: null, process_memory_limit_bytes: <int> | null }

    The decoder applies the 256 MiB check only to V8 reports. It rejects a report whose runtime differs from the runtime that launched the codec (the codec always runs on process.execPath; without this check a bun report would bypass the heap check), and a Bun report whose process_memory_limit_bytes differs from the limit its launcher enforced. The final limitation states the budget that actually applied.

  • JavaScript analysis worker: the ready handshake's heap_limit_bytes is nullable; the worker server sends null under Bun and the parent rejects a handshake that contradicts its own runtime. Under Bun, worker_heap_limit_bytes is null and worker_old_space_mb is not reported. javaScriptAnalysisCoverageLimits already drops non-numeric values, so coverage omits the heap limit.

  • HAR decoder (CaptureRelease.ts, CaptureFailures.ts, HistoricalCaptureDecoder.ts): HAR_CAPTURE_HEAP_ENFORCED gates the heap sizes in limitations, reported_limits and remediation.

  • Doctor: DoctorHost.nodeVersion is replaced by runtime: JavaScriptRuntime. The node check keeps its name, because DoctorScope and scripts/verify-package-discovery.mjs select it by name. It carries structured details: { runtime: "node", version }, or { runtime: "bun", version, node_compatibility_version } with detail Bun 1.4.2 (Node.js 26.3.0 compatibility). Under Bun the check passes when the Node.js compatibility version satisfies SUPPORTED_NODE_VERSION_RANGE, the same version setup already gates on (SetupHost uses process.versions.node); the Windows native addon already supports Bun as a Node-API host (#1531).

Audit of src for process.version, process.versions, node-v8, getHeapStatistics, heap_size_limit, nodeVersion and max-old-space-size:

Location Kind Action
TraceSourceMap.ts runtime record; SourceMapDecoder.ts heap check Evidence claim plus a check that depends on it Fixed
WebSourceLocationService.ts limitation "192 MiB old-generation heap and one V8 worker" Evidence claim Fixed
JavaScriptAnalysisWorkerServer.ts ready heap_limit_bytes; JavaScriptAnalysisWorker.ts #limits() Output claim (error details, coverage limits) Fixed
CaptureFailures.ts, HistoricalCaptureDecoder.ts HAR heap sizes Output claim (limitations, error details, remediation) Fixed
Doctor.ts / DoctorDiagnostics.ts nodeVersion Output claim Fixed
JavaScriptAnalysisWorker.ts parent result budget (heap_size_limit - heapUsed), checkpoint admission; JavaScriptAnalysisWorkerServer.ts GC trigger at half heap_size_limit Internal admission math Unchanged. Under Bun these use Bun's reported statistics, which describe no enforced limit. parent_result_budget_bytes is a derived admission budget, not a runtime claim, so it is still reported.
JsonInputFile.ts native-parse choice and assembly admission (total_available_size) Internal admission math; error details echo its inputs Unchanged. Under Bun total_available_size is ~33.6 GB, so these refusals are practically unreachable there.
--max-old-space-size etc. passed to the codec, HAR decoder, EVM worker and analysis worker Process arguments Unchanged. Under Bun on Windows the Job Object limit below bounds the codec, HAR decoder and analyzer instead. The EVM worker keeps its Linux prlimit cap and does not report its 256 MiB value.
NativeModuleResolver.ts --js-flags=--max-old-space-size=128 Chromium flag Unrelated to the host runtime
JadxSession.ts heap_limit_mib; WindowsNativeLoader.ts process.versions.napi JVM value; Node-API admission Unrelated

Job Object memory limit (Windows, Bun only)

Native addon (native/windows/src/process.cc, authority.hpp):

  • process_spawn accepts an optional 6th argument: the job memory limit in bytes. It must be a positive safe integer and a whole number of pages; anything else fails with ERROR_INVALID_PARAMETER before any process is created. Existing 4- and 5-argument calls are unchanged.
  • createJob(limit) sets JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE | JOB_OBJECT_LIMIT_JOB_MEMORY and JobMemoryLimit, then reads the policy back and requires the exact flags and exact limit. The page-alignment rule exists so the read-back can be exact, because the kernel stores the limit in pages.
  • For a limited job, a private I/O completion port is associated with the job before CreateProcessW. Each process_poll drains it without waiting and records a sticky memoryLimitReached when JOB_OBJECT_MSG_JOB_MEMORY_LIMIT (10) arrives. The drain runs before the exit observation, so an exit caused by a failed allocation is never reported without the notification. A limited job's poll also returns peakJobMemoryBytes (PeakJobMemoryUsed). An unlimited job returns neither field, so the poll shape for existing launches is unchanged. The spawn result echoes jobMemoryLimitBytes. process_close releases the port.
  • inspect() creates a 64 MiB-limited probe job with a port, then reports jobMemoryLimit: true.

TypeScript plumbing:

  • WindowsNativeLoader: jobMemoryLimit: z.literal(true).optional(); optional because artifacts built before this change omit it.
  • WindowsOwnedProcess: the trailing options are one object { protocolStdin, memoryLimitBytes, authority }; the only other caller (scripts/lib/windows-protocol-input.mjs) is migrated. A requested limit fails closed: an artifact without jobMemoryLimit refuses the launch instead of starting an unlimited process. The spawn read-back must equal the request. A limited job's poll is parsed with a strict schema that requires both new fields, and an unlimited job's poll rejects them. memoryLimitReached is set before exit is emitted.
  • ProviderProcess: OwnedProviderProcessSpawnOptions.memoryLimitBytes is honored only by the Windows owned-job launcher; every other launcher throws instead of ignoring it. memoryLimitReached is passed through to ProviderProcessSnapshot, and runOwnedCommand carries it via the spawn options.

Policy (src/process/JavaScriptWorkerMemoryLimit.ts, the single place this is decided): javaScriptWorkerMemoryLimitBytes(heapBudgetMiB) returns heapBudgetMiB × 1 MiB + BUN_WORKER_RUNTIME_RESERVE_BYTES only when platform === "win32" and the runtime is Bun; otherwise null. Every call site spreads memoryLimitBytes and adds the new reporting fields only when it is non-null, so Node.js launch arguments, the native call, the poll schema, Evidence and limitations are unchanged by construction.

Reserve derivation (BUN_WORKER_RUNTIME_RESERVE_BYTES = 512 MiB): bun -e 1 peaks at 52 MiB of job commit, but REA's workers commit far more before doing real work, because JavaScriptCore commits parsed and compiled module code. Peak job commit on trivial inputs, 5–8 uncapped runs each, on a 32-logical-processor host:

Worker Peak job commit (trivial input)
Source-map codec 278–368 MiB
HAR decoder 300–345 MiB
Analysis worker 321–346 MiB

512 MiB covers the largest observed baseline with about 40% margin. Resulting limits: 704 MiB for the codec and HAR decoder, and max_heap_mb + 512 MiB for the analyzer (1536 MiB by default).

Classification relies on the job notification, not on exit status, because Bun reports a reached limit in several ways: exit 3 (panic), exit 1 (RangeError: Out of memory), or 0xC0000409 (observed for the HAR decoder).

Helper Result when the limit is reached Reported limit
Analyzer AnalysisResourceConstraintError(…, "memory", …) from every path (exit, broken pipe or truncated frame, worker-caught exception, post-loop check). Completed static facts are retained, and the next source starts a fresh worker. worker_heap_limit_bytes: null plus worker_process_memory_limit_bytes, which is also a coverage limit
Source-map codec Memory AnalysisResourceConstraintError; no partial result codec_process_memory_limit_bytes; Evidence runtime.process_memory_limit_bytes
HAR decoder captureMemoryFailure from both the failed-exit path and a success path whose job reached the limit process_memory_limit_bytes, plus a remediation sentence

Not included: memory enforcement for Bun on Linux and macOS (they keep null / no-limit reporting), EVM (already prlimit-capped), Bun support in installers or rea setup/rea update, and changes to the rea doctor remediation wording.

Docs: installation.md, web-source-location.md, javascript-artifact-reconstruction.md, web-network-captures.md, testing.md, native/windows/README.md, and the skill reference javascript-applications.md.

Contract and boundary impact

  • Semantic owner and earliest changed stage: src/domain/runtimeVersion.ts (runtime identity) and src/process/JavaScriptWorkerMemoryLimit.ts (limit policy); the native process_spawn/process_poll boundary, then WindowsOwnedProcess, then spawnOwnedProviderProcess. Producers: codec process, analysis worker server, HAR adapter, doctor host.
  • CLI and MCP/tool-catalog contract: the trace_web_source_location output schema's normalized_result.runtime changes from { id: string, version: string, v8_heap_limit_bytes: int } to the two-variant union above; Node.js output is byte-identical. The max_heap_mb / --max-heap-mb description states that Bun ignores V8 heap flags and that Windows limits committed memory instead. rea doctor --json's node check gains details; on Node.js, detail is unchanged.
  • Provider, bridge, target-format, or platform compatibility: Node.js behavior is unchanged. Under Bun, trace_web_source_location works instead of always failing. Under Bun on Windows the helpers require the matching native artifact; an older artifact refuses the launch with "does not support Job Object memory limits" instead of running unlimited.
  • Evidence, artifact, provenance, or reconstruction contract: source-map Evidence records the runtime that ran the codec. Unenforced heap limits are null or omitted instead of fabricated. Enforced Job Object limits are reported (worker_process_memory_limit_bytes, codec_process_memory_limit_bytes, process_memory_limit_bytes) and limitations state which budget applied.
  • Process execution, authorization, cleanup, or containment impact: on Windows under Bun, the helpers' owned jobs gain a commit limit and a private completion port whose lifetime matches the job's. Kill-on-close, breakaway denial and atomic job assignment are unchanged and still verified. New consistency checks reject codec and worker reports from a runtime other than the one REA launched, and codec reports of a limit other than the enforced one.
  • Generated metadata (docs/public/product-catalog.json), package, or installation impact: ignored catalogs and skills/ regenerate from the changed schema, description and skill reference; no committed generated files changed (docs:check:built and mcp-catalog:check:built pass). native/windows/build is rebuilt by CI.

Evidence and regression coverage

  • Tests added or updated:

    Test file What it covers
    src/domain/runtimeVersion.test.ts Identifies Node.js and Bun from process.versions-shaped input.
    src/application/Doctor.test.ts node check details for Node.js; Bun passes with a supported compatibility version (26.3.0) and fails with an unsupported one (25.1.0). Doctor.fixture.ts and GhidraDoctor.test.ts move to the runtime host field.
    src/process/JavaScriptWorkerMemoryLimit.test.ts Policy: Bun+win32 → 704 MiB / 1536 MiB; Node.js on win32 and Linux → none; Bun on Linux and macOS → none.
    src/windows/WindowsOwnedProcess.test.ts Refuses a limit without the capability, before any native call; unlimited launch keeps 4 arguments and rejects unrequested limit fields; limited launch passes [false, limit], verifies the read-back and sets memoryLimitReached before exit; rejects a limited poll missing the fields and a mismatched applied limit.
    src/windows/WindowsNativeLoader.test.ts jobMemoryLimit admitted as true, absent on earlier artifacts, false rejected.
    src/process/ProviderProcessOutput.test.ts Snapshots carry memoryLimitReached only when the handle observed it.
    src/browser/history/CaptureFailures.test.ts Heap sizes in reported_limits only on Node.js; a reached limit after a Bun panic (exit 3) or exception (exit 1) → memory resource_constraint with process_memory_limit_bytes; the same exits without the notification stay execution_failure.
    src/application/WebSourceLocationService.test.ts Limitation sentence for Node V8, Bun with a Job Object limit, and Bun without one.
    tests/boundary/process/providerProcess.test.ts A non-Windows launcher refuses a limit.
    tests/boundary/javascript/sourceMapDecoder.test.ts The real codec's runtime record for whichever runtime runs the test; replaying the real report succeeds and the other runtime's record is rejected; classification by the notification; a mismatched reported limit is rejected.
    tests/boundary/javascript/javascriptSourceAnalysisWorker.test.ts Heap limits reported only when V8 enforces them; a contradicting handshake is rejected; classification (memory vs. ProviderAdapterError) by the notification, using a real child.
    tests/acceptance/webSourceLocationCli.test.ts The real CLI/MCP run expects the runtime id of the executing runtime.
    tests/acceptance/tools/historicalCaptureResources.test.ts Node keeps the fixed-V8-heap expectations; Bun on Windows expects the Job Object failure; Bun elsewhere is skipped because no bound exists to reach.
    scripts/verify-windows-native.mjs + scripts/lib/windows-job-memory-limit.mjs Native conformance (CI, Node.js on windows-latest), listed below.

    scripts/lib/windows-job-memory-limit.mjs asserts: inspect().jobMemoryLimit === true; invalid limits (0, negative, fractional, 2**53, NaN, Infinity, non-page-aligned 4097, a string, null) are rejected with Win32 87; an unlimited poll has neither new field; a 128 MiB-limited child allocating external Buffer memory (which V8 flags cannot bound) exits nonzero without completing, reports memoryLimitReached, reaches activeProcesses === 0, and peaks within [limit − 64 MiB, limit + 32 MiB]; a control child under the same limit exits 0 without reaching it; the WindowsOwnedProcess wrapper reports the reached limit; a missing capability is refused. Measured peaks exceed the limit by a few MiB of kernel-charged commit (512→520, 256→262, 704→708 MiB), hence the tolerance.

  • Base reproduction or other evidence:

    • On base under Bun, three tests fail and pass with this change (see Validation): the real-codec sourceMapDecoder test (Codec reported a V8 heap limit exceeding its independent process budget.), webSourceLocationCli (exit 1), and the HAR exhaustion acceptance test (nothing bounded the decoder).
    • Throwaway probes outside the repo (a PowerShell P/Invoke job probe and a Bun FFI job helper), Bun 1.4.2: with a 512 MiB job limit Bun panicked (exit 3, peak 520 MiB); with 256 MiB it threw a JavaScript out-of-memory exception (exit 1, peak 262 MiB). In both cases the completion port received JOB_OBJECT_MSG_JOB_MEMORY_LIMIT (10), the runtime-independent signal this change relies on.
    • Before the native control was built, this change's TypeScript was run with a launcher that put the child into a nested FFI Job Object with the requested limit:
      Run Limit Peak Result
      Analyzer, 1.7 MB bundle 1536 MiB 1538.5 MiB Memory AnalysisResourceConstraintError, static facts retained
      Analyzer, next small file 1536 MiB 345 MiB Succeeded in a fresh worker
      HAR, 34,098 entries 704 MiB 707.8 MiB Exit 0xC0000409; memory resource_constraint with process_memory_limit_bytes: 738197504
      HAR, 2 entries 704 MiB 320 MiB Succeeded
      Codec, at its maximum input budgets 704 MiB 405 MiB Stopped at its 262144-record structural budget; the limit was never reached
      Codec, same input 300 MiB (applied on purpose) — Memory AnalysisResourceConstraintError
  • User-visible CLI/MCP output (Bun 1.4.2, Windows x64, built CLI with the CI-built native artifact):

    • bun scripts\rea.mjs doctor --json, node check:
      { "name": "node", "ok": true, "classification": "healthy",
        "detail": "Bun 1.4.2 (Node.js 26.3.0 compatibility)",
        "details": { "runtime": "bun", "version": "1.4.2", "node_compatibility_version": "26.3.0" } }
      
    • bun scripts\rea.mjs trace-web-source-location <manifest> 0 <map> "https://app.test/app.js.map?v=1#context" 1 0 --json (inputs written from tests/fixtures/webSourceLocation.ts) exits 0 with one match and:
      "runtime": { "id": "bun", "version": "1.4.2", "node_compatibility_version": "26.3.0", "v8_heap_limit_bytes": null, "process_memory_limit_bytes": 738197504 }
      
      The last limitation ends: "The codec ran on Bun, which does not enforce REA's V8 heap flags; a Windows Job Object limited its committed memory to 738197504 bytes, which is not a heap or RSS limit."
    • bun scripts\rea.mjs analyze-javascript-application <dir> --format json on a directory with the 1,732,994-byte @vue/compiler-sfc browser bundle (src/big.js) and a one-line module (src/small.js): exit 0 in about 5 seconds. src/big.js reports resource_constraint / memory, "reached its 1610612736-byte process memory limit while analyzing src/big.js during semantic", with "reported_limits":{"worker_heap_limit_bytes":null,"worker_process_memory_limit_bytes":1610612736,"worker_timeout_ms":300000}. src/small.js and its small export are still recovered by static analysis.
    • The same command with --max-heap-mb 128 on a two-file app whose main.js has 4000 imports reaches the parent result-budget path and exits 0 with partial coverage. The target.js failure reports "reported_limits":{"worker_heap_limit_bytes":null,"worker_process_memory_limit_bytes":671088640,"worker_timeout_ms":300000,"semantic_result_budget_bytes":0}; coverage limits are javascript_analysis_semantic_result_budget_bytes, javascript_analysis_worker_process_memory_limit_bytes, javascript_analysis_worker_timeout_ms and javascript_analysis_parent_result_budget_bytes, with no worker heap limit.
  • Remaining proof gaps:

    • Node.js branches were not run locally (no Node.js on this machine); CI covers them.
    • javascriptSourceAnalysisWorker.test.ts cannot run under Vitest on Bun: every test in the file, including the three existing ones on base, fails with [module runner] "import.meta.resolve" is not supported. Its Bun-side behavior is covered by the analyzer CLI runs above; CI runs the file under Node.js.
    • The codec cannot reach its real 704 MiB limit within its own input budgets, so its reached-limit path is proven only with a smaller applied limit and with an injected notification.
    • The 512 MiB reserve was measured on one 32-logical-processor host; Bun's baseline commit grows with thread count.

For evidence-bearing changes:

  • Observed, derived, and inferred claims remain distinguishable.
  • Artifact identity, source provenance, and failed attempts remain preserved.
  • Unsupported, incomplete, unavailable, or uncertain outcomes remain visible.

Validation performed

Local validation ran under Bun 1.4.2 on Windows x64 without Node.js installed. npm-based wrapper scripts cannot run here, so the underlying scripts were run with bun --bun. The native artifact built by this commit's CI was installed in the ignored native/windows/build; base runs used the artifact from the published rea-agents@6.4.0 package, because base's loader rejects the new jobMemoryLimit inspection key.

Command Result
bun --bun run compile; artifacts:skills, artifacts:mcp-catalog, artifacts:product-catalog, artifacts:managed-evidence, artifacts:error-schema Pass
bun --bun run typecheck Pass
bun --bun run lint Pass, 0 errors. No new warnings compared with base. Already-flagged functions grew slightly: CaptureFailures mapping (complexity 59→63), HistoricalCaptureDecoder#inspect (30→31), JavaScriptAnalysisWorker#analyze (47→48; file 710→756 lines), verify-windows-native.mjs (675→680 lines).
oxfmt --check on touched files Pass, on LF-normalized copies (the Windows checkout uses CRLF, which oxfmt flags in every file).
bun --bun run docs:check:built, bun --bun run mcp-catalog:check:built Pass
bun --bun run test:local -- on JavaScriptWorkerMemoryLimit, WindowsOwnedProcess, WindowsNativeLoader, ProviderProcessOutput, CaptureFailures, WebSourceLocationService, runtimeVersion, Doctor, GhidraDoctor 118 of 120 pass. The 2 failures are GhidraDoctor Windows-authority tests that fail identically on base.
bun scripts/verify-windows-native.mjs . (full native conformance, under Bun instead of Node.js) ok: true, no false control. processes.jobMemoryLimit: the 128 MiB-limited child exited 1 at a peak of 145,399,808 bytes with the limit reached; the control exited 0 at 56,586,240 bytes; invalid limits, unlimited poll shape, wrapper reporting and missing-capability refusal all true.
bun --bun node_modules/vitest/vitest.mjs run on src/application src/domain src/browser/history src/javascript src/ghidra src/process src/windows tests/boundary/javascript tests/boundary/process, the webSourceLocationMcp, javascriptAnalysisResources, outputSchemaSemanticConstraints and compactInputSchemaMcp MCP boundary tests, targetDiagnostics, and the webSourceLocationCli and historicalCaptureResources acceptance tests, on this branch and on base Base: 2986 passed, 113 failed. This branch: 3013 passed, 114 failed (28 more tests). Fixed by this change: the real-codec sourceMapDecoder test, webSourceLocationCli, and the HAR exhaustion acceptance test. Failing only on this branch: the 4 new tests in javascriptSourceAnalysisWorker.test.ts, which fail like the file's existing tests because Vitest's module runner on Bun does not support import.meta.resolve. Every other failure is the same test on both and environment-specific under Bun on Windows (e.g. that import.meta.resolve limitation, POSIX-mode process tests, Linux/macOS Hopper installers, Ghidra Windows authority).
Built-CLI smoke runs: doctor --json, trace-web-source-location, analyze-javascript-application (twice) Output shown above.

CI on this commit (fork push run, Node.js on GitHub-hosted runners):

  • Build bundled Windows x64 native controls (MinGW build and artifact verification): passed. Its rea-windows-x64.node SHA-256 is 5fa70a01…11aac, byte-identical to the artifact used in the local runs above.
  • Windows capability and package lane (npm run verify:windows-native on windows-latest, including the jobMemoryLimit controls): passed.
  • Static checks, Build package, Docs Generation, all four test shards, Test (coverage + timing), all four native package baselines, the Windows NativeAOT fixture, and the real browser, web runtime, web source map, web network, JavaScript recovery, EVM, binary layout, recorded crash and Apple artifact lanes: passed (29 jobs).
  • Real Apple artifact CLI/MCP E2E (native-regressions) failed in "Run native Apple and process boundary regressions", which also fails the CI required gate. The same step failed on upstream main at this PR's base 966dc3ac (morluto/rea run 38116233537), and upstream has since changed its macOS lanes (3f4ea321, "ci: reduce macOS runner contention"). On macOS this change only adds the memoryLimitReached pass-through and the refusal of a requested limit, which the policy never requests outside Windows.

Compatibility, safety, and release

  • Breaking changes or migration steps: none for Node.js; the only Node.js-visible change is the new details object on the doctor node check. Consumers validating trace_web_source_location against the old schema could not have received Bun output before (it always failed). Bun-on-Windows users need the matching package artifact; an older artifact refuses the helper launch instead of running it unbounded.
  • Real Hopper/Ghidra, browser, or OS coverage: Windows x64 under Bun locally; native code built and verified by CI's Windows lanes. No browser, Hopper or Ghidra workflows involved.
  • Package or release metadata impact: rebuilt native/windows/build artifact.
  • Security, privacy, process, or containment review: the completion port is private and not inheritable, the handle list still restricts inheritance, and closing a limited job also closes its port. Job membership, kill-on-close and breakaway checks are unchanged. No new file access.

Review checklist

  • This PR addresses a concrete problem or an agreed enhancement.
  • The PR has one focused outcome and the title follows type(scope): outcome.
  • Related issue is linked, or the reason for not linking one is stated above.
  • Tests cover changed observable behavior and meaningful failure paths.
  • Owning docs, contracts, and generated metadata are updated where needed.
  • User-visible CLI/MCP changes include representative output.
  • I checked the final diff for secrets, unrelated cleanup, and unsupported claims.

🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/morluto/rea/pull/1730 **Author:** [@xeropresence](https://github.com/xeropresence) **Created:** 10/11/2026 **Status:** ✅ Merged **Merged:** 10/11/2026 **Merged by:** [@morluto](https://github.com/morluto) **Base:** `main` ← **Head:** `fix/bun-runtime-limits` --- ### 📝 Commits (2) - [`2480121`](https://github.com/morluto/rea/commit/24801218e25986238be292790aa509fa9ae85b94) fix(bun): report the real runtime and enforce worker memory budgets on Windows - [`169ca86`](https://github.com/morluto/rea/commit/169ca86b35102a846936a682ba992dccc71de2f1) fix(windows): drain memory notifications after observing exit ### 📊 Changes **48 files changed** (+1630 additions, -196 deletions) <details> <summary>View changed files</summary> 📝 `.agents/skills/reverse-engineer-anything/references/javascript-applications.md` (+4 -1) 📝 `docs/installation.md` (+12 -0) 📝 `docs/javascript-artifact-reconstruction.md` (+8 -1) 📝 `docs/testing.md` (+3 -1) 📝 `docs/web-network-captures.md` (+6 -0) 📝 `docs/web-source-location.md` (+12 -3) 📝 `native/windows/README.md` (+24 -1) 📝 `native/windows/src/authority.hpp` (+5 -1) 📝 `native/windows/src/process.cc` (+88 -9) ➕ `scripts/lib/windows-job-memory-limit.mjs` (+180 -0) 📝 `scripts/lib/windows-protocol-input.mjs` (+1 -1) 📝 `scripts/verify-windows-native.mjs` (+5 -0) 📝 `src/application/Doctor.fixture.ts` (+1 -1) 📝 `src/application/Doctor.test.ts` (+53 -15) 📝 `src/application/Doctor.ts` (+6 -2) 📝 `src/application/DoctorDiagnostics.ts` (+28 -3) 📝 `src/application/WebSourceLocationService.test.ts` (+46 -0) 📝 `src/application/WebSourceLocationService.ts` (+18 -3) 📝 `src/browser/history/CaptureFailures.test.ts` (+94 -0) 📝 `src/browser/history/CaptureFailures.ts` (+27 -3) _...and 28 more files_ </details> ### 📄 Description ## Summary When REA runs on Bun, it describes a Node.js/V8 runtime it did not observe, and its JavaScript helper processes run with no memory bound. Bun reports `process.versions.node` (its Node.js compatibility version, e.g. `26.3.0`) and V8-shaped `getHeapStatistics()` values, but it is not V8 and ignores V8 flags such as `--max-old-space-size`. REA relies on those flags to bound three helpers it starts on `process.execPath`: the JavaScript source analyzer (`--max-old-space-size=<max_heap_mb>`, default 1024), the source-map codec (192 MiB) and the HAR decoder (192 MiB old generation). This PR: - Identifies the runtime once from `process.versions` (only Bun sets `process.versions.bun`) and records it truthfully. Under Bun, `trace_web_source_location` now works instead of always failing, and Evidence reports unenforced V8 heap limits as `null` (or omits them) instead of inventing numbers. `rea doctor` says it is running on Bun. - On Windows under Bun, gives each helper's owned Job Object a commit limit of its heap budget plus a 512 MiB Bun runtime reserve, and reports both the limit and reaching it. Reaching it returns the same memory `resource_constraint` failure Node's V8 out-of-memory path returns. Node.js launches and outputs are unchanged, apart from a new `details` object on the doctor `node` check. No issue is linked; this was found while running REA under Bun on Windows. ## Problem and expected behavior Smallest triggers, all under Bun 1.4.2 on Windows x64: 1. `rea trace-web-source-location <manifest> 0 <map> <url> 1 0 --json` always failed. The codec reported `runtime: { id: "node-v8", version: process.version, v8_heap_limit_bytes: getHeapStatistics().heap_size_limit }`. Bun's synthetic heap limit (~470 MB) is above the decoder's 256 MiB check, so the decoder returned `AnalysisOutputError: Codec reported a V8 heap limit exceeding its independent process budget.` Even had it passed, the Evidence would claim a V8 runtime and an enforced heap limit that did not exist. 2. JavaScript analysis resource failures put `worker_old_space_mb` and `worker_heap_limit_bytes` (Bun's `heap_size_limit`) into `reported_limits` and coverage limits. Bun enforces neither. 3. `inspect_web_network_capture` limitations said "HAR uses a 192 MiB Node old-generation heap", and HAR memory failures reported `old_generation_heap_mib: 192`. Bun does not enforce that heap. 4. `rea doctor --json` reported `{"name":"node","detail":"26.3.0"}` with no sign that the runtime was Bun. 5. The helpers were unbounded. Uncapped measurements: analyzing a 1.7 MB bundle (`@vue/compiler-sfc` browser build) committed up to **50,537 MiB** and then failed with a generic `ProviderAdapterError`. A 34,098-entry HAR, which exhausts the 192 MiB heap on Node.js, committed **1,026 MiB**. Expected: REA records the runtime it actually ran on, reports an unenforced limit as unavailable rather than as a number, and bounds each helper by the memory budget it already declares wherever the platform allows it. ## Change and scope ### Runtime identity - `src/domain/runtimeVersion.ts`: adds `JavaScriptRuntime` and `javaScriptRuntime(process.versions)`, the single place that decides between Node.js and Bun. Adapters pass `process.versions`; the domain module does not read `process`. - Source-map trace (`TraceSourceMap.ts`, `webSourceLocation.ts`, `SourceMapDecoder.ts`, `WebSourceLocationService.ts`): `runtime` is a discriminated union: - `{ id: "node-v8", version, v8_heap_limit_bytes: <int> }` (unchanged on Node.js) - `{ id: "bun", version, node_compatibility_version, v8_heap_limit_bytes: null, process_memory_limit_bytes: <int> | null }` The decoder applies the 256 MiB check only to V8 reports. It rejects a report whose runtime differs from the runtime that launched the codec (the codec always runs on `process.execPath`; without this check a `bun` report would bypass the heap check), and a Bun report whose `process_memory_limit_bytes` differs from the limit its launcher enforced. The final limitation states the budget that actually applied. - JavaScript analysis worker: the `ready` handshake's `heap_limit_bytes` is nullable; the worker server sends `null` under Bun and the parent rejects a handshake that contradicts its own runtime. Under Bun, `worker_heap_limit_bytes` is `null` and `worker_old_space_mb` is not reported. `javaScriptAnalysisCoverageLimits` already drops non-numeric values, so coverage omits the heap limit. - HAR decoder (`CaptureRelease.ts`, `CaptureFailures.ts`, `HistoricalCaptureDecoder.ts`): `HAR_CAPTURE_HEAP_ENFORCED` gates the heap sizes in limitations, `reported_limits` and remediation. - Doctor: `DoctorHost.nodeVersion` is replaced by `runtime: JavaScriptRuntime`. The `node` check keeps its name, because `DoctorScope` and `scripts/verify-package-discovery.mjs` select it by name. It carries structured `details`: `{ runtime: "node", version }`, or `{ runtime: "bun", version, node_compatibility_version }` with detail `Bun 1.4.2 (Node.js 26.3.0 compatibility)`. Under Bun the check passes when the Node.js compatibility version satisfies `SUPPORTED_NODE_VERSION_RANGE`, the same version setup already gates on (`SetupHost` uses `process.versions.node`); the Windows native addon already supports Bun as a Node-API host (#1531). Audit of `src` for `process.version`, `process.versions`, `node-v8`, `getHeapStatistics`, `heap_size_limit`, `nodeVersion` and `max-old-space-size`: | Location | Kind | Action | | --- | --- | --- | | `TraceSourceMap.ts` runtime record; `SourceMapDecoder.ts` heap check | Evidence claim plus a check that depends on it | Fixed | | `WebSourceLocationService.ts` limitation "192 MiB old-generation heap and one V8 worker" | Evidence claim | Fixed | | `JavaScriptAnalysisWorkerServer.ts` ready `heap_limit_bytes`; `JavaScriptAnalysisWorker.ts` `#limits()` | Output claim (error details, coverage limits) | Fixed | | `CaptureFailures.ts`, `HistoricalCaptureDecoder.ts` HAR heap sizes | Output claim (limitations, error details, remediation) | Fixed | | `Doctor.ts` / `DoctorDiagnostics.ts` `nodeVersion` | Output claim | Fixed | | `JavaScriptAnalysisWorker.ts` parent result budget (`heap_size_limit - heapUsed`), checkpoint admission; `JavaScriptAnalysisWorkerServer.ts` GC trigger at half `heap_size_limit` | Internal admission math | Unchanged. Under Bun these use Bun's reported statistics, which describe no enforced limit. `parent_result_budget_bytes` is a derived admission budget, not a runtime claim, so it is still reported. | | `JsonInputFile.ts` native-parse choice and assembly admission (`total_available_size`) | Internal admission math; error details echo its inputs | Unchanged. Under Bun `total_available_size` is ~33.6 GB, so these refusals are practically unreachable there. | | `--max-old-space-size` etc. passed to the codec, HAR decoder, EVM worker and analysis worker | Process arguments | Unchanged. Under Bun on Windows the Job Object limit below bounds the codec, HAR decoder and analyzer instead. The EVM worker keeps its Linux `prlimit` cap and does not report its 256 MiB value. | | `NativeModuleResolver.ts` `--js-flags=--max-old-space-size=128` | Chromium flag | Unrelated to the host runtime | | `JadxSession.ts` `heap_limit_mib`; `WindowsNativeLoader.ts` `process.versions.napi` | JVM value; Node-API admission | Unrelated | ### Job Object memory limit (Windows, Bun only) Native addon (`native/windows/src/process.cc`, `authority.hpp`): - `process_spawn` accepts an optional 6th argument: the job memory limit in bytes. It must be a positive safe integer and a whole number of pages; anything else fails with `ERROR_INVALID_PARAMETER` before any process is created. Existing 4- and 5-argument calls are unchanged. - `createJob(limit)` sets `JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE | JOB_OBJECT_LIMIT_JOB_MEMORY` and `JobMemoryLimit`, then reads the policy back and requires the exact flags and exact limit. The page-alignment rule exists so the read-back can be exact, because the kernel stores the limit in pages. - For a limited job, a private I/O completion port is associated with the job before `CreateProcessW`. Each `process_poll` drains it without waiting and records a sticky `memoryLimitReached` when `JOB_OBJECT_MSG_JOB_MEMORY_LIMIT` (10) arrives. The drain runs **before** the exit observation, so an exit caused by a failed allocation is never reported without the notification. A limited job's poll also returns `peakJobMemoryBytes` (`PeakJobMemoryUsed`). An unlimited job returns neither field, so the poll shape for existing launches is unchanged. The spawn result echoes `jobMemoryLimitBytes`. `process_close` releases the port. - `inspect()` creates a 64 MiB-limited probe job with a port, then reports `jobMemoryLimit: true`. TypeScript plumbing: - `WindowsNativeLoader`: `jobMemoryLimit: z.literal(true).optional()`; optional because artifacts built before this change omit it. - `WindowsOwnedProcess`: the trailing options are one object `{ protocolStdin, memoryLimitBytes, authority }`; the only other caller (`scripts/lib/windows-protocol-input.mjs`) is migrated. A requested limit **fails closed**: an artifact without `jobMemoryLimit` refuses the launch instead of starting an unlimited process. The spawn read-back must equal the request. A limited job's poll is parsed with a strict schema that requires both new fields, and an unlimited job's poll rejects them. `memoryLimitReached` is set before `exit` is emitted. - `ProviderProcess`: `OwnedProviderProcessSpawnOptions.memoryLimitBytes` is honored only by the Windows owned-job launcher; every other launcher throws instead of ignoring it. `memoryLimitReached` is passed through to `ProviderProcessSnapshot`, and `runOwnedCommand` carries it via the spawn options. Policy (`src/process/JavaScriptWorkerMemoryLimit.ts`, the single place this is decided): `javaScriptWorkerMemoryLimitBytes(heapBudgetMiB)` returns `heapBudgetMiB × 1 MiB + BUN_WORKER_RUNTIME_RESERVE_BYTES` only when `platform === "win32"` and the runtime is Bun; otherwise `null`. Every call site spreads `memoryLimitBytes` and adds the new reporting fields only when it is non-null, so Node.js launch arguments, the native call, the poll schema, Evidence and limitations are unchanged by construction. Reserve derivation (`BUN_WORKER_RUNTIME_RESERVE_BYTES = 512 MiB`): `bun -e 1` peaks at 52 MiB of job commit, but REA's workers commit far more before doing real work, because JavaScriptCore commits parsed and compiled module code. Peak job commit on trivial inputs, 5–8 uncapped runs each, on a 32-logical-processor host: | Worker | Peak job commit (trivial input) | | --- | --- | | Source-map codec | 278–368 MiB | | HAR decoder | 300–345 MiB | | Analysis worker | 321–346 MiB | 512 MiB covers the largest observed baseline with about 40% margin. Resulting limits: 704 MiB for the codec and HAR decoder, and `max_heap_mb` + 512 MiB for the analyzer (1536 MiB by default). Classification relies on the job notification, not on exit status, because Bun reports a reached limit in several ways: exit 3 (panic), exit 1 (`RangeError: Out of memory`), or `0xC0000409` (observed for the HAR decoder). | Helper | Result when the limit is reached | Reported limit | | --- | --- | --- | | Analyzer | `AnalysisResourceConstraintError(…, "memory", …)` from every path (exit, broken pipe or truncated frame, worker-caught exception, post-loop check). Completed static facts are retained, and the next source starts a fresh worker. | `worker_heap_limit_bytes: null` plus `worker_process_memory_limit_bytes`, which is also a coverage limit | | Source-map codec | Memory `AnalysisResourceConstraintError`; no partial result | `codec_process_memory_limit_bytes`; Evidence `runtime.process_memory_limit_bytes` | | HAR decoder | `captureMemoryFailure` from both the failed-exit path and a success path whose job reached the limit | `process_memory_limit_bytes`, plus a remediation sentence | Not included: memory enforcement for Bun on Linux and macOS (they keep `null` / no-limit reporting), EVM (already `prlimit`-capped), Bun support in installers or `rea setup`/`rea update`, and changes to the `rea doctor` remediation wording. Docs: `installation.md`, `web-source-location.md`, `javascript-artifact-reconstruction.md`, `web-network-captures.md`, `testing.md`, `native/windows/README.md`, and the skill reference `javascript-applications.md`. ## Contract and boundary impact - Semantic owner and earliest changed stage: `src/domain/runtimeVersion.ts` (runtime identity) and `src/process/JavaScriptWorkerMemoryLimit.ts` (limit policy); the native `process_spawn`/`process_poll` boundary, then `WindowsOwnedProcess`, then `spawnOwnedProviderProcess`. Producers: codec process, analysis worker server, HAR adapter, doctor host. - CLI and MCP/tool-catalog contract: the `trace_web_source_location` output schema's `normalized_result.runtime` changes from `{ id: string, version: string, v8_heap_limit_bytes: int }` to the two-variant union above; Node.js output is byte-identical. The `max_heap_mb` / `--max-heap-mb` description states that Bun ignores V8 heap flags and that Windows limits committed memory instead. `rea doctor --json`'s `node` check gains `details`; on Node.js, `detail` is unchanged. - Provider, bridge, target-format, or platform compatibility: Node.js behavior is unchanged. Under Bun, `trace_web_source_location` works instead of always failing. Under Bun on Windows the helpers require the matching native artifact; an older artifact refuses the launch with "does not support Job Object memory limits" instead of running unlimited. - Evidence, artifact, provenance, or reconstruction contract: source-map Evidence records the runtime that ran the codec. Unenforced heap limits are `null` or omitted instead of fabricated. Enforced Job Object limits are reported (`worker_process_memory_limit_bytes`, `codec_process_memory_limit_bytes`, `process_memory_limit_bytes`) and limitations state which budget applied. - Process execution, authorization, cleanup, or containment impact: on Windows under Bun, the helpers' owned jobs gain a commit limit and a private completion port whose lifetime matches the job's. Kill-on-close, breakaway denial and atomic job assignment are unchanged and still verified. New consistency checks reject codec and worker reports from a runtime other than the one REA launched, and codec reports of a limit other than the enforced one. - Generated metadata (`docs/public/product-catalog.json`), package, or installation impact: ignored catalogs and `skills/` regenerate from the changed schema, description and skill reference; no committed generated files changed (`docs:check:built` and `mcp-catalog:check:built` pass). `native/windows/build` is rebuilt by CI. ## Evidence and regression coverage - Tests added or updated: | Test file | What it covers | | --- | --- | | `src/domain/runtimeVersion.test.ts` | Identifies Node.js and Bun from `process.versions`-shaped input. | | `src/application/Doctor.test.ts` | `node` check `details` for Node.js; Bun passes with a supported compatibility version (26.3.0) and fails with an unsupported one (25.1.0). `Doctor.fixture.ts` and `GhidraDoctor.test.ts` move to the `runtime` host field. | | `src/process/JavaScriptWorkerMemoryLimit.test.ts` | Policy: Bun+win32 → 704 MiB / 1536 MiB; Node.js on win32 and Linux → none; Bun on Linux and macOS → none. | | `src/windows/WindowsOwnedProcess.test.ts` | Refuses a limit without the capability, before any native call; unlimited launch keeps 4 arguments and rejects unrequested limit fields; limited launch passes `[false, limit]`, verifies the read-back and sets `memoryLimitReached` before `exit`; rejects a limited poll missing the fields and a mismatched applied limit. | | `src/windows/WindowsNativeLoader.test.ts` | `jobMemoryLimit` admitted as `true`, absent on earlier artifacts, `false` rejected. | | `src/process/ProviderProcessOutput.test.ts` | Snapshots carry `memoryLimitReached` only when the handle observed it. | | `src/browser/history/CaptureFailures.test.ts` | Heap sizes in `reported_limits` only on Node.js; a reached limit after a Bun panic (exit 3) or exception (exit 1) → memory `resource_constraint` with `process_memory_limit_bytes`; the same exits without the notification stay `execution_failure`. | | `src/application/WebSourceLocationService.test.ts` | Limitation sentence for Node V8, Bun with a Job Object limit, and Bun without one. | | `tests/boundary/process/providerProcess.test.ts` | A non-Windows launcher refuses a limit. | | `tests/boundary/javascript/sourceMapDecoder.test.ts` | The real codec's runtime record for whichever runtime runs the test; replaying the real report succeeds and the other runtime's record is rejected; classification by the notification; a mismatched reported limit is rejected. | | `tests/boundary/javascript/javascriptSourceAnalysisWorker.test.ts` | Heap limits reported only when V8 enforces them; a contradicting handshake is rejected; classification (memory vs. `ProviderAdapterError`) by the notification, using a real child. | | `tests/acceptance/webSourceLocationCli.test.ts` | The real CLI/MCP run expects the runtime id of the executing runtime. | | `tests/acceptance/tools/historicalCaptureResources.test.ts` | Node keeps the fixed-V8-heap expectations; Bun on Windows expects the Job Object failure; Bun elsewhere is skipped because no bound exists to reach. | | `scripts/verify-windows-native.mjs` + `scripts/lib/windows-job-memory-limit.mjs` | Native conformance (CI, Node.js on windows-latest), listed below. | `scripts/lib/windows-job-memory-limit.mjs` asserts: `inspect().jobMemoryLimit === true`; invalid limits (`0`, negative, fractional, `2**53`, `NaN`, `Infinity`, non-page-aligned `4097`, a string, `null`) are rejected with Win32 87; an unlimited poll has neither new field; a 128 MiB-limited child allocating external `Buffer` memory (which V8 flags cannot bound) exits nonzero without completing, reports `memoryLimitReached`, reaches `activeProcesses === 0`, and peaks within [limit − 64 MiB, limit + 32 MiB]; a control child under the same limit exits 0 without reaching it; the `WindowsOwnedProcess` wrapper reports the reached limit; a missing capability is refused. Measured peaks exceed the limit by a few MiB of kernel-charged commit (512→520, 256→262, 704→708 MiB), hence the tolerance. - Base reproduction or other evidence: - On base under Bun, three tests fail and pass with this change (see Validation): the real-codec `sourceMapDecoder` test (`Codec reported a V8 heap limit exceeding its independent process budget.`), `webSourceLocationCli` (exit 1), and the HAR exhaustion acceptance test (nothing bounded the decoder). - Throwaway probes outside the repo (a PowerShell P/Invoke job probe and a Bun FFI job helper), Bun 1.4.2: with a 512 MiB job limit Bun panicked (exit 3, peak 520 MiB); with 256 MiB it threw a JavaScript out-of-memory exception (exit 1, peak 262 MiB). In both cases the completion port received `JOB_OBJECT_MSG_JOB_MEMORY_LIMIT` (10), the runtime-independent signal this change relies on. - Before the native control was built, this change's TypeScript was run with a launcher that put the child into a nested FFI Job Object with the requested limit: | Run | Limit | Peak | Result | | --- | --- | --- | --- | | Analyzer, 1.7 MB bundle | 1536 MiB | 1538.5 MiB | Memory `AnalysisResourceConstraintError`, static facts retained | | Analyzer, next small file | 1536 MiB | 345 MiB | Succeeded in a fresh worker | | HAR, 34,098 entries | 704 MiB | 707.8 MiB | Exit `0xC0000409`; memory `resource_constraint` with `process_memory_limit_bytes: 738197504` | | HAR, 2 entries | 704 MiB | 320 MiB | Succeeded | | Codec, at its maximum input budgets | 704 MiB | 405 MiB | Stopped at its 262144-record structural budget; the limit was never reached | | Codec, same input | 300 MiB (applied on purpose) | — | Memory `AnalysisResourceConstraintError` | - User-visible CLI/MCP output (Bun 1.4.2, Windows x64, built CLI with the CI-built native artifact): - `bun scripts\rea.mjs doctor --json`, `node` check: ```json { "name": "node", "ok": true, "classification": "healthy", "detail": "Bun 1.4.2 (Node.js 26.3.0 compatibility)", "details": { "runtime": "bun", "version": "1.4.2", "node_compatibility_version": "26.3.0" } } ``` - `bun scripts\rea.mjs trace-web-source-location <manifest> 0 <map> "https://app.test/app.js.map?v=1#context" 1 0 --json` (inputs written from `tests/fixtures/webSourceLocation.ts`) exits 0 with one match and: ```json "runtime": { "id": "bun", "version": "1.4.2", "node_compatibility_version": "26.3.0", "v8_heap_limit_bytes": null, "process_memory_limit_bytes": 738197504 } ``` The last limitation ends: "The codec ran on Bun, which does not enforce REA's V8 heap flags; a Windows Job Object limited its committed memory to 738197504 bytes, which is not a heap or RSS limit." - `bun scripts\rea.mjs analyze-javascript-application <dir> --format json` on a directory with the 1,732,994-byte `@vue/compiler-sfc` browser bundle (`src/big.js`) and a one-line module (`src/small.js`): exit 0 in about 5 seconds. `src/big.js` reports `resource_constraint` / `memory`, "reached its 1610612736-byte process memory limit while analyzing src/big.js during semantic", with `"reported_limits":{"worker_heap_limit_bytes":null,"worker_process_memory_limit_bytes":1610612736,"worker_timeout_ms":300000}`. `src/small.js` and its `small` export are still recovered by static analysis. - The same command with `--max-heap-mb 128` on a two-file app whose `main.js` has 4000 imports reaches the parent result-budget path and exits 0 with partial coverage. The `target.js` failure reports `"reported_limits":{"worker_heap_limit_bytes":null,"worker_process_memory_limit_bytes":671088640,"worker_timeout_ms":300000,"semantic_result_budget_bytes":0}`; coverage limits are `javascript_analysis_semantic_result_budget_bytes`, `javascript_analysis_worker_process_memory_limit_bytes`, `javascript_analysis_worker_timeout_ms` and `javascript_analysis_parent_result_budget_bytes`, with no worker heap limit. - Remaining proof gaps: - Node.js branches were not run locally (no Node.js on this machine); CI covers them. - `javascriptSourceAnalysisWorker.test.ts` cannot run under Vitest on Bun: every test in the file, including the three existing ones on base, fails with `[module runner] "import.meta.resolve" is not supported`. Its Bun-side behavior is covered by the analyzer CLI runs above; CI runs the file under Node.js. - The codec cannot reach its real 704 MiB limit within its own input budgets, so its reached-limit path is proven only with a smaller applied limit and with an injected notification. - The 512 MiB reserve was measured on one 32-logical-processor host; Bun's baseline commit grows with thread count. For evidence-bearing changes: - [x] Observed, derived, and inferred claims remain distinguishable. - [x] Artifact identity, source provenance, and failed attempts remain preserved. - [x] Unsupported, incomplete, unavailable, or uncertain outcomes remain visible. ## Validation performed Local validation ran under Bun 1.4.2 on Windows x64 without Node.js installed. npm-based wrapper scripts cannot run here, so the underlying scripts were run with `bun --bun`. The native artifact built by this commit's CI was installed in the ignored `native/windows/build`; base runs used the artifact from the published `rea-agents@6.4.0` package, because base's loader rejects the new `jobMemoryLimit` inspection key. | Command | Result | | --- | --- | | `bun --bun run compile`; `artifacts:skills`, `artifacts:mcp-catalog`, `artifacts:product-catalog`, `artifacts:managed-evidence`, `artifacts:error-schema` | Pass | | `bun --bun run typecheck` | Pass | | `bun --bun run lint` | Pass, 0 errors. No new warnings compared with base. Already-flagged functions grew slightly: `CaptureFailures` mapping (complexity 59→63), `HistoricalCaptureDecoder#inspect` (30→31), `JavaScriptAnalysisWorker#analyze` (47→48; file 710→756 lines), `verify-windows-native.mjs` (675→680 lines). | | `oxfmt --check` on touched files | Pass, on LF-normalized copies (the Windows checkout uses CRLF, which oxfmt flags in every file). | | `bun --bun run docs:check:built`, `bun --bun run mcp-catalog:check:built` | Pass | | `bun --bun run test:local --` on `JavaScriptWorkerMemoryLimit`, `WindowsOwnedProcess`, `WindowsNativeLoader`, `ProviderProcessOutput`, `CaptureFailures`, `WebSourceLocationService`, `runtimeVersion`, `Doctor`, `GhidraDoctor` | 118 of 120 pass. The 2 failures are `GhidraDoctor` Windows-authority tests that fail identically on base. | | `bun scripts/verify-windows-native.mjs .` (full native conformance, under Bun instead of Node.js) | `ok: true`, no false control. `processes.jobMemoryLimit`: the 128 MiB-limited child exited 1 at a peak of 145,399,808 bytes with the limit reached; the control exited 0 at 56,586,240 bytes; invalid limits, unlimited poll shape, wrapper reporting and missing-capability refusal all `true`. | | `bun --bun node_modules/vitest/vitest.mjs run` on `src/application src/domain src/browser/history src/javascript src/ghidra src/process src/windows tests/boundary/javascript tests/boundary/process`, the `webSourceLocationMcp`, `javascriptAnalysisResources`, `outputSchemaSemanticConstraints` and `compactInputSchemaMcp` MCP boundary tests, `targetDiagnostics`, and the `webSourceLocationCli` and `historicalCaptureResources` acceptance tests, on this branch and on base | Base: 2986 passed, 113 failed. This branch: 3013 passed, 114 failed (28 more tests). Fixed by this change: the real-codec `sourceMapDecoder` test, `webSourceLocationCli`, and the HAR exhaustion acceptance test. Failing only on this branch: the 4 new tests in `javascriptSourceAnalysisWorker.test.ts`, which fail like the file's existing tests because Vitest's module runner on Bun does not support `import.meta.resolve`. Every other failure is the same test on both and environment-specific under Bun on Windows (e.g. that `import.meta.resolve` limitation, POSIX-mode process tests, Linux/macOS Hopper installers, Ghidra Windows authority). | | Built-CLI smoke runs: `doctor --json`, `trace-web-source-location`, `analyze-javascript-application` (twice) | Output shown above. | CI on this commit (fork push run, Node.js on GitHub-hosted runners): - `Build bundled Windows x64 native controls` (MinGW build and artifact verification): passed. Its `rea-windows-x64.node` SHA-256 is `5fa70a01…11aac`, byte-identical to the artifact used in the local runs above. - `Windows capability and package lane` (`npm run verify:windows-native` on windows-latest, including the `jobMemoryLimit` controls): passed. - Static checks, Build package, Docs Generation, all four test shards, Test (coverage + timing), all four native package baselines, the Windows NativeAOT fixture, and the real browser, web runtime, web source map, web network, JavaScript recovery, EVM, binary layout, recorded crash and Apple artifact lanes: passed (29 jobs). - `Real Apple artifact CLI/MCP E2E (native-regressions)` failed in "Run native Apple and process boundary regressions", which also fails the `CI required` gate. The same step failed on upstream `main` at this PR's base 966dc3ac (morluto/rea run 38116233537), and upstream has since changed its macOS lanes (3f4ea321, "ci: reduce macOS runner contention"). On macOS this change only adds the `memoryLimitReached` pass-through and the refusal of a requested limit, which the policy never requests outside Windows. ## Compatibility, safety, and release - Breaking changes or migration steps: none for Node.js; the only Node.js-visible change is the new `details` object on the doctor `node` check. Consumers validating `trace_web_source_location` against the old schema could not have received Bun output before (it always failed). Bun-on-Windows users need the matching package artifact; an older artifact refuses the helper launch instead of running it unbounded. - Real Hopper/Ghidra, browser, or OS coverage: Windows x64 under Bun locally; native code built and verified by CI's Windows lanes. No browser, Hopper or Ghidra workflows involved. - Package or release metadata impact: rebuilt `native/windows/build` artifact. - Security, privacy, process, or containment review: the completion port is private and not inheritable, the handle list still restricts inheritance, and closing a limited job also closes its port. Job membership, kill-on-close and breakaway checks are unchanged. No new file access. ## Review checklist - [x] This PR addresses a concrete problem or an agreed enhancement. - [x] The PR has one focused outcome and the title follows `type(scope): outcome`. - [x] Related issue is linked, or the reason for not linking one is stated above. - [x] Tests cover changed observable behavior and meaningful failure paths. - [x] Owning docs, contracts, and generated metadata are updated where needed. - [x] User-visible CLI/MCP changes include representative output. - [x] I checked the final diff for secrets, unrelated cleanup, and unsupported claims. --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
bsakaguchi 2026-10-31 00:54:58 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
morluto/rea#4779
No description provided.