[GH-ISSUE #5194] [Bug] Sampler shutdown can hide an interrupted process observation #3186

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

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

Area

Process capture or runtime observation

Observed behavior

On e8da7e1edd075feec0e6beedec93d5645fed8125, stopping an in-flight process sampler can abandon a known process observation and return sampling_partial:false, sampling_failures:0, and a null first failure. The failed observation is the sampler's own cancelled read, so recording an I/O error would also misrepresent what happened.

This is a deterministic source-boundary reproduction with synthetic Linux procfs data on macOS / Node 24.18.0. It does not establish natural Linux permission behavior or event-complete process sampling.

Reproduction

  1. Use the production startProcessSampler with a stable synthetic root PID 100, matching group/session identity and run token. Root /proc/100/stat reads succeed.
  2. Hold /proc/100/task/100/children after the sampler issues that read. The filesystem mock acknowledges issuance and rejects only when its passed signal aborts, using AbortError with cause === signal.reason.
  3. Await that acknowledgement, then await stop(). All reads and sampler cleanup settle.
  4. Observe that the root's identity bracket cannot finish and no process row is retained, yet process coverage reports no partial observation or failure.

Executed the dedicated regression against an isolated baseline worktree. The assertion that coverage is partial failed (expected false to be true); the matching read-cancellation control completed without an I/O failure. The same test passes with a separate interruption fact in the working correction.

Expected behavior

Distinguish an actually interrupted observation or unfinished traversal from normal shutdown after a completed pass. Retain completed rows and await issued reads. Report interruption separately from permission/I/O failures, propagate incomplete process coverage to comparison/truncation, and preserve unaffected dimensions. Coverage remains sampled, not event-complete.

Impact and scope

The observed sampler gap can be presented as a failure-free completed pass. Capture validation, aggregate truncation and process comparison currently only account for sample limits or failures; that propagation is source-derived. No real Linux target or Windows run was performed. Natural occurrence frequency is NOT_ENOUGH_INFO.

REA version

6.7.0; baseline e8da7e1edd075feec0e6beedec93d5645fed8125.

Source-derived cause

src/process/capture/ProcessSampling.ts aborts the current pass during stop(), suppresses the resulting AbortError, and returns no interruption fact. Linux task/BFS traversal can also stop with an unexamined suffix. Found while following #5081 into finalization and coverage consumers. Open and closed issues were searched for sampling/interruption reports.

Originally created by @morluto on GitHub (Oct 19, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/5194 ### Area Process capture or runtime observation ### Observed behavior On `e8da7e1edd075feec0e6beedec93d5645fed8125`, stopping an in-flight process sampler can abandon a known process observation and return `sampling_partial:false`, `sampling_failures:0`, and a null first failure. The failed observation is the sampler's own cancelled read, so recording an I/O error would also misrepresent what happened. This is a deterministic source-boundary reproduction with synthetic Linux procfs data on macOS / Node 24.18.0. It does not establish natural Linux permission behavior or event-complete process sampling. ### Reproduction 1. Use the production `startProcessSampler` with a stable synthetic root PID 100, matching group/session identity and run token. Root `/proc/100/stat` reads succeed. 2. Hold `/proc/100/task/100/children` after the sampler issues that read. The filesystem mock acknowledges issuance and rejects only when its passed signal aborts, using `AbortError` with `cause === signal.reason`. 3. Await that acknowledgement, then await `stop()`. All reads and sampler cleanup settle. 4. Observe that the root's identity bracket cannot finish and no process row is retained, yet process coverage reports no partial observation or failure. Executed the dedicated regression against an isolated baseline worktree. The assertion that coverage is partial failed (`expected false to be true`); the matching read-cancellation control completed without an I/O failure. The same test passes with a separate interruption fact in the working correction. ### Expected behavior Distinguish an actually interrupted observation or unfinished traversal from normal shutdown after a completed pass. Retain completed rows and await issued reads. Report interruption separately from permission/I/O failures, propagate incomplete process coverage to comparison/truncation, and preserve unaffected dimensions. Coverage remains sampled, not event-complete. ### Impact and scope The observed sampler gap can be presented as a failure-free completed pass. Capture validation, aggregate truncation and process comparison currently only account for sample limits or failures; that propagation is source-derived. No real Linux target or Windows run was performed. Natural occurrence frequency is `NOT_ENOUGH_INFO`. ### REA version 6.7.0; baseline `e8da7e1edd075feec0e6beedec93d5645fed8125`. ### Source-derived cause `src/process/capture/ProcessSampling.ts` aborts the current pass during `stop()`, suppresses the resulting AbortError, and returns no interruption fact. Linux task/BFS traversal can also stop with an unexamined suffix. Found while following #5081 into finalization and coverage consumers. Open and closed issues were searched for sampling/interruption reports.
bsakaguchi 2026-10-31 00:37:37 +00:00
  • closed this issue
  • added the
    bug
    label
Author
Owner

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

Fixed on main in ba866a26ecbe179a717e746cbc0d1605ae616ad5, alongside #5081.

The sampler now carries interruption as an internal coverage fact through identity reads, initialization, thread enumeration and process traversal. A completed pass stays complete when stopped; interrupted reads or an unexamined suffix make coverage partial. Already completed identity brackets remain available, and issued reads settle before final coverage. Unrelated AbortErrors remain read failures; actual read failures retain their diagnostic when shutdown also interrupts the pass.

Public capture shape remains unchanged. sampling_failures includes at most one interrupted pass, with explicit shutdown context in first_sampling_failure; it does not invent an OS permission or I/O error. The previous strict reader accepts both fresh CLI output and an interrupted-coverage schema fixture. Comparisons mark process coverage unknown while retaining unaffected dimensions.

Baseline interruption coverage reports false; the corresponding regression now passes. Final validation: 123 tests / 12 files, full static and documentation checks, and test discovery. Tests include a completed sibling surviving cancellation, a non-cancellable issued read that fails after stop, and an entirely completed pass. Real macOS source-owned PTY CLI/MCP controls ran; Linux permission policy and Windows remain unverified. No directly related cleanup remains identified beyond awaiting native filesystem operations that do not expose cancellation.

<!-- gh-comment-id:6261842398 --> @morluto commented on GitHub (Oct 19, 2026): Fixed on `main` in [ba866a26ecbe179a717e746cbc0d1605ae616ad5](https://github.com/morluto/rea/commit/ba866a26ecbe179a717e746cbc0d1605ae616ad5), alongside #5081. The sampler now carries interruption as an internal coverage fact through identity reads, initialization, thread enumeration and process traversal. A completed pass stays complete when stopped; interrupted reads or an unexamined suffix make coverage partial. Already completed identity brackets remain available, and issued reads settle before final coverage. Unrelated AbortErrors remain read failures; actual read failures retain their diagnostic when shutdown also interrupts the pass. Public capture shape remains unchanged. `sampling_failures` includes at most one interrupted pass, with explicit shutdown context in `first_sampling_failure`; it does not invent an OS permission or I/O error. The previous strict reader accepts both fresh CLI output and an interrupted-coverage schema fixture. Comparisons mark process coverage unknown while retaining unaffected dimensions. Baseline interruption coverage reports false; the corresponding regression now passes. Final validation: 123 tests / 12 files, full static and documentation checks, and test discovery. Tests include a completed sibling surviving cancellation, a non-cancellable issued read that fails after stop, and an entirely completed pass. Real macOS source-owned PTY CLI/MCP controls ran; Linux permission policy and Windows remain unverified. No directly related cleanup remains identified beyond awaiting native filesystem operations that do not expose cancellation.
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#3186
No description provided.