Repository navigation
fix(opencode): restarted server stops an orphaned OpenCode server started through a wrapper - #34959
Conversation
The reaper skips a wrapper's server group when the ledger recorded the forked child before it exec'd, because the recorded command line no longer matches. Gate the child on a fifo so the test records it pre-exec. Fails on main; reported in #22324 item 4. AI-assisted; verified with the tests and tools listed above.
…rted through a wrapper The ledger recorded the member's full command line, which for a wrapper's forked child can be the pre-exec line, and the reaper required an exact match, so it treated the orphan as a recycled pid and left it running. Record the serve arguments instead and match on them both when tracking and when reaping. Fixes #22324 AI-assisted; verified with the tests and tools listed above.
Keep the version-1 entry format with its full command line and add the serve arguments as an optional field. The reaper stops a recorded group when the command line still matches exactly or, for entries that carry them, when it still ends with the serve arguments. Entries written by an older server still decode and are reaped as before, and a wrapper that appends its own options is still recorded and stopped by its command line. AI-assisted; verified with the tests and tools listed above.
ApprovabilityVerdict: Approved at Macroscope's review found this PR approvable — Orphan cleanup now recognizes wrapper-started OpenCode servers after their command line changes during exec. Existing owner, process identity, and process-group checks keep cleanup confined to recorded orphaned servers. Notes:
You can add or adjust custom eligibility rules. Learn more. |
Thread transfer impact✅ Thread transfer remains within every enforced ceiling.
Other scenariosAll 64 scenario measurements match All 64 scenario measurements
Baseline: Scenario and decoded snapshot size10 historical turns, 5 command tools per turn, 878.9 KiB retained MCP result per historical turn, and a 1.05 MiB retained result in the measured turn.
Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed. |
|
No actionable comments were generated in the recent review. Walkthrough
Merge Risk:
|
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Dismissing prior approval to re-evaluate 7be89fd
…rted through a wrapper (pingdotgg#34959) (cherry picked from commit d0957bc)
When OpenCode is launched through a wrapper, the ledger can record the server process while it still carries the wrapper's command line. After it execs, the full command no longer matches, so a restarted T3 server treated the group as a recycled pid and left the orphaned OpenCode server running. Ledger entries now also store the serve arguments (optional field, so version-1 entries without it still decode). On reap, a group is stopped when pid, start time and pgid match and either the exact command line or the saved serve arguments (as the command's suffix) match; the exact-command fallback is kept for wrappers that append their own options and for legacy entries. Author's approach kept unchanged; main merged in cleanly (merge commit), and one follow-up commit removes the test helper's now-unused
ready/printedsignal, since the wrapper test now synchronizes through fifos. The new wrapper test fails on main (Linux and Darwin observers) and passes with the fix. The legacy-entry and appended-options tests pass on main too; they guard the compatibility paths the competing PR #22209 removes. Note for the PR body: legacy ledger entries are retained and reaped by exact command, not dropped.Fixes #22324
Reproduction on main
Proposed real-app procedure, not executed: On Linux or macOS, configure an OpenCode provider instance's Binary path to a wrapper that forks a child, exits immediately, and has the child wait on a FIFO before exec'ing the real OpenCode executable with the original arguments. Start an OpenCode thread in T3 Code; while startup is waiting, confirm the server has written the group's ledger entry, then release the FIFO and let OpenCode finish starting. Record the OpenCode PID and port, forcibly terminate only the owning T3 Code server to bypass graceful cleanup, and restart T3 Code with the same state directory. On main, the old OpenCode process and listening port remain; with this change, restart cleanup stops that recorded group. The gated fixture makes the otherwise intermittent fork-to-exec race deliberate.
Evidence
Real T3 server and OpenCode 1.19.5 on Linux, fresh synthetic state on each side. Launch an OpenCode thread through a FIFO-gated forking wrapper; confirm the ledger records its child before exec; release the gate and observe the same PID/start time/group running OpenCode on its recorded port; crash only T3 and restart with the same state. Both runs use a 200ms spawn-return delay, disable systemd agent scopes, and pause T3 after ledger creation while OpenCode boots to prevent separate wrapper-exit handling from racing the crash. No application source or ledger edits.
Driver · Complete fixture sources, original terminal recordings, transcripts and sanitized server logs. Videos are rendered from timestamped live terminal output.
Not shown:
Tests
vp test run src/server/OpenCodeServerLedger.test.ts (packages/provider-opencode): 16/16 pass. Same tests with main's OpenCodeServerLedger.ts: 2 failed ('records a group whose spawned wrapper already exited', linux and darwin observers), 14 passed. vp run check:changed: format, lint, knip, typecheck (10 packages), tests (2 files, 18 tests) all pass.
Not in this change
Who did the work
Run by the t3code factory in T3 Code.