@@ -309,6 +309,47 @@ describe("BackgroundProcessManager", () => {
309309 } ) ;
310310 } ) ;
311311
312+ describe ( "process group termination" , ( ) => {
313+ it ( "should terminate child processes when parent is killed" , async ( ) => {
314+ // This test validates that set -m creates a process group where PID === PGID,
315+ // allowing kill -PID to terminate the entire process tree.
316+
317+ // Spawn a parent that creates a child process
318+ // The parent runs: (sleep 60 &); wait
319+ // This creates: parent bash -> child sleep
320+ const result = await manager . spawn ( runtime , testWorkspaceId , "bash -c 'sleep 60 & wait'" , {
321+ cwd : process . cwd ( ) ,
322+ } ) ;
323+
324+ expect ( result . success ) . toBe ( true ) ;
325+ if ( ! result . success ) return ;
326+
327+ // Give the child process time to start
328+ await new Promise ( ( resolve ) => setTimeout ( resolve , 100 ) ) ;
329+
330+ // Verify process is running
331+ const procBefore = await manager . getProcess ( result . processId ) ;
332+ expect ( procBefore ?. status ) . toBe ( "running" ) ;
333+
334+ // Terminate - this should kill both parent and child via process group
335+ await manager . terminate ( result . processId ) ;
336+
337+ // Verify parent is killed
338+ const procAfter = await manager . getProcess ( result . processId ) ;
339+ expect ( procAfter ?. status ) . toBe ( "killed" ) ;
340+
341+ // Wait a moment for any orphaned processes to show up
342+ await new Promise ( ( resolve ) => setTimeout ( resolve , 500 ) ) ;
343+
344+ // Verify no orphaned sleep processes from our test
345+ // (checking via ps would be flaky, so we rely on the exit code being set,
346+ // which only happens after the entire process group is dead)
347+ const exitCode = procAfter ?. exitCode ;
348+ expect ( exitCode ) . not . toBeNull ( ) ;
349+ expect ( exitCode ) . toBeGreaterThanOrEqual ( 128 ) ; // Signal exit code
350+ } ) ;
351+ } ) ;
352+
312353 describe ( "exit_code file" , ( ) => {
313354 it ( "should write exit_code file when process exits" , async ( ) => {
314355 const result = await manager . spawn ( runtime , testWorkspaceId , "exit 42" , {
0 commit comments