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

@@ -3,10 +3,14 @@ import { createReadStream } from "node:fs";
import { open, stat } from "node:fs/promises";
import { z } from "zod";
import { AnalysisResourceConstraintError } from "../../domain/analysisErrorCore.js";
import type { JsonValue } from "../../domain/jsonValue.js";
import type { JavaScriptArtifactFile } from "../../domain/javascript/javascriptArtifactFiles.js";
import type { JavaScriptModuleSemanticIr } from "../../domain/javascript/javascriptModuleSemanticIr.js";
import { JAVASCRIPT_SEMANTIC_ITEM_MAXIMUM } from "../../domain/javascript/javascriptAnalysisResourceControls.js";
import {
JAVASCRIPT_PARENT_HEAP_NODE_OPTIONS,
JAVASCRIPT_SEMANTIC_ITEM_MAXIMUM,
} from "../../domain/javascript/javascriptAnalysisResourceControls.js";
import type {
JavaScriptSemanticFileProjection,
JavaScriptSemanticProjectionCapacity,
@@ -66,6 +70,54 @@ export const javaScriptTransferByteBudget = (
Math.max(TRANSFER_BYTES, Math.floor(resultBudgetBytes / 8)),
);
/**
* The record ceiling and the 2 GiB file maximum are fixed. The effective file
* ceiling follows the parent offered result budget and can rise until that maximum.
*/
export const javaScriptTransferCeilingRemediation = (
transferFileBytes: number | undefined,
): string => {
const recordBytes = String(JAVASCRIPT_ANALYSIS_TRANSFER_RECORD_BYTES);
const maximumBytes = String(MAX_TRANSFER_BYTES);
const fixed = `The ${recordBytes}-byte transfer record ceiling and the ${maximumBytes}-byte transfer file maximum have no runtime override. Raising max_heap_mb does not raise either fixed ceiling. A request that exceeds the record ceiling or that maximum fails the same way when repeated unchanged.`;
const adjustable = `${JAVASCRIPT_PARENT_HEAP_NODE_OPTIONS} to raise the effective file ceiling up to ${maximumBytes} bytes. Selecting fewer source_paths can also raise this effective file allowance, because earlier retained results and the reserve for remaining sources reduce the offered budget. Neither change raises the record ceiling or the maximum.`;
if (
transferFileBytes !== undefined &&
transferFileBytes >= MAX_TRANSFER_BYTES
)
return `${fixed} The effective transfer file ceiling is the ${maximumBytes}-byte maximum. Selecting fewer source_paths does not raise the record ceiling or that maximum.`;
const effective =
transferFileBytes === undefined
? "The effective transfer file ceiling follows the parent offered result budget, which follows the CLI/MCP process heap, and can be lower than the maximum."
: `The effective transfer file ceiling is ${String(transferFileBytes)} bytes. It follows the parent offered result budget, which follows the CLI/MCP process heap.`;
return `${fixed} ${effective} ${adjustable}`;
};
/** Reattach transfer recovery after a worker protocol frame drops remediation. */
export const javaScriptForwardedTransferRemediation = (
limits: Readonly<Record<string, JsonValue>> | null,
): string | undefined => {
const transferFileBytes = limits?.transfer_file_bytes;
if (
typeof limits?.transfer_record_bytes !== "number" &&
typeof transferFileBytes !== "number"
)
return undefined;
return javaScriptTransferCeilingRemediation(
typeof transferFileBytes === "number" ? transferFileBytes : undefined,
);
};
const javaScriptTransferCeilingReason = (
recordBytes: number,
transferFileBytes: number,
): string =>
recordBytes > FRAME_BYTES
? `A complete JavaScript fact exceeds the ${String(FRAME_BYTES)}-byte transfer record ceiling. This release has no runtime override.`
: transferFileBytes >= MAX_TRANSFER_BYTES
? `Complete JavaScript file facts exceed the ${String(MAX_TRANSFER_BYTES)}-byte transfer file maximum. This release has no runtime override.`
: `Complete JavaScript file facts exceed the effective transfer file ceiling of ${String(transferFileBytes)} bytes (maximum ${String(MAX_TRANSFER_BYTES)} bytes). That effective ceiling follows the parent offered result budget and can rise until the maximum. The record ceiling and that maximum have no runtime override.`;
const assertTransferByteBudget = (transferBytes: number): void => {
if (
!Number.isSafeInteger(transferBytes) ||
@@ -89,15 +141,16 @@ const encodeTransferRecord = (
throw new AnalysisResourceConstraintError(
"analyze_javascript_application",
"transport",
size > FRAME_BYTES
? "A complete JavaScript fact exceeds the transfer record byte budget"
: "Complete JavaScript file facts exceed the transfer byte budget",
javaScriptTransferCeilingReason(size, transferBytes),
{
transfer_record_bytes: FRAME_BYTES,
transfer_file_bytes: transferBytes,
record_bytes: size,
transfer_bytes_at_least: previousBytes + size,
},
{
remediationAction: javaScriptTransferCeilingRemediation(transferBytes),
},
);
return { line, size, kind: record.kind };
};