You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
flake: rpc-host-gc 'removes an uncleanly killed endpoint' finds nothing to remove (CI, unrelated PR) #4119
packages/coding-agent/test/rpc-host-gc.test.ts: the case gc against real supervised hosts > removes an uncleanly killed endpoint, its socket and its siblings, and keeps the live one beside it failed once in CI on a PR that touches no RPC host or gc code.
In the 8 most recent failed CI runs on other branches, this case did not fail.
Expected
The case passes deterministically. gc removes the killed endpoint's record, socket and siblings, and keeps the live host.
Hypothesis (unverified)
The test drives real supervised host processes. If gc runs before the killed host's death is observable (its pid still answers, or the lock or socket is not released yet), gc correctly treats the endpoint as live and removes nothing. That would make the case timing-dependent on a loaded runner, like #2876 (a different case in the same file).
Sibling in the same family flaked on CI for an unrelated PR (senpi#4109, which touches only the subscription-limit store): test/rpc-host-gc.test.ts > gc against real supervised hosts > removes an endpoint whose supervisor alone was SIGKILLed, after its host child's watchdog cleaned up, expected removed:[endpoint], got kept:[endpoint]. Run: https://github.com/code-yeongyu/senpi/actions/runs/40051880459/job/120746130698
Root cause. gc read a SIGKILLed supervisor or host child as still running while it was exited but not yet reaped (state Z): kill(pid, 0) succeeds for a zombie and ps -o lstart= still prints its start time, so the generation pidfile matched as live and gc kept the dead endpoint as live_generation. On a loaded runner the reap could land after gc read the pidfile, which is why this flaked rather than failed every time.
Fix. The identity probe reads state and start time from one ps snapshot and treats state Z as absent; pidfile matching, the host-child check, session-path claim ownership and process-group ownership all treat an unreaped process as exited. Process-group cleanup keeps its previous verdict for a zombie leader (sweep).
Evidence.
Deterministic RED with a real held zombie in a generation host.pid / host-child.pid and as a claim owner: before the fix gc returned removed: [] with live_generation / live_claim, the diff in this issue; reverting only the source change makes every new case fail again.
Both cases named here passed 30/30 under three parallel load lanes on macOS; the related real-host suites passed, and CI was green at the merged head.
The separate flake #2876 (recreated endpoint.json case) is unrelated: an A/B under identical load showed the same rate with and without this change.
Activity
code-yeongyu commentedon Oct 24, 2026
Sibling in the same family flaked on CI for an unrelated PR (senpi#4109, which touches only the subscription-limit store):
test/rpc-host-gc.test.ts > gc against real supervised hosts > removes an endpoint whose supervisor alone was SIGKILLed, after its host child's watchdog cleaned up, expected removed:[endpoint], got kept:[endpoint]. Run: https://github.com/code-yeongyu/senpi/actions/runs/40051880459/job/120746130698fix(rpc): treat an unreaped leader and claim owner as exited too (#4119…
code-yeongyu commentedon Oct 25, 2026
Fixed by #4160 (merge commit
f8eeec6a03).Root cause. gc read a SIGKILLed supervisor or host child as still running while it was exited but not yet reaped (state Z):
kill(pid, 0)succeeds for a zombie andps -o lstart=still prints its start time, so the generation pidfile matched as live and gc kept the dead endpoint aslive_generation. On a loaded runner the reap could land after gc read the pidfile, which is why this flaked rather than failed every time.Fix. The identity probe reads state and start time from one
pssnapshot and treats state Z as absent; pidfile matching, the host-child check, session-path claim ownership and process-group ownership all treat an unreaped process as exited. Process-group cleanup keeps its previous verdict for a zombie leader (sweep).Evidence.
host.pid/host-child.pidand as a claim owner: before the fix gc returnedremoved: []withlive_generation/live_claim, the diff in this issue; reverting only the source change makes every new case fail again.The separate flake #2876 (recreated
endpoint.jsoncase) is unrelated: an A/B under identical load showed the same rate with and without this change.fix(rpc): gc no longer counts an exited, unreaped host as a live gene…