[PR #6099] [MERGED] fix(process): report the effective regex heap and uncertain signal retry #7145

Closed
opened 2026-11-01 21:45:44 +00:00 by bsakaguchi · 0 comments
Owner

📋 Pull Request Information

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

Base: main ← Head: cursor/process-limit-wording-d7ef


📝 Commits (9)

  • 607ea75 fix(process): report the effective regex worker heap
  • e2b5d9d fix(process): keep SIGXCPU and SIGXFSZ retries uncertain
  • 4e4b70e fix(process): leave percentage heap overrides unestablished
  • 3d61585 fix(process): read launch NODE_OPTIONS, not process.env
  • bc0c9da fix(process): leave env-file heap overrides unestablished
  • a8e194a fix(process): leave --max-heap-size old generation unestablished
  • 93db004 fix(process): leave config-file heap options unestablished
  • c2dcae6 fix(process): read launch exec argv from the command line
  • ca68b26 fix(process): leave a title-overwritten command line unestablished

📊 Changes

7 files changed (+1361 additions, -19 deletions)

View changed files

📝 docs/kuna-provider.md (+1 -1)
📝 docs/native-investigation.md (+31 -3)
📝 src/process/ArtifactAnalysisResources.test.ts (+49 -0)
📝 src/process/ArtifactAnalysisResources.ts (+4 -1)
➕ src/process/RegexMatcher.test.ts (+493 -0)
📝 src/process/RegexMatcher.ts (+404 -6)
📝 tests/boundary/process/regexMatcher.test.ts (+379 -8)

📄 Description

Summary

Follow-up to #6071 for post-merge Codex P2 findings, plus the Greptile percentage-heap finding, the Codex launch-NODE_OPTIONS finding, the env-file heap finding, the --max-heap-size finding, the Node config-file finding, the mutable-execArgv finding, and the command-line title finding. Part of #5910 (parent #5808).

The regex worker reports the effective old-generation heap instead of an unchangeable 128 MiB cap. A launch --max-old-space-size-percentage takes precedence over that absolute flag and leaves the effective heap unestablished. The launch NODE_OPTIONS value is read from the process initial environment, not from a later process.env edit. The launch Node options are read from the initial command line when that buffer still has a program entry, not from a later process.execArgv edit. process.title and --title overwrite that command line, so a buffer without a program entry leaves the heap unestablished. --env-file and --env-file-if-exists leave the heap unestablished when that variable was absent, because the file can supply it. A positive --max-heap-size leaves the old generation unestablished, because that flag sets the total heap and can be lower than the 128 MiB request. --experimental-config-file and --experimental-default-config-file leave the heap unestablished, because the config file can supply a heap percentage and is not read back. SIGXCPU and SIGXFSZ no longer say an unchanged retry fails the same way when the signal's cause is not established.

Problem and expected behavior

REGEX_MATCHING_LIMITATION and the heap error said the worker heap was a fixed 128 MiB cap and that this release has no runtime override. Node gives --max-old-space-size, including a value supplied only through NODE_OPTIONS, precedence over resourceLimits.maxOldGenerationSizeMb (Worker API). The worker can exceed 128 MiB and may not raise ERR_WORKER_OUT_OF_MEMORY at that budget. NODE_OPTIONS is not copied into process.execArgv.

--max-old-space-size-percentage also overrides the 128 MiB request and takes precedence over --max-old-space-size in either order (CLI). Node computes that MiB from constrained or total memory while parsing options. On this host, cgroup memory.high moved between reads, and some Node versions also cap total memory at 4 GiB when pointer compression is on. A later read is not the value Node applied. Reporting 128 or the absolute flag is the wrong heap.

process.env.NODE_OPTIONS is mutable. A preload started from that variable can clear or rewrite it after V8 has already applied the original flags. Reading the rewritten string reports 128 MiB, or some other false effective limit, while process.execArgv still does not contain the original flag.

process.execArgv is mutable too. On Node 24.15.0, node --max-old-space-size=64 --require=preload.cjs still applied the 64 MiB heap after the preload set process.execArgv.length = 0. Deriving the heap from the emptied array reports the 128 MiB request.

process.title and --title overwrite the argv memory behind /proc/self/cmdline. On Node v22.23.3, node --max-old-space-size=64 with a preload that sets process.title = "rea", and node --title=rea --max-old-space-size=64, both left that file as rea followed by NULs while the worker heap stayed at 64 MiB. Treating that buffer as an empty option list advertised the 128 MiB request. Node documents that setting the title overwrites argv memory.

--env-file and --env-file-if-exists set environment variables from a file at startup. When NODE_OPTIONS is absent from the initial environment, NODE_OPTIONS=--max-old-space-size=256 in that file raises the worker heap, but /proc/self/environ has no NODE_OPTIONS entry and process.execArgv records only the file flag. Classifying the missing entry as recovered advertises 128 MiB. The file is not an immutable launch source: it can change after startup, and --env-file-if-exists may have been missing. A percentage in the file also beats a later absolute flag. Node does not let the file replace a NODE_OPTIONS value that was already in the initial environment.

--max-heap-size sets the total heap, not the old generation. On Node v22.23.3, node --max-heap-size=64 with a worker maxOldGenerationSizeMb of 128 kept that resource limit at 128 and reported heap_size_limit of exactly 64 MiB. The underscore form did the same. A later --max-old-space-size did not restore a pure old-generation size. Treating the flag as absent advertised an effective 128 MiB old generation. Node 22.23.3 rejects the flag inside NODE_OPTIONS; Node 24 and later allow it there. A zero or empty value cleared the total-heap cap and left the old-space result. A split value and a non-integer aborted the process.

--experimental-config-file and --experimental-default-config-file load a Node config whose nodeOptions can include max-old-space-size-percentage. On Node v22.23.3, a config of {"nodeOptions":{"max-old-space-size-percentage":"1"}} kept resourceLimits.maxOldGenerationSizeMb at 128 and lowered the worker heap_size_limit on this host. process.execArgv contained only the config-file flag. The same lowered heap remained when --max-old-space-size=256 followed the flag. Node accepts the path as = or the next argument. --experimental-default-config-file reads node.config.json from the working directory. The file is not an immutable launch source. A 1% heap is not a stable comparison: on a large host without Node's 4 GiB cap it can exceed the 128 MiB request.

The SIGXCPU/SIGXFSZ remediation said Repeating the unchanged request fails the same way in the same branch that says the signal's exact cause is not independently established. A producer can self-raise or externally receive either signal without exhausting the reported rlimit, so an unchanged retry may succeed.

The matching deadline remains a fixed cap with no runtime override. The inherited producer ceiling still has no REA setting that raises it.

Change and scope

Wording and error projection. resolveRegexWorkerHeap derives the effective old-generation heap from a recovered launch NODE_OPTIONS string, then recovered launch Node options. It does not read process.env or process.execArgv. A positive --max-old-space-size or --max_old_space_size is that heap. A zero or empty value leaves the 128 MiB request in effect. An unparsed absolute flag leaves the effective heap unestablished instead of claiming 128.

Any --max-old-space-size-percentage token, including the underscore form and a value passed as the next argument, leaves the effective heap unestablished. It wins over an absolute flag in either source and either order, including a later zero or a value Node rejects. The raw NODE_OPTIONS string is not retained. OOM details report worker_heap_mib only for an established heap and requested_worker_heap_mib as 128.

On Linux, the launch string is the NODE_OPTIONS entry in /proc/self/environ, the execve environment block. A later process.env assignment does not change that block. The rest of the block is not retained. When that block cannot be read, or the platform has no equivalent immutable block, the effective heap stays unestablished. A missing reading is not treated as an absent NODE_OPTIONS.

On Linux, the launch Node options are recovered from /proc/self/cmdline only when that buffer still has a program entry: a script path, stdin -, an eval or print argument, or the token after --. The parser skips the executable and omits arguments after the entry. A title overwrite is one token plus NUL padding and has no program entry, so it is unrecoverable rather than an empty option list. An unrecognized option immediately before the entry, invalid UTF-8, or a failed read is also unrecoverable. Other platforms have no equivalent block. Unrecoverable provenance stays unknown. process.execArgv is not a fallback.

When NODE_OPTIONS is absent and the recovered command line contains --env-file or --env-file-if-exists, including a path in the next argument, the effective heap stays unestablished. An initial NODE_OPTIONS, including an empty one, is still parsed together with the command line, because the file does not replace it.

A positive --max-heap-size or --max_heap_size, on the CLI or in the recovered NODE_OPTIONS string, leaves the old generation unestablished. An old-space flag does not restore a claimed MiB while that total-heap value is in effect. A final zero or empty value clears it and the old-space result stands. An unparsed value stays unestablished, including when a later zero or old-space flag follows it.

When the recovered command line or the recovered NODE_OPTIONS string contains --experimental-config-file or --experimental-default-config-file, including a path in = or the next argument, the effective heap stays unestablished. That is true when an absolute --max-old-space-size is also present and when NODE_OPTIONS was recovered. The config file is not read.

SIGXCPU/SIGXFSZ remediation says an unchanged retry may succeed or may fail the same way. The projected retryable flag is true so it does not contradict that sentence.

Follow the contribution scope.
Unresolved scope, authority, side effects, or required verification block
integration. Link the required maintainer scope decision for new providers or
capabilities involving target execution, injection, mutation, or protection
recovery; ordinary fixes within existing boundaries may state "not applicable".
Draft PRs may leave the scope decision pending and the readiness and merge checklists
unchecked. Maintainers can help resolve open questions.

Not applicable. This stays inside existing process error text. No new provider, target execution, injection, mutation, or protection recovery.

Contract and boundary impact

  • Semantic owner and earliest changed stage: regex worker heap text is owned by RegexMatcher; producer signal recovery is owned by ArtifactAnalysisResources.commandFailure.
  • Contribution scope fit and linked maintainer decision: not applicable. Wording and projection within existing boundaries.
  • CLI and MCP/tool-catalog contract: regex capability limitations and the heap error change. Search tool descriptions still name only the fixed deadline. No new arguments or settings.
  • Provider, bridge, target-format, or platform compatibility: Hopper and Kuna regex limitation text. Linux producer SIGXCPU/SIGXFSZ recovery. The worker still requests maxOldGenerationSizeMb: 128. Linux can recover launch NODE_OPTIONS from the initial environment block and launch Node options from the initial command line when a program entry is still present. Other platforms leave that heap unestablished.
  • Evidence, artifact, provenance, or reconstruction contract: none. Heap numbers are derived from launch flags, not a measured V8 heap on each match. A percentage flag, an env file with no initial NODE_OPTIONS, a positive --max-heap-size, a Node config-file flag, a title-overwritten command line, and an unrecoverable command line are reported as unestablished rather than a reconstructed MiB. The current process.env.NODE_OPTIONS string and the current process.execArgv array are not launch provenance. An env file and a Node config file are not reread.
  • Target selection, authority, upstream behavior, and cleanup details (if applicable): unchanged. Cleanup and the worker request are the same.
  • Generated metadata (docs/public/product-catalog.json), package, or installation impact (CI generates and validates ignored catalogs and skills/; edit authored instructions in .agents/skills/reverse-engineer-anything/ and never commit build outputs): docs updated in docs/kuna-provider.md and docs/native-investigation.md. No package or catalog source change.

Expected effects (check known effects; use documentation-only on its own).
Unchecked items do not establish absence; leave uncertainties visible.

  • Read existing files or captured data.
  • Launch or attach to a process.
  • Inject instrumentation or run scripts/code.
  • Change process memory, registers, files, or configuration.
  • Contact network services.
  • Recover protected code or work around protections.
  • Documentation or usability only; no target interaction.
  • Not sure yet; investigation or maintainer guidance needed.

The SIGXCPU/SIGXFSZ projection now reports retryable: true. That matches "an unchanged retry may succeed." It does not change target interaction. Reading /proc/self/environ retains only the NODE_OPTIONS entry long enough to parse heap flags. Reading /proc/self/cmdline retains only the recovered Node option tokens, and only when a program entry is present.

Evidence and regression coverage

  • Tests added or updated: src/process/RegexMatcher.test.ts, tests/boundary/process/regexMatcher.test.ts, src/process/ArtifactAnalysisResources.test.ts.
  • Base reproduction or other evidence: on Node v22.23.3, a worker created with resourceLimits.maxOldGenerationSizeMb: 128 kept reporting that resource limit while v8.getHeapStatistics().heap_size_limit followed --max-old-space-size. Comparisons use pinned 64 MiB and 256 MiB heaps rather than an unconstrained baseline.
  • Command-line provenance, same Node: node --max-old-space-size=64 --require=preload.cjs with the preload setting process.execArgv.length = 0 reported an empty process.execArgv and a worker heap within 5% of a pinned 64 MiB child. The intact /proc/self/cmdline still contained the 64 MiB flag, and the resolver reported effective 64 MiB. Passing the emptied array to resolveRegexWorkerHeap would report the 128 MiB request.
  • Title overwrite, same Node: a preload process.title = "rea" after --max-old-space-size=64, and --title=rea --max-old-space-size=64, both replaced /proc/self/cmdline with rea and NUL padding. The worker heap stayed within 5% of the pinned 64 MiB child and resourceLimits.maxOldGenerationSizeMb stayed 128. The parser returned unrecoverable, and the launch heap was unknown rather than 128 MiB. A synthetic command line of node plus script.js and no heap flag still recovers an empty option list. A single rea token and rea plus NUL padding do not.
  • Off Linux, readLaunchExecArgv is unrecoverable and the launch heap stays unknown even when a recovered NODE_OPTIONS string contains --max-old-space-size=64. The boundary probe that reads /proc/self/cmdline does not spawn on those platforms.
  • Percentage, env-file, --max-heap-size, and config-file evidence from the earlier commits on this branch still applies. Percentage and config-file probes no longer compare heap_size_limit with a host-memory baseline.
  • User-visible CLI/MCP output (if applicable): capability text names the effective MiB when the absolute flag parses, and says the deadline has no runtime override. A percentage flag, an unrecoverable launch NODE_OPTIONS, an unrecoverable command line, a title-overwritten command line, an env-file launch with no initial NODE_OPTIONS, a positive --max-heap-size, or a Node config-file flag says the effective heap was not established.
  • Remaining proof gaps: no Hopper or Kuna regex session. The effective heap is derived from the launch flags this Node applies; it is not read back from the worker V8 heap on each match. The command-line and title probes were run on Node v22.23.3 only. The Node 24.15.0 title overwrite was not re-executed on that version here; this host showed the same rea plus NUL shape. macOS and Windows have no equivalent command-line block, so those platforms leave the heap unestablished. A node launch with no script, such as a REPL, is unrecoverable because it has no program entry.

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

  • npx oxfmt on the touched files, then npm run format:check — all matched files use the correct format (4736 files).
  • npm run check:fast — typecheck and lint succeeded (turbo 2.11.7, 2 tasks, 4.195s) after the title-overwrite change.
  • npm run test:focused -- src/process/RegexMatcher.test.ts tests/boundary/process/regexMatcher.test.ts — 2 files passed, 58 tests passed. The env-file probe passed in 321ms. The config-file probe passed in 329ms.
  • Earlier on this branch, before these heap-provenance edits: npm run test:focused -- src/process/ArtifactAnalysisResources.test.ts tests/boundary/captures/captureProvider.test.ts tests/boundary/signals/recordingProvider.test.ts covered the SIGXCPU/SIGXFSZ wording. Those later edits do not change that path.

Compatibility, safety, and release

  • Breaking changes or migration steps: none. Callers that matched the old heap sentence or "Repeating the unchanged request fails the same way." need to expect the new sentences. SIGXCPU/SIGXFSZ now project retryable: true. A cleared process.execArgv no longer changes the reported heap. A title-overwritten command line no longer reports 128 MiB. A missing command-line reading no longer reports 128 MiB. A percentage flag, env-file launch with no initial NODE_OPTIONS, positive --max-heap-size, or Node config-file flag no longer reports a MiB that the launch provenance does not establish.
  • Real Hopper/Ghidra, browser, or OS coverage: not run. Node v22.23.3 worker-heap, /proc/self/environ, /proc/self/cmdline, title, env-file, --max-heap-size, and config-file probes only.
  • Package or release metadata impact: none.
  • Security, privacy, process, or containment review: no authority change. The 128 MiB request is still passed to the worker. The text does not invent a MiB from a mutated process.execArgv, a title-overwritten command line, a percentage flag, an env file, a positive --max-heap-size, or a Node config file. The initial environment block is read only to parse NODE_OPTIONS. The command line is read only to recover Node option tokens when a program entry is present. The env file and the Node config file are not read back.

Author readiness

Human owner: @morluto

  • I reviewed the final diff and relevant existing code, understand the change and its edge cases, and take responsibility for the complete PR, including AI-assisted work.
  • I checked the evidence above, confirmed it supports the current change, and recorded meaningful validation gaps.
  • I declare this PR ready for initial maintainer review.

Before merge

  • This PR addresses a concrete problem or an agreed enhancement.
  • Material scope and authority questions are resolved, and any required maintainer scope decision covers the final diff.
  • 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 output includes representative output.
  • I checked the final diff for secrets, unrelated cleanup, and unsupported claims.
Open in Web Open in Cursor 

🔄 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/6099 **Author:** [@morluto](https://github.com/morluto) **Created:** 10/21/2026 **Status:** ✅ Merged **Merged:** 10/21/2026 **Merged by:** [@morluto](https://github.com/morluto) **Base:** `main` ← **Head:** `cursor/process-limit-wording-d7ef` --- ### 📝 Commits (9) - [`607ea75`](https://github.com/morluto/rea/commit/607ea75a01fe69a1c471e513fe0b25e54b5965d3) fix(process): report the effective regex worker heap - [`e2b5d9d`](https://github.com/morluto/rea/commit/e2b5d9d038bdcfc034f3fa8988d7e446f7fc69e4) fix(process): keep SIGXCPU and SIGXFSZ retries uncertain - [`4e4b70e`](https://github.com/morluto/rea/commit/4e4b70ecf75e6abe00653dabd413e3fe8c9ce1d4) fix(process): leave percentage heap overrides unestablished - [`3d61585`](https://github.com/morluto/rea/commit/3d615858a4bb4ab7062a1ad8234f1725509f47ad) fix(process): read launch NODE_OPTIONS, not process.env - [`bc0c9da`](https://github.com/morluto/rea/commit/bc0c9daaa3c2feb3ab843a119b84e95bfa955a3b) fix(process): leave env-file heap overrides unestablished - [`a8e194a`](https://github.com/morluto/rea/commit/a8e194acb6581d9a40834174b65928282598cd90) fix(process): leave --max-heap-size old generation unestablished - [`93db004`](https://github.com/morluto/rea/commit/93db004ba32c7b38d95abf547357011b24b211d6) fix(process): leave config-file heap options unestablished - [`c2dcae6`](https://github.com/morluto/rea/commit/c2dcae68fa455fdd312bb356d5785cef9d43f669) fix(process): read launch exec argv from the command line - [`ca68b26`](https://github.com/morluto/rea/commit/ca68b2645ae973a7031737df84e44e5fa8480b06) fix(process): leave a title-overwritten command line unestablished ### 📊 Changes **7 files changed** (+1361 additions, -19 deletions) <details> <summary>View changed files</summary> 📝 `docs/kuna-provider.md` (+1 -1) 📝 `docs/native-investigation.md` (+31 -3) 📝 `src/process/ArtifactAnalysisResources.test.ts` (+49 -0) 📝 `src/process/ArtifactAnalysisResources.ts` (+4 -1) ➕ `src/process/RegexMatcher.test.ts` (+493 -0) 📝 `src/process/RegexMatcher.ts` (+404 -6) 📝 `tests/boundary/process/regexMatcher.test.ts` (+379 -8) </details> ### 📄 Description <!-- CURSOR_AGENT_PR_BODY_BEGIN --> ## Summary Follow-up to #6071 for post-merge Codex P2 findings, plus the Greptile percentage-heap finding, the Codex launch-`NODE_OPTIONS` finding, the env-file heap finding, the `--max-heap-size` finding, the Node config-file finding, the mutable-`execArgv` finding, and the command-line title finding. Part of #5910 (parent #5808). The regex worker reports the effective old-generation heap instead of an unchangeable 128 MiB cap. A launch `--max-old-space-size-percentage` takes precedence over that absolute flag and leaves the effective heap unestablished. The launch `NODE_OPTIONS` value is read from the process initial environment, not from a later `process.env` edit. The launch Node options are read from the initial command line when that buffer still has a program entry, not from a later `process.execArgv` edit. `process.title` and `--title` overwrite that command line, so a buffer without a program entry leaves the heap unestablished. `--env-file` and `--env-file-if-exists` leave the heap unestablished when that variable was absent, because the file can supply it. A positive `--max-heap-size` leaves the old generation unestablished, because that flag sets the total heap and can be lower than the 128 MiB request. `--experimental-config-file` and `--experimental-default-config-file` leave the heap unestablished, because the config file can supply a heap percentage and is not read back. SIGXCPU and SIGXFSZ no longer say an unchanged retry fails the same way when the signal's cause is not established. ## Problem and expected behavior `REGEX_MATCHING_LIMITATION` and the heap error said the worker heap was a fixed 128 MiB cap and that this release has no runtime override. Node gives `--max-old-space-size`, including a value supplied only through `NODE_OPTIONS`, precedence over `resourceLimits.maxOldGenerationSizeMb` ([Worker API](https://nodejs.org/api/worker_threads.html#new-workerfilename-options)). The worker can exceed 128 MiB and may not raise `ERR_WORKER_OUT_OF_MEMORY` at that budget. `NODE_OPTIONS` is not copied into `process.execArgv`. `--max-old-space-size-percentage` also overrides the 128 MiB request and takes precedence over `--max-old-space-size` in either order ([CLI](https://nodejs.org/api/cli.html#--max-old-space-size-percentagesize)). Node computes that MiB from constrained or total memory while parsing options. On this host, cgroup `memory.high` moved between reads, and some Node versions also cap total memory at 4 GiB when pointer compression is on. A later read is not the value Node applied. Reporting 128 or the absolute flag is the wrong heap. `process.env.NODE_OPTIONS` is mutable. A preload started from that variable can clear or rewrite it after V8 has already applied the original flags. Reading the rewritten string reports 128 MiB, or some other false effective limit, while `process.execArgv` still does not contain the original flag. `process.execArgv` is mutable too. On Node 24.15.0, `node --max-old-space-size=64 --require=preload.cjs` still applied the 64 MiB heap after the preload set `process.execArgv.length = 0`. Deriving the heap from the emptied array reports the 128 MiB request. `process.title` and `--title` overwrite the argv memory behind `/proc/self/cmdline`. On Node v22.23.3, `node --max-old-space-size=64` with a preload that sets `process.title = "rea"`, and `node --title=rea --max-old-space-size=64`, both left that file as `rea` followed by NULs while the worker heap stayed at 64 MiB. Treating that buffer as an empty option list advertised the 128 MiB request. Node documents that setting the title overwrites argv memory. `--env-file` and `--env-file-if-exists` set environment variables from a file at startup. When `NODE_OPTIONS` is absent from the initial environment, `NODE_OPTIONS=--max-old-space-size=256` in that file raises the worker heap, but `/proc/self/environ` has no `NODE_OPTIONS` entry and `process.execArgv` records only the file flag. Classifying the missing entry as recovered advertises 128 MiB. The file is not an immutable launch source: it can change after startup, and `--env-file-if-exists` may have been missing. A percentage in the file also beats a later absolute flag. Node does not let the file replace a `NODE_OPTIONS` value that was already in the initial environment. `--max-heap-size` sets the total heap, not the old generation. On Node v22.23.3, `node --max-heap-size=64` with a worker `maxOldGenerationSizeMb` of 128 kept that resource limit at 128 and reported `heap_size_limit` of exactly 64 MiB. The underscore form did the same. A later `--max-old-space-size` did not restore a pure old-generation size. Treating the flag as absent advertised an effective 128 MiB old generation. Node 22.23.3 rejects the flag inside `NODE_OPTIONS`; Node 24 and later allow it there. A zero or empty value cleared the total-heap cap and left the old-space result. A split value and a non-integer aborted the process. `--experimental-config-file` and `--experimental-default-config-file` load a Node config whose `nodeOptions` can include `max-old-space-size-percentage`. On Node v22.23.3, a config of `{"nodeOptions":{"max-old-space-size-percentage":"1"}}` kept `resourceLimits.maxOldGenerationSizeMb` at 128 and lowered the worker `heap_size_limit` on this host. `process.execArgv` contained only the config-file flag. The same lowered heap remained when `--max-old-space-size=256` followed the flag. Node accepts the path as `=` or the next argument. `--experimental-default-config-file` reads `node.config.json` from the working directory. The file is not an immutable launch source. A 1% heap is not a stable comparison: on a large host without Node's 4 GiB cap it can exceed the 128 MiB request. The SIGXCPU/SIGXFSZ remediation said `Repeating the unchanged request fails the same way` in the same branch that says the signal's exact cause is not independently established. A producer can self-raise or externally receive either signal without exhausting the reported rlimit, so an unchanged retry may succeed. The matching deadline remains a fixed cap with no runtime override. The inherited producer ceiling still has no REA setting that raises it. ## Change and scope Wording and error projection. `resolveRegexWorkerHeap` derives the effective old-generation heap from a recovered launch `NODE_OPTIONS` string, then recovered launch Node options. It does not read `process.env` or `process.execArgv`. A positive `--max-old-space-size` or `--max_old_space_size` is that heap. A zero or empty value leaves the 128 MiB request in effect. An unparsed absolute flag leaves the effective heap unestablished instead of claiming 128. Any `--max-old-space-size-percentage` token, including the underscore form and a value passed as the next argument, leaves the effective heap unestablished. It wins over an absolute flag in either source and either order, including a later zero or a value Node rejects. The raw `NODE_OPTIONS` string is not retained. OOM details report `worker_heap_mib` only for an established heap and `requested_worker_heap_mib` as 128. On Linux, the launch string is the `NODE_OPTIONS` entry in `/proc/self/environ`, the `execve` environment block. A later `process.env` assignment does not change that block. The rest of the block is not retained. When that block cannot be read, or the platform has no equivalent immutable block, the effective heap stays unestablished. A missing reading is not treated as an absent `NODE_OPTIONS`. On Linux, the launch Node options are recovered from `/proc/self/cmdline` only when that buffer still has a program entry: a script path, stdin `-`, an eval or print argument, or the token after `--`. The parser skips the executable and omits arguments after the entry. A title overwrite is one token plus NUL padding and has no program entry, so it is unrecoverable rather than an empty option list. An unrecognized option immediately before the entry, invalid UTF-8, or a failed read is also unrecoverable. Other platforms have no equivalent block. Unrecoverable provenance stays unknown. `process.execArgv` is not a fallback. When `NODE_OPTIONS` is absent and the recovered command line contains `--env-file` or `--env-file-if-exists`, including a path in the next argument, the effective heap stays unestablished. An initial `NODE_OPTIONS`, including an empty one, is still parsed together with the command line, because the file does not replace it. A positive `--max-heap-size` or `--max_heap_size`, on the CLI or in the recovered `NODE_OPTIONS` string, leaves the old generation unestablished. An old-space flag does not restore a claimed MiB while that total-heap value is in effect. A final zero or empty value clears it and the old-space result stands. An unparsed value stays unestablished, including when a later zero or old-space flag follows it. When the recovered command line or the recovered `NODE_OPTIONS` string contains `--experimental-config-file` or `--experimental-default-config-file`, including a path in `=` or the next argument, the effective heap stays unestablished. That is true when an absolute `--max-old-space-size` is also present and when `NODE_OPTIONS` was recovered. The config file is not read. SIGXCPU/SIGXFSZ remediation says an unchanged retry may succeed or may fail the same way. The projected `retryable` flag is true so it does not contradict that sentence. Follow the [contribution scope](https://github.com/morluto/rea/blob/main/CONTRIBUTING.md#contribution-scope). Unresolved scope, authority, side effects, or required verification block integration. Link the required maintainer scope decision for new providers or capabilities involving target execution, injection, mutation, or protection recovery; ordinary fixes within existing boundaries may state "not applicable". Draft PRs may leave the scope decision pending and the readiness and merge checklists unchecked. Maintainers can help resolve open questions. Not applicable. This stays inside existing process error text. No new provider, target execution, injection, mutation, or protection recovery. ## Contract and boundary impact - Semantic owner and earliest changed stage: regex worker heap text is owned by `RegexMatcher`; producer signal recovery is owned by `ArtifactAnalysisResources.commandFailure`. - Contribution scope fit and linked maintainer decision: not applicable. Wording and projection within existing boundaries. - CLI and MCP/tool-catalog contract: regex capability limitations and the heap error change. Search tool descriptions still name only the fixed deadline. No new arguments or settings. - Provider, bridge, target-format, or platform compatibility: Hopper and Kuna regex limitation text. Linux producer SIGXCPU/SIGXFSZ recovery. The worker still requests `maxOldGenerationSizeMb: 128`. Linux can recover launch `NODE_OPTIONS` from the initial environment block and launch Node options from the initial command line when a program entry is still present. Other platforms leave that heap unestablished. - Evidence, artifact, provenance, or reconstruction contract: none. Heap numbers are derived from launch flags, not a measured V8 heap on each match. A percentage flag, an env file with no initial `NODE_OPTIONS`, a positive `--max-heap-size`, a Node config-file flag, a title-overwritten command line, and an unrecoverable command line are reported as unestablished rather than a reconstructed MiB. The current `process.env.NODE_OPTIONS` string and the current `process.execArgv` array are not launch provenance. An env file and a Node config file are not reread. - Target selection, authority, upstream behavior, and cleanup details (if applicable): unchanged. Cleanup and the worker request are the same. - Generated metadata (`docs/public/product-catalog.json`), package, or installation impact (CI generates and validates ignored catalogs and `skills/`; edit authored instructions in `.agents/skills/reverse-engineer-anything/` and never commit build outputs): docs updated in `docs/kuna-provider.md` and `docs/native-investigation.md`. No package or catalog source change. Expected effects (check known effects; use documentation-only on its own). Unchecked items do not establish absence; leave uncertainties visible. - [ ] Read existing files or captured data. - [ ] Launch or attach to a process. - [ ] Inject instrumentation or run scripts/code. - [ ] Change process memory, registers, files, or configuration. - [ ] Contact network services. - [ ] Recover protected code or work around protections. - [x] Documentation or usability only; no target interaction. - [ ] Not sure yet; investigation or maintainer guidance needed. The SIGXCPU/SIGXFSZ projection now reports `retryable: true`. That matches "an unchanged retry may succeed." It does not change target interaction. Reading `/proc/self/environ` retains only the `NODE_OPTIONS` entry long enough to parse heap flags. Reading `/proc/self/cmdline` retains only the recovered Node option tokens, and only when a program entry is present. ## Evidence and regression coverage - Tests added or updated: `src/process/RegexMatcher.test.ts`, `tests/boundary/process/regexMatcher.test.ts`, `src/process/ArtifactAnalysisResources.test.ts`. - Base reproduction or other evidence: on Node v22.23.3, a worker created with `resourceLimits.maxOldGenerationSizeMb: 128` kept reporting that resource limit while `v8.getHeapStatistics().heap_size_limit` followed `--max-old-space-size`. Comparisons use pinned 64 MiB and 256 MiB heaps rather than an unconstrained baseline. - Command-line provenance, same Node: `node --max-old-space-size=64 --require=preload.cjs` with the preload setting `process.execArgv.length = 0` reported an empty `process.execArgv` and a worker heap within 5% of a pinned 64 MiB child. The intact `/proc/self/cmdline` still contained the 64 MiB flag, and the resolver reported effective 64 MiB. Passing the emptied array to `resolveRegexWorkerHeap` would report the 128 MiB request. - Title overwrite, same Node: a preload `process.title = "rea"` after `--max-old-space-size=64`, and `--title=rea --max-old-space-size=64`, both replaced `/proc/self/cmdline` with `rea` and NUL padding. The worker heap stayed within 5% of the pinned 64 MiB child and `resourceLimits.maxOldGenerationSizeMb` stayed 128. The parser returned unrecoverable, and the launch heap was unknown rather than 128 MiB. A synthetic command line of `node` plus `script.js` and no heap flag still recovers an empty option list. A single `rea` token and `rea` plus NUL padding do not. - Off Linux, `readLaunchExecArgv` is unrecoverable and the launch heap stays unknown even when a recovered `NODE_OPTIONS` string contains `--max-old-space-size=64`. The boundary probe that reads `/proc/self/cmdline` does not spawn on those platforms. - Percentage, env-file, `--max-heap-size`, and config-file evidence from the earlier commits on this branch still applies. Percentage and config-file probes no longer compare `heap_size_limit` with a host-memory baseline. - User-visible CLI/MCP output (if applicable): capability text names the effective MiB when the absolute flag parses, and says the deadline has no runtime override. A percentage flag, an unrecoverable launch `NODE_OPTIONS`, an unrecoverable command line, a title-overwritten command line, an env-file launch with no initial `NODE_OPTIONS`, a positive `--max-heap-size`, or a Node config-file flag says the effective heap was not established. - Remaining proof gaps: no Hopper or Kuna regex session. The effective heap is derived from the launch flags this Node applies; it is not read back from the worker V8 heap on each match. The command-line and title probes were run on Node v22.23.3 only. The Node 24.15.0 title overwrite was not re-executed on that version here; this host showed the same `rea` plus NUL shape. macOS and Windows have no equivalent command-line block, so those platforms leave the heap unestablished. A `node` launch with no script, such as a REPL, is unrecoverable because it has no program entry. 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 - `npx oxfmt` on the touched files, then `npm run format:check` — all matched files use the correct format (4736 files). - `npm run check:fast` — typecheck and lint succeeded (turbo 2.11.7, 2 tasks, 4.195s) after the title-overwrite change. - `npm run test:focused -- src/process/RegexMatcher.test.ts tests/boundary/process/regexMatcher.test.ts` — 2 files passed, 58 tests passed. The env-file probe passed in 321ms. The config-file probe passed in 329ms. - Earlier on this branch, before these heap-provenance edits: `npm run test:focused -- src/process/ArtifactAnalysisResources.test.ts tests/boundary/captures/captureProvider.test.ts tests/boundary/signals/recordingProvider.test.ts` covered the SIGXCPU/SIGXFSZ wording. Those later edits do not change that path. ## Compatibility, safety, and release - Breaking changes or migration steps: none. Callers that matched the old heap sentence or "Repeating the unchanged request fails the same way." need to expect the new sentences. SIGXCPU/SIGXFSZ now project `retryable: true`. A cleared `process.execArgv` no longer changes the reported heap. A title-overwritten command line no longer reports 128 MiB. A missing command-line reading no longer reports 128 MiB. A percentage flag, env-file launch with no initial `NODE_OPTIONS`, positive `--max-heap-size`, or Node config-file flag no longer reports a MiB that the launch provenance does not establish. - Real Hopper/Ghidra, browser, or OS coverage: not run. Node v22.23.3 worker-heap, `/proc/self/environ`, `/proc/self/cmdline`, title, env-file, `--max-heap-size`, and config-file probes only. - Package or release metadata impact: none. - Security, privacy, process, or containment review: no authority change. The 128 MiB request is still passed to the worker. The text does not invent a MiB from a mutated `process.execArgv`, a title-overwritten command line, a percentage flag, an env file, a positive `--max-heap-size`, or a Node config file. The initial environment block is read only to parse `NODE_OPTIONS`. The command line is read only to recover Node option tokens when a program entry is present. The env file and the Node config file are not read back. ## Author readiness Human owner: @morluto - [x] I reviewed the final diff and relevant existing code, understand the change and its edge cases, and take responsibility for the complete PR, including AI-assisted work. - [x] I checked the evidence above, confirmed it supports the current change, and recorded meaningful validation gaps. - [x] I declare this PR ready for initial maintainer review. ## Before merge - [x] This PR addresses a concrete problem or an agreed enhancement. - [x] Material scope and authority questions are resolved, and any required maintainer scope decision covers the final diff. - [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 output includes representative output. - [x] I checked the final diff for secrets, unrelated cleanup, and unsupported claims. <!-- CURSOR_AGENT_PR_BODY_END --> <div><a href="https://cursor.com/agents/bc-8e83139f-c445-504b-87b2-25b97262d7ef?cursor_ref=pr_footer&cursor_cta=open_in_web"><picture><source media="(prefers-color-scheme: dark)" srcset="https://cursor.com/assets/images/open-in-web-dark.png"><source media="(prefers-color-scheme: light)" srcset="https://cursor.com/assets/images/open-in-web-light.png"><img alt="Open in Web" width="114" height="28" src="https://cursor.com/assets/images/open-in-web-dark.png"></picture></a>&nbsp;<a href="https://cursor.com/background-agent?bcId=bc-8e83139f-c445-504b-87b2-25b97262d7ef&cursor_ref=pr_footer&cursor_cta=open_in_cursor"><picture><source media="(prefers-color-scheme: dark)" srcset="https://cursor.com/assets/images/open-in-cursor-dark.png"><source media="(prefers-color-scheme: light)" srcset="https://cursor.com/assets/images/open-in-cursor-light.png"><img alt="Open in Cursor" width="131" height="28" src="https://cursor.com/assets/images/open-in-cursor-dark.png"></picture></a>&nbsp;</div> --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
bsakaguchi 2026-11-01 21:45:44 +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#7145
No description provided.