Repository navigation
fix(javascript): keep static facts after semantic results use the parent budget - #8648
Conversation
…ent budget Static checkpoints were admitted against the same budget as semantic and application results, half of the parent's free heap. Once those charges used it, the next checkpoint stopped the analysis and every remaining source lost its static facts, with most of the parent heap still free. Semantic and application results still stop at that budget. Static checkpoints may now continue to three quarters of the free heap while the live heap also has room for another copy of what it holds. Without that room, admission falls back to the result budget. The live condition is needed because the limit V8 reports counts young-generation capacity that retained objects cannot use: without it, the parent aborted on heap exhaustion at small heap limits. Reserved relationships are given up only when the larger share runs short. A stop still fails with a typed error, which now reports the limit that applied, what was available, the live heap in use and what the checkpoint needed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
[Medium impact] The reviewed changes appear safe to merge; no blocking code issue was established. SummaryThis PR lets static facts continue past the parent’s semantic-result budget while keeping separate memory checks.
No actionable new issue or in-scope rule violation was established. The ancestry check passed, but the merge bases differed, so the full PR changes were reviewed. No previous review threads were supplied. Tests were not run during this review. Supersession could not be checked because GitHub access required authentication. Diagram%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[Completed static checkpoint] --> B[Check decode headroom]
B --> C{Live heap has room for another copy?}
C -->|Yes| D[Use three-quarter static ceiling]
C -->|No| E[Use half-capacity result budget]
D --> F{Checkpoint fits after possible reclamation?}
E --> F
F -->|Yes| G[Retain static facts and continue]
F -->|No| H[Retain completed facts and report exhaustion]
G --> I[Keep semantic and application offers within result budget]
Reviews (2) · Last reviewed commit: "Merge origin/main into fix/7736-javascri..." · Reviewed by Greptile |
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
What broke: Once semantic and application charges used the parent's result budget, the next static checkpoint stopped the analysis, so every remaining file lost its static facts while most of the parent heap was free.
The fix: Static checkpoints may now continue past that budget, up to three quarters of the free heap, while the live heap also has room for another copy of what it holds. Semantic and application results still stop at the budget.
The risk: More facts are retained, so peak parent memory rises when a run now completes (mixed fixture peak heap about 1.3 GiB to 2.4 GiB, limit 4.2 GiB). The three-quarter share is a judgment from runs on synthetic fixtures, not a proven bound for every input shape.
Summary
Fixes #7736
The issue's exact 301-file recipe no longer fails on latest main: #7744 and #7986 cover it, and it exited 0 with 301/301 files in 4 runs. The root cause is still there for any input whose earlier charges cannot be reclaimed. A fixture with vendor-style chunks first still exits 1, with 90 to 98 of 501 files losing their static facts.
Problem and expected behavior
JavaScriptAnalysisWorker.#readCheckpointadmitted static checkpoints against the same budget as semantic and application results, half of the parent's free heap. #7986 made application reservations reclaimable, but adopted semantic results are not. Once they fill the budget, the next checkpoint exhausts it.Observed on
8250bf232with the generator below (501 files, 13,323,141 bytes),--max-heap-mb 4096, default parent heap:{"code":"resource_constraint","message":"The parent's result capacity was exhausted after retaining static facts for chunk-0208.js; remaining sources were not analyzed"}parsed_javascript_fileswas 409, 411 and 403 of 501 across three runs, so the failure point moves.Expected: files the parent can hold keep their static facts, and the run completes with partial semantic coverage reported explicitly.
Change and scope
All admission changes are in
#readCheckpointand#refreshResultBudget, so the CLI and MCP share them.retained + used <= limit - used - transfer reserve, meaning the live heap has room for another copy of what it holds. Without that room, admission falls back to the old budget.The live condition is required. A first version admitted checkpoints up to the whole free-heap figure and the parent aborted with
SIGABRTat--max-old-space-size=160and192, where base stops with a typed failure. A three-quarter share alone still aborted at 160. V8'sheap_size_limitincludes young-generation capacity (limit 384 MiB at old space 192 MiB; the abort came at about 186 MiB), so the existing live check does not protect the parent near its real limit. This PR does not correct that figure for the result budget.Not included: the later Evidence stages listed under proof gaps, and #3671. The projection refusal path it reports is unchanged. #8411 and #8468 are untouched.
Contract and boundary impact
JavaScriptAnalysisWorkerowns parent result admission. Its consumer,analyzeAndAdoptJavaScriptArtifactFiles, is unchanged. No superseded path was removed: reclamation and the next-checkpoint reserve still apply.reported_limitson the exhaustion failure gainsparent_static_capacity_bytes,parent_available_static_capacity_bytes,parent_used_heap_bytesandstatic_retention_estimate_bytes. These are not added to coverage limits.application_projection_failure.Expected effects:
Evidence and regression coverage
tests/acceptance/applications/javascriptCheckpointReserveCli.test.ts: a public CLI run with a real tight parent heap examines all 17 files, and each later source keeps its static facts and reports the omission.tests/boundary/javascript/javascriptStaticProjectionReclamation.test.ts: three new worker cases (past the budget, unreclaimable semantic charges, no live room) and updated thresholds.parsed_javascript_files: 501, skipped_javascript_files: 0. A real stop now reports (excerpt):{"parent_result_budget_bytes":103928540,"parent_static_capacity_bytes":103928540,"parent_available_static_capacity_bytes":5710,"parent_used_heap_bytes":197340816}Before and after, executed. CLI stdout,
--max-heap-mb 4096, default parent heap (limit 4288 MiB), two runs each on macOS with Node 24.22.1. The machine was shared with other jobs, and the base and fix batches ran separately, so small timing differences are not meaningful.The mixed rows are not equivalent work: base stops early and fix completes. Cold and warm:
Parent heap sweep, executed. Issue generator at 901 files, one run per cell. Files with static facts:
--max-old-space-sizeEvery run from 160 to 512 exits 1 without aborting on both builds. At 1024, base exits 1 and fix exits 0. The abort at 128 happens at about 4 s on both builds. One larger fix-only run, 3,051 files and 81 MB at the default heap, exited 0 with 3051/3051 in 214.8 s, peak heap 2821 MiB, RSS 3389 MiB, stdout 658.9 MB.
Remaining proof gaps:
analyze_javascript_applicationon the 201-file, 301-file and mixed fixtures ends in an error. On fix it is alwaysrecord_evidence, "The investigation's retained representation allowance is exhausted". Base gives the same error on 201 and 301 files. On mixed, base fails earlier with the compact response-budget error. So MCP callers do not get these facts until that allowance is addressed. Two calls on one server for mixed took 72.9 s and 63.1 s on base, 77.2 s and 51.2 s on fix, peak RSS 2624 and 2647 MiB against 3000 and 2746 MiB, all ending in those errors.--detail summary --evidence_outputstill exits 1 on these fixtures with "Insufficient heap headroom to authenticate the Evidence checkpoint" ([Bug] analyze-javascript-application on GitHub Desktop 3.6.5 fails at the final Evidence checkpoint and keeps nothing #8411). Mixed on base stops earlier at the static stage.used_heap_sizeincludes uncollected garbage, so the live condition can fall back to the old budget early. That outcome matches base. Semantic coverage can still vary slightly between runs.320), the live condition set the limit. Only the mocked worker tests pin the share itself.Boundary regressions: public CLI workflow plus production-boundary worker tests, covering capacity and partial-coverage reporting. Lifecycle regressions do not apply: no process or resource ownership changed.
For evidence-bearing changes:
gen-mixed.mjs(deterministic synthetic fixture:node gen-mixed.mjs ./fx 200 300)Validation performed
npm run check:fast— exit 0, on the final treenpm run format:check— exit 0, on the final treenpm run docs:check— exit 0, on the final treenpm run test:focusedon the four budget and reclamation test files — 17 of 17 passed, on the final treenpm run test:focusedon 136 JavaScript, Electron and acceptance test files — 1671 of 1671 passed, before merging the latest main commit, which touches only native tracingnpm run metadata:check— not run, no catalog changeCompatibility, safety, and release
Author readiness
Human owner: @kaoru0822-kitauji
These boxes were ticked by Claude Code under the owner's standing written instruction for this repository's automation, not after a separate human review of this diff.
Before merge
type(scope): outcome.🤖 Generated with Claude Code