From c639a848c2953d92b1f111c124be0ddd007f4d83 Mon Sep 17 00:00:00 2001 From: Snider Date: Tue, 17 Mar 2026 05:56:22 +0000 Subject: [PATCH] fix: PID polling fallback for process completion detection MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit proc.Wait() hangs when Claude Code's child processes inherit pipes. Added PID polling every 5s — when the main process is dead (Signal(0) fails), force completion even if pipes are still open. Fixes: empty agent logs, missing completion events, stuck queue drain. Co-Authored-By: Virgil --- pkg/agentic/dispatch.go | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/pkg/agentic/dispatch.go b/pkg/agentic/dispatch.go index 85e3e33..b54429a 100644 --- a/pkg/agentic/dispatch.go +++ b/pkg/agentic/dispatch.go @@ -8,6 +8,7 @@ import ( "os" "path/filepath" "strings" + "syscall" "time" coreio "forge.lthn.ai/core/go-io" @@ -115,7 +116,34 @@ func (s *PrepSubsystem) spawnAgent(agent, prompt, wsDir, srcDir string) (int, st pid := proc.Info().PID go func() { - proc.Wait() + // Wait for process exit with PID polling fallback. + // go-process Wait() can hang if child processes inherit pipes. + // Poll the PID every 5s — if the process is gone, force completion. + done := make(chan struct{}) + go func() { + proc.Wait() + close(done) + }() + + ticker := time.NewTicker(5 * time.Second) + defer ticker.Stop() + for { + select { + case <-done: + goto completed + case <-ticker.C: + // Check if main process is still alive + p, err := os.FindProcess(pid) + if err != nil { + goto completed + } + if err := p.Signal(syscall.Signal(0)); err != nil { + // Process is dead — force cleanup + goto completed + } + } + } + completed: // Write captured output to log file if output := proc.Output(); output != "" {