fix(javascript): name heap and semantic ceilings (#6022)

* fix(javascript): name heap and semantic ceilings

Refs #5910. Heap exhaustion names max_heap_mb with its effective value,
range, and next-analysis worker restart. Semantic ceiling coverage names
the four max_semantic_* arguments. Wakaru and transfer ceilings say this
release has no runtime override.

Co-authored-by: morluto <morluto@users.noreply.github.com>

* fix(javascript): distinguish adjustable ceilings from fixed limits

The effective transfer file ceiling and the parent result budget follow
the CLI/MCP process heap, so recovery names NODE_OPTIONS instead of
denying an override. A provenance report over 8 MiB keeps the fixed
allowance guidance, and the Wakaru deadline says an unchanged retry may
hit it again.

Refs #5910

Co-authored-by: morluto <morluto@users.noreply.github.com>

* fix(javascript): keep Bun OOM and checkpoint heap controls distinct

Bun heap-exhaustion exits no longer claim the ignored max_heap_mb V8
old-space limit. Checkpoint decode names the parent heap and Node
NODE_OPTIONS, the same control as the parent result budget.

Refs #5910

Co-authored-by: morluto <morluto@users.noreply.github.com>

* fix(javascript): name semantic ceilings without claiming both were reached

Truncated semantic retention names the total and per-file controls as
effective ceilings. Callers only know that retention was truncated, so
the limitation no longer says both ceilings were reached.

Refs #5910

Co-authored-by: morluto <morluto@users.noreply.github.com>

* fix(javascript): name only the semantic ceiling that stopped retention

Semantic ceiling limitations claimed both the application total and the
per-file control whenever either one truncated the graph. Record which
offered budget stopped node and unknown retention, and name only those
controls. Coverage limits still list both effective values.

Co-authored-by: morluto <morluto@users.noreply.github.com>

* fix(javascript): require a value in the NODE_OPTIONS heap example

A bare --max-old-space-size is an illegal V8 flag and prevents Node from
starting. Parent-heap and transfer-ceiling recovery now share
--max-old-space-size=<MiB>, and the Kuna heap remediation uses the same
complete form.

Co-authored-by: morluto <morluto@users.noreply.github.com>

* fix(javascript): limit the old-space placeholder to NODE_OPTIONS recovery

The review covers the CLI/MCP NODE_OPTIONS example and the transfer-ceiling
copy. The Kuna heap-headroom sentence stays unchanged.

Co-authored-by: morluto <morluto@users.noreply.github.com>

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: morluto <morluto@users.noreply.github.com>
This commit is contained in:
authored and GitHub committed 2026-10-22 02:44:14 +08:00
1 parent 01788bac86
commit a0214e0e0e
22 files changed
+1166 -87

No files matched your search

@@ -0,0 +1,68 @@
import { expect, it } from "vitest";
import { AnalysisResourceConstraintError } from "../../domain/analysisErrorCore.js";
import { analyzeJavaScriptSemantics } from "../../domain/javascript/javascriptSemanticAnalysis.js";
import { projectJavaScriptModuleSemantics } from "../../domain/javascript/javascriptModuleSemanticIr.js";
import { projectAnalysisError } from "../../domain/analysisErrorProjection.js";
import {
javaScriptForwardedTransferRemediation,
javaScriptTransferCeilingRemediation,
measureJavaScriptModuleTransfer,
} from "./JavaScriptAnalysisTransfer.js";
it("distinguishes the adjustable transfer file ceiling from the fixed record and maximum ceilings", () => {
const ir = analyzeJavaScriptSemantics(
"export function add(value) { return value + 1; }",
);
const module = projectJavaScriptModuleSemantics(ir);
const measured = measureJavaScriptModuleTransfer(module);
let caught: unknown;
try {
measureJavaScriptModuleTransfer(module, measured.bytes - 1);
} catch (cause) {
caught = cause;
}
if (!(caught instanceof AnalysisResourceConstraintError))
throw new Error("Expected a transfer ceiling refusal");
const projected = projectAnalysisError(caught);
expect(projected.message).toContain("no runtime override");
expect(projected.message).toContain(
String(caught.reportedLimits?.transfer_file_bytes),
);
expect(projected.remediation.action).toContain("no runtime override");
expect(projected.remediation.action).toContain("max_heap_mb");
expect(projected.remediation.action).toContain("8388608");
expect(projected.remediation.action).toContain("2147483648");
expect(projected.remediation.action).toContain("NODE_OPTIONS");
expect(projected.remediation.action).toContain("--max-old-space-size=<MiB>");
expect(projected.remediation.action).toContain(
"Selecting fewer source_paths can",
);
expect(projected.remediation.action).not.toContain(
"Selecting fewer source_paths does not raise them",
);
expect(projected.remediation.action).not.toContain("get_evidence_bundle");
expect(projected.message).toContain("can rise until the maximum");
const forwarded = javaScriptForwardedTransferRemediation(
caught.reportedLimits,
);
expect(forwarded).toContain("NODE_OPTIONS");
expect(forwarded).toContain("Selecting fewer source_paths can");
const recordOnly = javaScriptForwardedTransferRemediation({
transfer_record_bytes: 8_388_608,
});
expect(recordOnly).toContain("NODE_OPTIONS");
expect(recordOnly).toContain("can be lower than the maximum");
expect(recordOnly).not.toContain(
"The effective transfer file ceiling is the 2147483648-byte maximum",
);
});
it("keeps the 2 GiB transfer file maximum fixed", () => {
const remediation = javaScriptTransferCeilingRemediation(2_147_483_648);
expect(remediation).toContain("2147483648");
expect(remediation).toContain("no runtime override");
expect(remediation).not.toContain("NODE_OPTIONS");
expect(remediation).toContain(
"Selecting fewer source_paths does not raise the record ceiling",
);
});