Conversation
When the underlying JsonRpc connection is disposed (e.g., CLI process crashed), soft steer's SendAsync throws ObjectDisposedException. Previously this just reported the error and gave up, losing the user's steering message. Now, connection errors (detected via IsConnectionError) in the soft steer path remove the already-added user message from history and fall through to the hard steer path, which calls AbortSessionAsync + SendPromptAsync. The latter has full reconnection logic (dispose old session, restart server if needed, recreate client, resume session, retry send). Non-connection errors (e.g., invalid arguments) still use the original error-and-cleanup behavior. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
PR #293 Review — "fix: soft steer falls through to hard steer on disposed JsonRpc connection"CI Status: Core Logic — ✅ CorrectThe control flow is sound:
Reference equality 🟡 MODERATE — Unguarded
|
Independent Re-Review — PR #293 (5-Model Consensus)CI Status: 🟡 MODERATE → 🔴 CRITICAL (5/5 models) — Duplicate message persisted to
|
…ard test IndexOf
- Defer _chatDb.AddMessageAsync to after SendAsync succeeds so connection
error fallback leaves no orphaned DB entry (hard steer would have written
the same message again via SendPromptAsync)
- Wrap History.RemoveAt in InvokeOnUI() for thread safety: List<ChatMessage>
is iterated by the UI thread during render; mutations must be marshaled
- Add Assert.True guard for second IndexOf('// Hard steer:') in both
SteeringMessageTests source-structure tests to give clear failure messages
instead of ArgumentOutOfRangeException
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…al race InvokeOnUI (fire-and-forget) could fire after SendPromptAsync already added the new steering message to History. The [^1] == userMsg guard would then fail (wrong last item), leaving the original userMsg stranded as a duplicate. Switching to await InvokeOnUIAsync ensures the removal completes on the UI thread before the hard steer path's AbortSessionAsync+SendPromptAsync run. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
PR #293 Round 2 Re-review (commits
|
| Finding | Status |
|---|---|
🟡 Duplicate _chatDb write on fallback |
✅ FIXED (5/5) — AddMessageAsync moved inside try after softSteerSucceeded = true; connection-error path never writes |
🟡 History.RemoveAt on background thread |
✅ FIXED (5/5) — wrapped in await InvokeOnUIAsync(), ensuring removal completes on UI thread before hard steer runs |
🟡 Unguarded IndexOf in test |
✅ FIXED (5/5) — both tests now assert hardSteerIdx >= 0 before Substring |
Additional Fix Applied
During re-review, 4/5 models identified that InvokeOnUI (fire-and-forget) introduced a secondary race: the removal callback could fire after SendPromptAsync added the new steering message, causing History[^1] == userMsg to mismatch and leaving userMsg stranded as a duplicate.
The fix (d622d44) upgrades to await InvokeOnUIAsync() — the awaitable variant already present in the codebase — which guarantees the removal completes before the hard steer path executes. All 20 SteeringMessage tests pass.
No New Issues Found
The fix is correct and complete:
- On success: DB write after
SendAsyncsucceeds — no orphaned entries - On connection error: History removal awaited before hard steer — no duplicates
- On other errors: unchanged cleanup + return path — no regression
- Test anchors guarded — clear failure messages if structure changes
Bug
⚠️ Soft steer failed: Cannot access a disposed object. Object name: 'StreamJsonRpc.JsonRpc'When the underlying JsonRpc connection is disposed (e.g., CLI process crashed), soft steer's
SendAsyncthrowsObjectDisposedException. Previously this just reported the error and gave up — the user's steering message was lost.Fix
Connection errors (detected via
IsConnectionError) in the soft steer path now:AbortSessionAsync+SendPromptAsync)SendPromptAsynchas full reconnection logic: dispose old session → restart server if needed → recreate client → resume session → retry send. Non-connection errors still use the original error-and-cleanup behavior.Tests
SteerSession_SoftSteerPath_ContainsConnectionErrorFallback— source-structure test verifying the fallback path exists with correct patterns (IsConnectionError,STEER-FALLBACK,History.RemoveAt,softSteerSucceeded).Verified