[GH-ISSUE #6536] [Bug] capture-process includes its own temporary directory in filesystem effects #3926

Closed
opened 2026-10-31 00:45:18 +00:00 by bsakaguchi · 1 comment
Owner

Originally created by @coygeek on GitHub (Oct 23, 2026).
Original GitHub issue: https://github.com/morluto/rea/issues/6536

Before submitting

  • I searched the open and closed issues for a related report.
  • I removed credentials, tokens, private paths, and sensitive target data.

Area

Process capture or runtime observation

Observed behavior

When filesystem_observation_paths includes REA's host temporary directory, a no-write target records REA's own random rea-process-* directory as created. Two direct captures of the same no-write Node child produced different directory entries, despite normalization.paths: true and complete filesystem coverage. The selected parent directory also appeared modified because its directory size changed.

A third capture with limits.files: 1 fit the initially empty root but reported final filesystem enumeration truncated solely because REA had added its own directory. All three targets exited 0, REA reported verified process cleanup and temporary-root removal, and the observed directory was empty after each call.

These are observations from the shared production capture boundary, with the real Linux PTY and filesystem. The public CLI/MCP adapters were not invoked.

Reproduction

The executed source probe used an empty private directory as the REA process's TMPDIR, kept its script/results outside that directory, and made these calls against the revision below:

// observedRoot is an empty absolute /tmp/rea-process-fixture/observer-temp directory.
// evidenceRoot is its parent; imports come from the source checkout.
process.env.TMPDIR = observedRoot;
for (const files of [10, 10, 1]) {
  const scenario = parseProcessScenario({
    executable: process.execPath,
    arguments: ['-e', 'process.exitCode=0;'],
    working_directory: evidenceRoot,
    filesystem_observation_paths: [observedRoot],
    limits: { files },
    timeout_ms: 3000,
    settle_ms: 30,
    normalization: { paths: true, pids: true, ports: false },
  });
  const result = await captureProcessScenario(scenario);
  // Inspect files_before, files_after, filesystem_effects,
  // truncation_details.filesystem_after, cleanup, and readdir(observedRoot).
}

The first two cases each observed a different REA-created directory. The one-entry case observed filesystem truncation. Each case ran once in one initial sequential probe; no post-draft rerun was performed. Setting only scenario.environment.TMPDIR does not relocate REA's own allocation.

Expected behavior

REA's bookkeeping directory should not introduce an unexplained creation or consume the target's filesystem observation capacity. Preserve the caller's selected root and distinguish observer-owned artifacts from target state, including their effect on parent metadata.

The documented workflow compares direct runs of a caller-selected command. Filesystem checkpoints and effects record selected before/after state. A silent, no-write command should not acquire a different random filesystem creation on each run because of the observer's own preparation.

Impact and scope

P2: bounded evidence contamination for valid observation roots containing REA's temporary allocation. With ample entry capacity it records unrelated random creations; with a tight entry limit it consumes capacity and makes filesystem coverage incomplete even when the target writes nothing. The observed parent-metadata change was on tmpfs; directory-size behavior on other filesystems was not tested.

The ordinary domain comparison of the two stored captures reported filesystem: "changed". Its checkpoints also had different elapsed timestamps, so that comparison alone does not isolate random directory names as its sole cause. The differing recorded directory entries and the one-entry truncation are directly observed independently of that comparison.

No target data loss, cleanup leak, or security failure was observed. Production frequency and macOS behavior are unknown. Relocating the REA process's TMPDIR outside selected roots, while separately selecting the child's temporary environment, is a proposed workaround; it was not tested.

REA version

Source commit e073f64be0a5a30c32d8b3cec23f9bd411080903 on main; package version 6.7.0. The audit coordinator verified 6.7.0 as the latest published npm release. This report concerns the identified source revision, not a separately installed release run.

Runtime and platform

Node.js v24.18.0; Linux 7.2.8-9-omarchy; x86_64; tmpfs fixture. Locked source dependencies loaded with the checkout's tsx loader. Real @lydell/node-pty capture; no external analysis provider.

Target and artifact context

Self-authored Node child containing only process.exitCode=0;. Empty, readable, private temporary observation root. Returned process-capture objects, filesystem checkpoints, coverage, and cleanup outcomes.

Interface

Other

Earliest failure stage

Capture or observation

Relevant output or evidence

Reduced projection from the initial probe; random names are retained exactly:

run-a, files limit 10:
  files_before: [root_0:.]
  files_after:  [root_0:., root_0:rea-process-CQ254V]
  effects: [(root_0:., modified), (root_0:rea-process-CQ254V, created)]
  filesystem_after.enumeration_truncated: false
  truncated: false

run-b, files limit 10:
  files_before: [root_0:.]
  files_after:  [root_0:., root_0:rea-process-jlxFSs]
  effects: [(root_0:., modified), (root_0:rea-process-jlxFSs, created)]
  filesystem_after.enumeration_truncated: false
  truncated: false

one-file-budget:
  files_before: [root_0:.]
  files_after:  [root_0:.]
  filesystem_after.enumeration_truncated: true
  filesystem_after.enumeration_reasons: [files_limit]
  truncated: true

all three:
  exit: {code:0, signal:0, reason:exited}
  cleanup: {owned_process_group:verified, temporary_root:removed}
  observation-root entries after cleanup: []

Evidence provenance: observed production-boundary run. No mocks, filesystem fault injection, or native-provider claims. The initial .ts launcher attempt failed before execution because its module format disallowed top-level await; the unchanged probe was then launched as .mts, producing the observations above. CLI/MCP transport behavior and a relocation workaround were not executed.

Typed outcome or recovery expected

Return the no-write target's selected filesystem observations without an unexplained REA-generated random creation or observer-induced file-limit exhaustion. Retain honest coverage for actual target entries and successful cleanup facts.

Analysis or suspected cause

Source-derived explanation matching the observed artifact:

Additional context

Open and closed GitHub issue searches included repo:morluto/rea is:issue is:open in:title "process" "temporary" and its is:closed counterpart, "TMPDIR" "snapshot" separately by state, "filesystem" "observer-created", "capture" "contaminates", and "filesystem" "rea-process-" "created" with the same repository/issue qualifiers. No matching observer-directory contamination report was found.

Closest reports compared: #6444 concerns missing uncertainty beneath a selected symlink; this fixture uses a real directory with no symlinks. Closed #5140 concerns descriptor-close failure ownership; this probe has successful cleanup. #724 concerns explaining timing differences; this report directly observes random observer-owned filesystem entries and capacity consumption. #59 is the broad process/filesystem capability roadmap, not this defect. Closed #1250 introduced dimension-specific accounting, which correctly exposes the observer-induced exhaustion here.

Last-known-good revision: NOT_ENOUGH_INFO; no historical comparison was run. Prepared with AI assistance. No source changes or fix are included.

Triage assessment

  • Impact level: P2
  • Assessment status: supported
  • Impact basis: Real no-write captures contain different REA-owned directory creations, and a one-entry root becomes filesystem-truncated because of that allocation. The effect is limited to roots containing the observer's temporary directory; capture and cleanup otherwise succeed.
  • Workaround status: unverified
  • Workaround basis: Keeping the REA process's temporary allocation outside selected roots should avoid this artifact while allowing a separately configured child TMPDIR, but no relocation control was executed and feasibility depends on the caller's selected roots.
Originally created by @coygeek on GitHub (Oct 23, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/6536 ## Before submitting - [x] I searched the open and closed issues for a related report. - [x] I removed credentials, tokens, private paths, and sensitive target data. ## Area Process capture or runtime observation ## Observed behavior When `filesystem_observation_paths` includes REA's host temporary directory, a no-write target records REA's own random `rea-process-*` directory as `created`. Two direct captures of the same no-write Node child produced different directory entries, despite `normalization.paths: true` and complete filesystem coverage. The selected parent directory also appeared `modified` because its directory size changed. A third capture with `limits.files: 1` fit the initially empty root but reported final filesystem enumeration truncated solely because REA had added its own directory. All three targets exited 0, REA reported verified process cleanup and temporary-root removal, and the observed directory was empty after each call. These are observations from the shared production capture boundary, with the real Linux PTY and filesystem. The public CLI/MCP adapters were not invoked. ## Reproduction The executed source probe used an empty private directory as the **REA process's** `TMPDIR`, kept its script/results outside that directory, and made these calls against the revision below: ```ts // observedRoot is an empty absolute /tmp/rea-process-fixture/observer-temp directory. // evidenceRoot is its parent; imports come from the source checkout. process.env.TMPDIR = observedRoot; for (const files of [10, 10, 1]) { const scenario = parseProcessScenario({ executable: process.execPath, arguments: ['-e', 'process.exitCode=0;'], working_directory: evidenceRoot, filesystem_observation_paths: [observedRoot], limits: { files }, timeout_ms: 3000, settle_ms: 30, normalization: { paths: true, pids: true, ports: false }, }); const result = await captureProcessScenario(scenario); // Inspect files_before, files_after, filesystem_effects, // truncation_details.filesystem_after, cleanup, and readdir(observedRoot). } ``` The first two cases each observed a different REA-created directory. The one-entry case observed filesystem truncation. Each case ran once in one initial sequential probe; no post-draft rerun was performed. Setting only `scenario.environment.TMPDIR` does not relocate REA's own allocation. ## Expected behavior REA's bookkeeping directory should not introduce an unexplained creation or consume the target's filesystem observation capacity. Preserve the caller's selected root and distinguish observer-owned artifacts from target state, including their effect on parent metadata. The [documented workflow](https://github.com/morluto/rea/blob/e073f64be0a5a30c32d8b3cec23f9bd411080903/docs/process-capture.md#L3-L6) compares direct runs of a caller-selected command. [Filesystem checkpoints and effects](https://github.com/morluto/rea/blob/e073f64be0a5a30c32d8b3cec23f9bd411080903/docs/process-capture.md#L232-L246) record selected before/after state. A silent, no-write command should not acquire a different random filesystem creation on each run because of the observer's own preparation. ## Impact and scope P2: bounded evidence contamination for valid observation roots containing REA's temporary allocation. With ample entry capacity it records unrelated random creations; with a tight entry limit it consumes capacity and makes filesystem coverage incomplete even when the target writes nothing. The observed parent-metadata change was on tmpfs; directory-size behavior on other filesystems was not tested. The ordinary domain comparison of the two stored captures reported `filesystem: "changed"`. Its checkpoints also had different elapsed timestamps, so that comparison alone does not isolate random directory names as its sole cause. The differing recorded directory entries and the one-entry truncation are directly observed independently of that comparison. No target data loss, cleanup leak, or security failure was observed. Production frequency and macOS behavior are unknown. Relocating the REA process's `TMPDIR` outside selected roots, while separately selecting the child's temporary environment, is a proposed workaround; it was not tested. ## REA version Source commit `e073f64be0a5a30c32d8b3cec23f9bd411080903` on main; package version `6.7.0`. The audit coordinator verified `6.7.0` as the latest published npm release. This report concerns the identified source revision, not a separately installed release run. ## Runtime and platform Node.js `v24.18.0`; Linux `7.2.8-9-omarchy`; x86_64; tmpfs fixture. Locked source dependencies loaded with the checkout's `tsx` loader. Real `@lydell/node-pty` capture; no external analysis provider. ## Target and artifact context Self-authored Node child containing only `process.exitCode=0;`. Empty, readable, private temporary observation root. Returned process-capture objects, filesystem checkpoints, coverage, and cleanup outcomes. ## Interface Other ## Earliest failure stage Capture or observation ## Relevant output or evidence Reduced projection from the initial probe; random names are retained exactly: ```text run-a, files limit 10: files_before: [root_0:.] files_after: [root_0:., root_0:rea-process-CQ254V] effects: [(root_0:., modified), (root_0:rea-process-CQ254V, created)] filesystem_after.enumeration_truncated: false truncated: false run-b, files limit 10: files_before: [root_0:.] files_after: [root_0:., root_0:rea-process-jlxFSs] effects: [(root_0:., modified), (root_0:rea-process-jlxFSs, created)] filesystem_after.enumeration_truncated: false truncated: false one-file-budget: files_before: [root_0:.] files_after: [root_0:.] filesystem_after.enumeration_truncated: true filesystem_after.enumeration_reasons: [files_limit] truncated: true all three: exit: {code:0, signal:0, reason:exited} cleanup: {owned_process_group:verified, temporary_root:removed} observation-root entries after cleanup: [] ``` Evidence provenance: observed production-boundary run. No mocks, filesystem fault injection, or native-provider claims. The initial `.ts` launcher attempt failed before execution because its module format disallowed top-level await; the unchanged probe was then launched as `.mts`, producing the observations above. CLI/MCP transport behavior and a relocation workaround were not executed. ## Typed outcome or recovery expected Return the no-write target's selected filesystem observations without an unexplained REA-generated random creation or observer-induced file-limit exhaustion. Retain honest coverage for actual target entries and successful cleanup facts. ## Analysis or suspected cause Source-derived explanation matching the observed artifact: - [Preparation takes the baseline before allocating the temporary root](https://github.com/morluto/rea/blob/e073f64be0a5a30c32d8b3cec23f9bd411080903/src/process/capture/ProcessCaptureLifecycle.ts#L1652-L1658). [The production allocator](https://github.com/morluto/rea/blob/e073f64be0a5a30c32d8b3cec23f9bd411080903/src/process/capture/ProcessCaptureLifecycle.ts#L1720-L1725) creates `rea-process-*` beneath the host `tmpdir()`. - [The final checkpoint](https://github.com/morluto/rea/blob/e073f64be0a5a30c32d8b3cec23f9bd411080903/src/process/capture/ProcessHarness.ts#L537-L548) runs before [finishProcessRun](https://github.com/morluto/rea/blob/e073f64be0a5a30c32d8b3cec23f9bd411080903/src/process/capture/ProcessHarness.ts#L807-L852) reaches [resource release](https://github.com/morluto/rea/blob/e073f64be0a5a30c32d8b3cec23f9bd411080903/src/process/capture/ProcessHarness.ts#L422-L425). - [Directory snapshotting](https://github.com/morluto/rea/blob/e073f64be0a5a30c32d8b3cec23f9bd411080903/src/process/capture/FilesystemSnapshot.ts#L504-L517) records that artifact normally, and [the entry limit](https://github.com/morluto/rea/blob/e073f64be0a5a30c32d8b3cec23f9bd411080903/src/process/capture/FilesystemSnapshot.ts#L543-L555) charges it against the selected capacity. ## Additional context Open and closed GitHub issue searches included `repo:morluto/rea is:issue is:open in:title "process" "temporary"` and its `is:closed` counterpart, `"TMPDIR" "snapshot"` separately by state, `"filesystem" "observer-created"`, `"capture" "contaminates"`, and `"filesystem" "rea-process-" "created"` with the same repository/issue qualifiers. No matching observer-directory contamination report was found. Closest reports compared: [#6444](https://github.com/morluto/rea/issues/6444) concerns missing uncertainty beneath a selected symlink; this fixture uses a real directory with no symlinks. Closed [#5140](https://github.com/morluto/rea/issues/5140) concerns descriptor-close failure ownership; this probe has successful cleanup. [#724](https://github.com/morluto/rea/issues/724) concerns explaining timing differences; this report directly observes random observer-owned filesystem entries and capacity consumption. [#59](https://github.com/morluto/rea/issues/59) is the broad process/filesystem capability roadmap, not this defect. Closed [#1250](https://github.com/morluto/rea/issues/1250) introduced dimension-specific accounting, which correctly exposes the observer-induced exhaustion here. Last-known-good revision: `NOT_ENOUGH_INFO`; no historical comparison was run. Prepared with AI assistance. No source changes or fix are included. ## Triage assessment - Impact level: P2 - Assessment status: supported - Impact basis: Real no-write captures contain different REA-owned directory creations, and a one-entry root becomes filesystem-truncated because of that allocation. The effect is limited to roots containing the observer's temporary directory; capture and cleanup otherwise succeed. - Workaround status: unverified - Workaround basis: Keeping the REA process's temporary allocation outside selected roots should avoid this artifact while allowing a separately configured child TMPDIR, but no relocation control was executed and feasibility depends on the caller's selected roots.
Author
Owner

@morluto commented on GitHub (Oct 23, 2026):

Resolved by github.com/morluto/rea@2f6e0506f0. Observer allocation precedes baseline; exact physical observer identity is excluded before capacity accounting. Tests check unchanged effects at files=1 and parent aliases while retaining same-name target directories. Validation: src/process/capture/FilesystemCoverage.test.ts passed in the recorded round2/3 execution; this review did not rerun tests. Coverage is limited to the cited control; no broader real-provider, Windows or performance claim.

<!-- gh-comment-id:6376109310 --> @morluto commented on GitHub (Oct 23, 2026): Resolved by https://github.com/morluto/rea/commit/2f6e0506f0845ce13b438d101691d8a44e8ff740. Observer allocation precedes baseline; exact physical observer identity is excluded before capacity accounting. Tests check unchanged effects at files=1 and parent aliases while retaining same-name target directories. Validation: `src/process/capture/FilesystemCoverage.test.ts` passed in the recorded round2/3 execution; this review did not rerun tests. Coverage is limited to the cited control; no broader real-provider, Windows or performance claim.
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#3926
No description provided.