Skip to content

fix: keep host dialog queue polls and workspace state quiet when a session is not loaded - #3734

Merged
bobleer merged 2 commits into
GCWing:mainfrom
Raing5Days:fix/issue-3729-dialog-queue-not-loaded
Nov 5, 2026
Merged

bobleer merged 2 commits into
GCWing:mainfrom
Raing5Days:fix/issue-3729-dialog-queue-not-loaded

Conversation

@Raing5Days

Copy link
Copy Markdown
Contributor

Fixes #3729.

Problem

When switching sessions/workspaces — especially across a local <-> remote workspace switch — passive host dialog queue observers (observeHostQueue / useHostQueueTimeline) keep polling manage_dialog_queue for a session that has not been restored on the execution host. The backend (DialogScheduler::execute_queue_request) rejects every read with NotFound: "Session is not loaded on the execution host", and the transport adapter logs each poll as ERROR (~every 10s; the issue trace shows 7 failures in 76 seconds).

Two secondary symptoms in the same trace:

  • Every workspace switch emits workspace:loading immediately followed by workspace:error (with a null error) in the same tick, so listeners observe an instant-failure pair even for clean switches.
  • Polls of get_external_source_* against a host without the external compatibility service log ERROR on every poll even though callers degrade by design.

Root cause

  1. execute_queue_request bails out with PortErrorKind::NotFound before looking at the action whenever session_manager.get_session(session) is None. But the scheduler keeps its own queue receipts independent of session restoration, so read-only requests could be answered correctly.
  2. WorkspaceManager.setLoading / setError emit unconditionally; setLoading(true) followed by setError(null) emits workspace:error with an unchanged null error.
  3. The transport adapter's expected-error classifier only knew about get_config.

Changes

  • DialogScheduler::execute_queue_request (host_message_queue.rs): read-only actions (List, Get) now answer from the scheduler-owned receipts even when the session is not loaded on this execution host; mutations (Submit, Cancel, Promote) still refuse user work for an unloaded session so nothing can silently disappear.
  • WorkspaceManager.setLoading / setError: state-quiet — skip the update and event when the value did not change.
  • TauriTransportAdapter.isExpectedTauriRequestError: classify a structured host_unavailable answer (ExternalSourceOperationError contract, direct object or serialized inside message/error/details) from get_external_source_* polls as expected (debug-level). ApiClient reuses the same classifier for the command-level log path.

Testing

  • New regression test host_queue_reads_degrade_for_a_session_not_loaded_on_the_host submits a message, evicts the loaded session, and asserts List/Get still return the queued receipt with a stable epoch while Submit/Promote fail with NotFound. Full host_queue_ filter: 20 passed.
  • New workspaceManager tests: a clean scanWorkspaceInfo emits workspace:loading but no workspace:error; a real failure still reports workspace:error. 35 passed.
  • New tauri-adapter tests: structured host_unavailable on get_external_source_* is expected; plain prose failures and mutation commands are not. 10 passed. HostPendingQueuePanel tests stay green (4 passed; 49 total across the three files).
  • pnpm run type-check (web-ui) passes; pnpm run fmt:rs applied.

AI-assisted.

…ssion is not loaded

Fixes GCWing#3729.

Switching sessions/workspaces (especially local <-> remote) left passive
observers polling manage_dialog_queue for a session that was never
restored on the execution host, producing an ERROR log roughly every
10s ("Session is not loaded on the execution host"). Three changes:

- DialogScheduler::execute_queue_request now lets read-only actions
  (List, Get) answer from the scheduler-owned receipts when the session
  is not loaded on this host, instead of failing every poll. Mutations
  (Submit, Cancel, Promote) still refuse user work for an unloaded
  session so nothing can silently disappear.
- WorkspaceManager.setLoading/setError no longer emit state-quiet
  events; repeating an unchanged value made listeners observe
  workspace:loading followed by workspace:error in the same tick, which
  looked like a load that failed instantly.
- The transport adapter classifies a structured host_unavailable answer
  from get_external_source_* polls as expected (debug-level) instead of
  logging each poll as ERROR; ApiClient reuses the same classifier.

Verification: cargo test -p openbitfun-core (host_queue_ filters, 20
passed incl. new degradation test); web-ui vitest workspaceManager +
tauri-adapter + HostPendingQueuePanel (49 passed); web-ui type-check;
pnpm run fmt:rs.

AI-assisted.
The ApiClient command-level log path now consumes
isExpectedTauriRequestError, but ApiClient.test.ts replaced the
'../adapters' module with a stub that did not export it, so every
rejected invoke in that suite crashed with a TypeError
("preserves structured transport error facts across command wrapping",
"keeps message-only transport errors parseable by domain adapters").

Keep the real classifier via vi.importActual while the transport
adapter itself stays mocked. CI confirmed this was the only real
failure: the macos-15 Rust/CLI jobs were cancelled mid-run (no logs,
BlobNotFound) and the Rust / CLI Validation aggregator only failed
because of that abandonment.

Verification: web-ui vitest ApiClient + tauri-adapter +
workspaceManager suites (63 passed).

AI-assisted.
@bobleer
bobleer merged commit 267ec56 into GCWing:main Nov 5, 2026
13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] 切换会话/工作区时 manage_dialog_queue 返回 "Session is not loaded on the execution host"

2 participants