[GH-ISSUE #1253] feat(process): support a bounded child finalization interval on capture timeout #448

Closed
opened 2026-10-15 18:25:09 +00:00 by bsakaguchi · 4 comments
Owner

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

Current behavior and use case

Scenario timeout/idle timeout immediately sends SIGKILL. settle_ms is post-exit observation, so increasing it does not let a child write its final diagnostic report. Long reconstruction captures can retain useful periodic files while losing a terminal summary.

Executed reproduction

An owned Node child writes periodic.json, installs a SIGTERM handler that writes final.json, and stays alive. Capture it with timeout_ms:500, idle_timeout_ms:1500, settle_ms:100, and its directory selected for filesystem observation.

On both audited versions, Evidence correctly reports reason:timeout, signal:9, null child exit code and verified cleanup. The selected periodic artifact is retained with a digest; no final artifact is written. A later SDK ping succeeds. This is the current declared forced-timeout behavior, not a demonstrated termination or cleanup bug.

Required outcome

Evaluate a caller-selected, separately bounded finalization interval before forced timeout cleanup. Distinguish the scenario's active-run budget, finalization duration and post-exit settlement. Record signals, deadline outcome, collected observations and final cleanup independently of whether the child cooperates.

Preserve immediate/default behavior where appropriate, ownership checks and a hard overall bound; cancellation semantics require explicit consideration rather than silently delaying every cancellation. Periodic/final artifact selection can use existing filesystem roots and ordinary child scripts without a new orchestration language.

Verify a cooperative finalizer, ignored signals, descendants, finalizer failure, cancellation and exhausted hash budgets. Partial/running files must not become successful terminal reports.

Source and relationship

src/process/capture/ProcessCaptureLifecycle.ts:613. Related #48/#1065; distinct from #1232/#1233 cancellation defects.

Verification and scope

Finite source-owned timeout and artifact-retention probe executed through MCP on both baselines. No hours-long game run was restarted and no successful full-campaign claim is inferred.

Audit date: 2026-10-09. Latest npm dist-tag verified as rea-agents@6.1.0; current main tested at f4c99ab8a53806dd57dc24b3d5a600905bcb4686. Linux x64, Node 24.18.0, MCP client SDK 2.3.1. Native observations use unmodified Ghidra 12.1.4 and JDK 21, two CPUs and a 512 MiB Java heap. No target execution or executable mutation was used for the static native checks.

Originally created by @N0zoM1z0 on GitHub (Oct 9, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/1253 ### Current behavior and use case Scenario timeout/idle timeout immediately sends SIGKILL. `settle_ms` is post-exit observation, so increasing it does not let a child write its final diagnostic report. Long reconstruction captures can retain useful periodic files while losing a terminal summary. ### Executed reproduction An owned Node child writes `periodic.json`, installs a SIGTERM handler that writes `final.json`, and stays alive. Capture it with `timeout_ms:500`, `idle_timeout_ms:1500`, `settle_ms:100`, and its directory selected for filesystem observation. On both audited versions, Evidence correctly reports `reason:timeout`, `signal:9`, null child exit code and verified cleanup. The selected periodic artifact is retained with a digest; no final artifact is written. A later SDK ping succeeds. This is the current declared forced-timeout behavior, not a demonstrated termination or cleanup bug. ### Required outcome Evaluate a caller-selected, separately bounded finalization interval before forced timeout cleanup. Distinguish the scenario's active-run budget, finalization duration and post-exit settlement. Record signals, deadline outcome, collected observations and final cleanup independently of whether the child cooperates. Preserve immediate/default behavior where appropriate, ownership checks and a hard overall bound; cancellation semantics require explicit consideration rather than silently delaying every cancellation. Periodic/final artifact selection can use existing filesystem roots and ordinary child scripts without a new orchestration language. Verify a cooperative finalizer, ignored signals, descendants, finalizer failure, cancellation and exhausted hash budgets. Partial/running files must not become successful terminal reports. ### Source and relationship [src/process/capture/ProcessCaptureLifecycle.ts:613](https://github.com/morluto/rea/blob/f4c99ab8a53806dd57dc24b3d5a600905bcb4686/src/process/capture/ProcessCaptureLifecycle.ts#L613). Related #48/#1065; distinct from #1232/#1233 cancellation defects. ### Verification and scope Finite source-owned timeout and artifact-retention probe executed through MCP on both baselines. No hours-long game run was restarted and no successful full-campaign claim is inferred. Audit date: 2026-10-09. Latest npm dist-tag verified as `rea-agents@6.1.0`; current main tested at `f4c99ab8a53806dd57dc24b3d5a600905bcb4686`. Linux x64, Node 24.18.0, MCP client SDK 2.3.1. Native observations use unmodified Ghidra 12.1.4 and JDK 21, two CPUs and a 512 MiB Java heap. No target execution or executable mutation was used for the static native checks.
Author
Owner

@nikolay-dementiev commented on GitHub (Oct 9, 2026):

I'd like to take this one. I have an implementation ready locally and will open a PR once its verification finishes. The proposed shape, happy to adjust:

  • New optional scenario field finalization_ms (default 0 = today's immediate SIGKILL). It enters the committed scenario identity only when it is greater than 0, so every existing scenario keeps its identity.
  • When timeout_ms or idle_timeout_ms fires and finalization_ms > 0: send SIGTERM once, keep observing, and send SIGKILL after finalization_ms if the target is still alive. The overall bound becomes timeout + finalization + settle.
  • Cancellation stays an immediate SIGKILL, also during an ongoing finalization.
  • Evidence: optional exit.finalization { requested_ms, signal: "SIGTERM", outcome: "target_exited" | "forced_kill", elapsed_ms } in complete and partial captures; exit.reason keeps the initiating deadline, so a cooperative finalizer is not reported as an ordinary target exit.

Does this direction work for you?

<!-- gh-comment-id:6084133790 --> @nikolay-dementiev commented on GitHub (Oct 9, 2026): I'd like to take this one. I have an implementation ready locally and will open a PR once its verification finishes. The proposed shape, happy to adjust: - New optional scenario field `finalization_ms` (default `0` = today's immediate SIGKILL). It enters the committed scenario identity only when it is greater than `0`, so every existing scenario keeps its identity. - When `timeout_ms` or `idle_timeout_ms` fires and `finalization_ms > 0`: send SIGTERM once, keep observing, and send SIGKILL after `finalization_ms` if the target is still alive. The overall bound becomes timeout + finalization + settle. - Cancellation stays an immediate SIGKILL, also during an ongoing finalization. - Evidence: optional `exit.finalization { requested_ms, signal: "SIGTERM", outcome: "target_exited" | "forced_kill", elapsed_ms }` in complete and partial captures; `exit.reason` keeps the initiating deadline, so a cooperative finalizer is not reported as an ordinary target exit. Does this direction work for you?
Author
Owner

@N0zoM1z0 commented on GitHub (Oct 10, 2026):

The opt-in direction is suitable for a focused PR. Keep finalization_ms: 0 behavior and all existing scenario identities unchanged, retain the initiating timeout/idle reason, and keep cancellation immediate even after finalization begins.

Please carry the existing owned-process/group identity checks through both signals, preserve observations if signal delivery or cleanup fails, and validate the combined timeout/finalization/settlement budget at its owning boundary. Report what was observed when the target exits or a signal cannot be delivered rather than inferring a cooperative exit.

Include public CLI/MCP workflows with real cooperating and SIGTERM-ignoring children, cancellation during the grace interval, and the unchanged-default identity case. Update the canonical scenario contract and user examples together. These changes should retain the current supported host boundaries; no extra approval field is needed for an explicitly selected capture.

<!-- gh-comment-id:6096144506 --> @N0zoM1z0 commented on GitHub (Oct 10, 2026): The opt-in direction is suitable for a focused PR. Keep `finalization_ms: 0` behavior and all existing scenario identities unchanged, retain the initiating timeout/idle reason, and keep cancellation immediate even after finalization begins. Please carry the existing owned-process/group identity checks through both signals, preserve observations if signal delivery or cleanup fails, and validate the combined timeout/finalization/settlement budget at its owning boundary. Report what was observed when the target exits or a signal cannot be delivered rather than inferring a cooperative exit. Include public CLI/MCP workflows with real cooperating and SIGTERM-ignoring children, cancellation during the grace interval, and the unchanged-default identity case. Update the canonical scenario contract and user examples together. These changes should retain the current supported host boundaries; no extra approval field is needed for an explicitly selected capture.
Author
Owner

@nikolay-dementiev commented on GitHub (Oct 10, 2026):

Thanks, that is clear. I'll fold these into the PR before opening it:

  • Both SIGTERM and the escalation SIGKILL will go through the existing owned-process identity checks instead of a raw terminal kill; the finalization_ms: 0 path stays exactly as it is.
  • If a signal cannot be delivered or cleanup fails, the collected observations are kept and the delivery result is recorded.
  • exit.finalization will report what was observed (the signals sent, when, and their delivery result, alongside the observed exit) instead of an inferred cooperative outcome.
  • The combined timeout/finalization/settlement budget will be validated in the scenario schema.
  • Public CLI and MCP workflows with a cooperating child, a SIGTERM-ignoring child, cancellation during the grace interval, and the unchanged-default identity case; the canonical scenario contract and the user examples updated together.

No approval field, and the current host boundaries stay as they are.

<!-- gh-comment-id:6096181886 --> @nikolay-dementiev commented on GitHub (Oct 10, 2026): Thanks, that is clear. I'll fold these into the PR before opening it: - Both SIGTERM and the escalation SIGKILL will go through the existing owned-process identity checks instead of a raw terminal kill; the `finalization_ms: 0` path stays exactly as it is. - If a signal cannot be delivered or cleanup fails, the collected observations are kept and the delivery result is recorded. - `exit.finalization` will report what was observed (the signals sent, when, and their delivery result, alongside the observed exit) instead of an inferred cooperative outcome. - The combined timeout/finalization/settlement budget will be validated in the scenario schema. - Public CLI and MCP workflows with a cooperating child, a SIGTERM-ignoring child, cancellation during the grace interval, and the unchanged-default identity case; the canonical scenario contract and the user examples updated together. No approval field, and the current host boundaries stay as they are.
Author
Owner

@nikolay-dementiev commented on GitHub (Oct 10, 2026):

PR is up: #1583, implemented as discussed above.

<!-- gh-comment-id:6099191268 --> @nikolay-dementiev commented on GitHub (Oct 10, 2026): PR is up: #1583, implemented as discussed above.
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#448
No description provided.