Skip to content

fix(probe): signal the app's group through /proc, not a negative pid - #32

Merged
gjtorikian merged 4 commits into
mainfrom
fix/probe-stop-escalates-to-kill
Oct 15, 2026
Merged

gjtorikian merged 4 commits into
mainfrom
fix/probe-stop-escalates-to-kill

Conversation

@gjtorikian

@gjtorikian gjtorikian commented Oct 15, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

  • stopBackgroundApp now finds every process in the app's group by reading /proc/<pid>/stat and signals each one: SIGTERM, up to five seconds for the group to empty, then SIGKILL. Where /proc is absent (a host runtime on macOS) it keeps the shell's negative-pid group kill.
  • Root cause: the sandbox images run sh = dash, whose builtin kill rejects a negative pid with "Illegal number", so the group signal kill -TERM -- -<pgid> the harness has always sent failed and its fallback terminated only the group leader. Replaying the probe lifecycle inside quickstudy/workos-nextjs showed next dev and its start-server child still alive and bound to port 3000 thirty seconds after the stop; the next probe then booted on another port while the stale server answered the probe's requests.
  • Seen in the hosted oracle run of the two-product authkit-webhooks eval (workos/evals#26, run 01M50M4XPG5KR3WKQ2WC4DY5HW): the Next.js attempt passed the login probe and answered 500 to every later probe, routes the stale server had never compiled. The consumer's waitForAppStopped waits 30 s for exactly this and then gives up; the harness should finish the job.
  • Tests: the shell contract is pinned offline, and a behavioural case starts a TERM-ignoring process group and asserts it is gone after stop (runs where setsid exists, i.e. Linux and the sandbox images; skips on macOS). Verified in oven/bun:1.

Next.js's `next dev` keeps its `start-server` worker bound to the port
after the parent handles SIGTERM, so stopBackgroundApp returned with
the old server still answering. The next probe then booted on another
port while the stale server took the probe's requests and answered 500
for routes it had never compiled — the two-product eval's oracle passed
the login probe and failed every probe after it on Next.js. The stop
now gives the group five seconds after TERM and then SIGKILLs it.
The inline start command carried single quotes that the launcher's
own sh -c '…' wrapper cut short, so the background job kept the test's
stdout pipe open and the test timed out on Linux.
@greptile-apps

greptile-apps Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

RetriggerView in GreptileConfidence Score: 5/5

[Medium impact] The PR appears safe to merge, with both previous test issues addressed.

Summary

stopBackgroundApp finds live group members through /proc, sends SIGTERM, waits up to five seconds, and sends SIGKILL to survivors.

  • Probe shutdown now stops every process in the app’s group.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart TD
  A[Read saved app PID] --> B{PID present?}
  B -->|No| C[Return]
  B -->|Yes| D[Send SIGTERM to group members]
  D --> E[Wait up to five seconds for live members]
  E --> F[Send SIGKILL to remaining members]
Loading

Reviews (3) · Last reviewed commit: "test(probe): clean up the stubborn group..." · Reviewed by Greptile

Comment thread src/probe.ts Resolved
Comment thread tests/probe-stop.test.ts Resolved
Comment thread tests/probe-stop.test.ts Resolved
dash's builtin kill rejects a negative pid ("Illegal number"), so the
group signal `kill -- -<pgid>` never worked on the Debian-based
sandbox images: the fallback terminated the group leader alone and a
dev server's worker lived on. The stop now enumerates the group from
/proc and signals each member, TERM first and KILL five seconds later,
keeping the negative-pid form only where /proc is absent.
@gjtorikian gjtorikian changed the title fix(probe): kill the app's process group when TERM leaves it running fix(probe): signal the app's group through /proc, not a negative pid Oct 15, 2026
The behavioural test now kills its own group and removes its script in
a finally block, polls up to five seconds for the group to vanish
after SIGKILL instead of asserting at once, and both it and the stop
ignore zombies: a killed process can sit unreaped in /proc for a
moment, and an unreaped process is not a running server.
@gjtorikian
gjtorikian merged commit 1820860 into main Oct 15, 2026
13 checks passed
@gjtorikian
gjtorikian deleted the fix/probe-stop-escalates-to-kill branch October 15, 2026 21:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant