diff --git a/.debugging/status-timeout-hang.md b/.debugging/status-timeout-hang.md deleted file mode 100644 index e98ed4d6d..000000000 --- a/.debugging/status-timeout-hang.md +++ /dev/null @@ -1,100 +0,0 @@ -# Debugging Journal: session.status() Infinite Hang - -**Date:** 2026-05-16 -**Branch:** fix/status-timeout-hang -**Worktree:** mimo v2.5 pro -**Severity:** Critical — deadlocks entire plugin event chain - ---- - -## Phase 0 — Environment - -- **Runtime:** Bun 1.3.x (TypeScript, strict mode) -- **Host:** OpenCode plugin system (`oh-my-opencode`) -- **Symptom:** Plugin hangs forever during prompting — no hooks fire, no events processed, system becomes unresponsive -- **Reporter:** User reports "갑자기 프롬프팅하다가 뻗어버리는" (suddenly hangs while prompting) - ---- - -## Phase 1 — Hypotheses - -### H1: `isSessionActive()` has no timeout — CONFIRMED - -`isSessionActive()` in `src/shared/session-idle-settle.ts` calls `client.session.status()` with NO timeout wrapper. The SDK client (`packages/sdk/js/src/client.ts`) explicitly disables fetch timeout: - -```typescript -const customFetch: any = (req: any) => { - req.timeout = false // DISABLES TIMEOUT - return fetch(req) -} -``` - -If the opencode server is slow or unresponsive, this call hangs forever. - -### H2: Sequential `await` in `dispatchToHooks` propagates hang — CONFIRMED - -`src/plugin/event.ts` calls hooks sequentially: - -```typescript -const dispatchToHooks = async (input: EventInput): Promise => { - await runEventHookSafely("todoContinuationEnforcer", ...) - await runEventHookSafely("runtimeFallback", ...) - await runEventHookSafely("atlasHook", ...) - // ... 20+ hooks, ALL sequential await -} -``` - -`runEventHookSafely` catches errors but has NO timeout. If any hook hangs, the entire chain is blocked. - -### H3: Reservation deadlock — CONFIRMED (secondary effect) - -In `dispatchAfterSessionIdle()`: -1. Reservation is set BEFORE `isSessionActive()` call -2. If `isSessionActive()` hangs, the `finally` block never runs -3. Reservation is never released -4. All subsequent calls for the same sessionID return "reserved" - -### H4: Multiple hooks trigger same hang path — CONFIRMED - -At least 6 hooks call `promptAsyncAfterSessionIdle` or `shouldPromptAfterSessionIdle`: -- `todoContinuationEnforcer` (main session continuation) -- `atlasHook` (boulder session continuation) -- `ralphLoop` (ralph loop continuation) -- `runtimeFallback` (error recovery) -- `sessionRecovery` (session resume) -- `teamIdleWakeHint` (team mode wake) - ---- - -## Root Cause - -`isSessionActive()` in `session-idle-settle.ts` has no timeout on `client.session.status()`. The SDK client disables fetch timeout. When opencode server is slow/unresponsive: - -1. `isSessionActive()` hangs forever -2. `dispatchAfterSessionIdle()` hangs (reservation never released) -3. `dispatchToHooks()` hangs (sequential await blocks all subsequent hooks) -4. Plugin becomes completely unresponsive - ---- - -## Fix Plan - -Add timeout wrapper to `isSessionActive()`. If status call exceeds threshold (5s), treat session as inactive (return false). This is safe because: -- If the server is unresponsive, we can't trust the status anyway -- Returning false lets the caller proceed (dispatch or skip) -- The reservation mechanism prevents duplicate dispatches even with false negatives - -**Files to change:** -- `src/shared/session-idle-settle.ts` — add timeout to `isSessionActive()` -- `src/hooks/shared/session-idle-settle.test.ts` — add timeout behavior test -- `src/hooks/shared/prompt-async-gate.test.ts` — add status-timeout recovery test - ---- - -## Verification - -- [ ] Existing tests pass (no behavioral change for normal flow) -- [ ] New test: `isSessionActive` returns false when status call hangs -- [ ] New test: `dispatchAfterSessionIdle` recovers from status timeout -- [ ] `bun test` green -- [ ] `bun run typecheck` green