code-yeongyu / code-yeongyu/senpi
senpi-codemode: py kernel orphaned forever when parent cli.js dies (detached spawn, no parent-death watchdog)
- Dominant language
- TypeScript
- Stars
- 429
- Forks
- 98
- Avg merge
- 5h 3m
- Merged PRs (30d)
- 526
Description
## Bug
`senpi-codemode`'s Python kernel is spawned with `detached: true` (`packages/senpi-codemode/src/kernels/py/process.ts`, `defaultSpawn`). When the parent `senpi`/`cli.js` process dies without a clean shutdown — terminal closed, frontend killed, crash — the kernel is not in the parent's process group, receives no SIGHUP, and is reparented to PID 1 where it sleeps forever. The same applies to the `cli.js` session backend itself and its `lsp-daemon` child: the whole subtree outlives the session.
## Repro
1. Start a senpi session, run one `eval` cell (spawns `prelude.py` kernel).
2. Kill the session's frontend/parent (`kill -9` on the `omo`/`senpi` wrapper, or close the terminal).
3. `ps -eo pid,ppid,cmd | grep prelude.py` → kernel still alive with `PPID 1`.
Observed on two production hosts (Linux, Bun 1.4.x, senpi 2026.9.13): orphaned `cli.js` backends and `prelude.py` kernels accumulated for days; one backend even survived SIGTERM and needed SIGKILL. Compounding factor: the Bun parent does not reap children via SIGCHLD (pidfd-based), so even non-detached children can linger.
## Why it matters
Every abnormal session exit leaks a kernel + backend + LSP daemon. On long-lived servers this is a slow resource leak that currently requires an external reaper cron job to clean up.
## Suggested fix
Add a parent-death watchdog inside `prelude.py` (or the kernel bootstrap): on Linux `prctl(PR_SET_PDEATHSIG, SIGTERM)` via ctypes, plus a portable fallback thread polling `os.getppid() == 1` → `os._exit(0)`. This keeps `detached: true` semantics (intentional process-group separation) while guaranteeing the kernel dies with its session. The same watchdog pattern could cover the `lsp-daemon` child.
Happy to implement this — let me know if you'd prefer the watchdog in `prelude.py` vs. a Node-side `pidfd`/parent-monitor approach.
Contributor guide
Research direction
Start with packages/senpi-codemode/src/kernels/py/process.ts and its defaultSpawn configuration, then inspect the prelude.py kernel bootstrap and the lsp-daemon child lifecycle. Reproduce the issue by running an eval cell, killing the frontend or parent, and checking process ancestry with ps. Done means abnormal session termination leaves no kernel, backend, or LSP daemon running while detached process-group semantics remain intact.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- bun, python, typescript
- Domain
- backend, cli, operating-systems
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100