Research: issue-209-research.md
Stacked on: PR #208 (fix/write-stabilize-non-fatal-phase1)
Send() hangs indefinitely when ReadyForInitialPrompt never returns true, even though Status() reports "stable". This is because statusLocked() doesn't check initialPromptReady, allowing Send() to enqueue messages, but the stableSignal (which the send loop waits on) requires initialPromptReady == true. If readiness never arrives, the signal never fires and Send() blocks forever. (See research: Problem Context)
The real-world trigger is Claude Code v2.1.87's onboarding screen using ╌ (U+254C) instead of ─ (U+2500) in its box-drawing characters, causing message box detection to fail. (See research: Code Analysis — findGreaterThanMessageBox)
-
Guard
statusLocked()withinitialPromptReadycheck AND fix detection.- Options: (A) guard statusLocked only, (B) decouple stableSignal, (C) fix detection only, (D) both A+C.
- Chosen: D — both guard and detection.
- Classification: Agent-recommended (issue author also recommends option D).
- Reasoning: The
stableSignalgates ALL outbound messages oninitialPromptReady.statusLocked()must reflect this — otherwise status says "stable" but the system cannot process messages. The detection fix handles the immediate trigger; the guard prevents the class of bugs.
-
Return
ConversationStatusChanging(notInitializing) wheninitialPromptReadyis false.- Options:
changingvsinitializing. - Chosen:
changing. - Classification: Agent-recommended.
- Reasoning: The snapshot buffer IS full (past the
initializingphase). The errorErrMessageValidationChangingsays "message can only be sent when the agent is waiting for user input" — which is semantically correct when readiness hasn't been detected.
- Options:
-
Apply the guard unconditionally (not only when
InitialPromptis configured).- Options: Conditional (only when InitialPrompt set) vs unconditional.
- Chosen: Unconditional.
- Classification: Agent-recommended.
- Reasoning: The
stableSignalgates oninitialPromptReadyfor ALL queued messages, not just the initial prompt. IfinitialPromptReadyis false and a user callsSend(), the message hangs regardless of whetherInitialPromptis configured. Status must reflect actual send capability. WhenReadyForInitialPromptis nil (the default), it auto-returnstrueandinitialPromptReadybecomestrueon the first snapshot tick — before status could ever transition tostable. So the unconditional guard causes no regressions for the default case. (See research: Approach A — Nuance)
-
Add
╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌(U+254C repeated) as an alternative pattern in message box detection.- Options: just U+254C, also U+254D, broad set of horizontal box-drawing characters.
- Chosen: U+254C only.
- Classification: Agent-recommended.
- Reasoning: This is the specific character seen in the wild. The
statusLocked()guard provides the safety net for future unknown characters. Adding a broad set risks false positives.
-
Update existing tests that assert
stablewheninitialPromptReadyis false.- The tests
"agent not ready - status is stable until agent becomes ready"and"no initial prompt - normal status logic applies"currently assertstablewhen readiness is false. The research notes the second test's assertion was correct from a pure screen-stability perspective, but is inconsistent withSend()behavior:stableSignalgates oninitialPromptReadyfor ALL messages, soSend()would hang despitestablestatus. The status must reflect actual send capability. - The test
"no initial prompt configured - normal status logic applies"is unaffected because it doesn't setReadyForInitialPrompt, so the default (return true) applies andinitialPromptReadybecomestruebefore stability is reached. - Classification: Agent-recommended.
- The tests
-
Message box detection — Extend both message box detection functions to also match
╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌patterns. Add a testdata fixture for the Claude onboarding screen with╌characters. Run readiness tests to verify. -
Status guard — Add a check in the status logic: when
initialPromptReadyis false and the screen is otherwise stable, returnchanginginstead ofstable. This preventsSend()from enqueueing messages that can never be processed. -
Update existing tests — Fix the two tests that assert
stablewhen readiness is false. Update them to expectchanging. Confirm the third "no initial prompt configured" test is unaffected. -
Add reproducing test — Add a test that demonstrates
Send()returns an error instead of hanging wheninitialPromptReadyis false. -
Run full test suite — Verify no regressions.