Skip to content

fix(cli): make session chat failures acceptance-aware with Effect - #715

Open
zxch3n wants to merge 3 commits into
mainfrom
debug/session-chat-fetch
Open

zxch3n wants to merge 3 commits into
mainfrom
debug/session-chat-fetch

Conversation

@zxch3n

@zxch3n zxch3n commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

Related issue

Internal maintainer change; no separate intake issue.

Problem / pressure

Cloud workspace enumeration runs before MCP chat acceptance, even for same-machine targets. A transport failure could become a non-retryable INTERNAL_ERROR; a failure after durable acceptance could produce the same envelope and encourage duplicate sends.

Summary

  • Use Effect for bounded workspace reads, typed errors, cancellation and scoped manager cleanup.
  • Retry only transient reads: four attempts maximum, 5 seconds per attempt, 10 seconds total. Preserve explicit HTTP denial and business/schema failures.
  • Return the stored Operation after post-accept materialization failure; unreadable receipts require recovery with the original ID.
  • Keep legacy writes joined before cleanup and preserve concurrent MCP requester contexts.
  • Add safe stage/Operation/endpoint/cause-code diagnostics and deterministic real SQLite boundary tests.
  • No offline authorization, schema migration, new dependency, production configuration or desktop runtime changes.

Visual explanation

session_chat
  workspace read: bounded retry + timeout + AbortSignal
  manager scope: acquire → use → release
    identity / permission validation
    SQLite accept: fixed Operation + target Turn
    materialize input: failure leaves accepted work for coordinator
    read receipt: stored result, or OPERATION_RESULT_UNAVAILABLE
  cancellation joins legacy writes before releasing manager

Before / after

Before After
Workspace transport failure becomes generic non-retryable error Bounded read retry and typed unavailable/denied/invalid errors
Accepted materialization failure resembles rejection Stored active Operation remains recoverable without resending
Promise cleanup has no request cancellation ownership Effect scope aborts workspace I/O and joins non-cancelable work before cleanup

Test plan

  • 193 tests passed across workspace, command-runtime, MCP network boundary, machine-access mapping, Operation model/store/coordinator, access retry/policy and OSS zero-cloud suites.
  • Shared IPC suite separately passed 9/9 with temporary socket permissions.
  • CLI typecheck, changed-file type-aware lint, Prettier, docs check, public/platform boundary checks and git diff checks passed; lint/docs retain warnings.
  • Ran pnpm format; excluded its unrelated existing Electron formatting change.
  • Full pnpm check passed typechecking and lint but was interrupted during shared tests after sandbox listen EPERM failures; this is not a full green check.
  • No desktop build or whole-device E2E. Synthetic target SQLite sink proves acceptance/retry behavior at the materializer port, not complete daemon delivery.

Context handoff

Instructions for reviewing agents

  • Review focus: Challenge HTTP/body error classification in workspace.ts and acceptance/cancellation/AsyncLocalStorage boundaries in lody-mcp-server.ts.
  • Decisions to challenge: Four attempts and a 10-second read deadline; joining legacy non-cancelable Promises; returning an accepted Operation instead of reporting send rejection.
  • Plausible failures / evidence gaps: Cancellation may wait for legacy calls to settle; synthetic materializer coverage is not real-device delivery; the historical network root cause remains unknown.

Authoring context

  • User goal / directives: Investigate desktop MCP fetch failures and implement the approved Effect refactor in an isolated checkout.
  • Constraints / non-goals: Preserve existing authorization, same-ID/source-Turn matching, materialization claims and coordinator recovery; no offline bypass or production changes.
  • Risk-bearing decisions: Retry only read failures; preserve durable acceptance; use original Operation IDs for uncertain outcomes; do not treat network unavailability as permission.
  • Destructive or irreversible behavior: No data migration or deletion; only synthetic test fixtures are cleaned up. Production durable records retain their existing format.
  • Deliberately not done or tested: No batch/create orchestration rewrite, new local routing, real network disruption, desktop rebuild or deployed-client acceptance.
  • Unknowns / confidence: Deterministic failure injection establishes flow semantics, not the original DNS/proxy/TLS cause. Draft Spec and bilingual implementation note document limits.

Bound workspace access reads, preserve typed failures and cancellation, and recover durable Operations without resending inputs.

Model: gpt-5
Inspect the completed Exit after joining writes and cleanup, then recover only the original receipt outside the interrupted fiber. Assert first responses at the accept and materialization cancellation boundaries.

Model: gpt-5

zxch3n commented Sep 15, 2026

Copy link
Copy Markdown
Contributor Author

Addressed the cancellation review in 9aee69a:

  • Reproduced the bug by asserting the first response: the previous materialization-cancellation test returned INTERNAL_ERROR with retryable=false despite a durable input.
  • The chat entrypoint now inspects runPromiseExit after the canceled fiber has joined writes and released its manager. If acceptance is possible, a fresh read-only Effect returns the original receipt or OPERATION_RESULT_UNAVAILABLE with retryable=true. It never retries the command or materializer. A catchAllCause inside the interrupted fiber alone did not recover the final Exit in an installed-version runtime check.
  • Documented that Effect.either does not turn interruption Causes into Left values.
  • Tests now assert the first response both immediately after SQLite accept (before materialization) and during gated materialization, including unreadable receipts, cleanup and no duplicate append.
  • Documented the 560 exception: Convex src/browser/http_client.ts defines it as STATUS_CODE_UDF_FAILED, not a generic transient gateway failure.

Validation: 196 targeted tests passed; CLI typecheck, changed-file lint, format and docs checks passed. Full pnpm check again passed typechecking/lint but was stopped after sandbox IPC listen EPERM failures; full-repository tests are not claimed green. These tests establish handler outcomes, not response delivery over a disconnected transport.

@zxch3n
zxch3n marked this pull request as ready for review September 15, 2026 04:47

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant