The socket-loopback DaemonClient (T-127) raced the UI on first launch:
panels queried before the socket finished connecting and cached a
"daemon not connected" error, and the Claude pane's spawn gate tripped,
leaving an empty terminal. Three fixes in the startup/connection path:
- DaemonClient.request() now waits briefly (5s) for an in-flight
connection instead of failing instantly, gated on _started so a
never-started client still fails fast. start() sets _started
synchronously so the gate is armed before the UI builds.
- swapIpcServer reuses the live server when the opened project matches
the workspace it already serves, instead of tearing it down — the
project-open flow fired right as the Claude pane spawned, dropping
the connection mid-spawn.
- _connect bails if already connected, so start() arming the reconnect
loop and swapIpcServer's reconnectAt can't open a second socket
(which had been double-delivering events).
This whole orchestration had no automated coverage — integration tests
stub a FakeDaemonClient. Adds a real wait-then-connect client test.
Co-Authored-By: Claude <noreply@anthropic.com>
test / unit + widget + golden + a11y (push) Failing after 2m16s
test / integration_test (xvfb) (push) Has been skipped
test / bundle smoke (xvfb 5s) (push) Has been skipped
test / daemon subprocess + web WASM smoke (push) Has been skipped
test / dart doc (lib API) (push) Failing after 29s
Fourth slice of T-99. The UI's DaemonClient now talks to its own
IpcServer through the same per-workspace Unix socket the C `clide`
client uses — one transport, one wire contract, no second path
through the dispatch tree.
Changes:
* lib/kernel/src/ipc/in_process.dart deleted. Nothing imports it.
* DaemonClient.socketPath becomes mutable + new `reconnectAt(path)`
method swaps an active client onto a different socket without
restart. Project switch in main.dart uses it — the dispatcher
+ IpcServer are rebuilt for the new workspace, and the client
reconnects to the new path.
* main.dart's daemonClientFactory now builds a real DaemonClient
pointed at workspaceSocketPath(workRoot); swapIpcServer kicks
off server.start() then client.start() in sequence.
* lib/test_app.dart's pane.spawn smoke test uses dispatcher.dispatch
directly instead of InProcessClient — same coverage, no dead-end
import.
* DaemonClient client_test gets a reconnectAt round-trip test.
T-128 (delete IsolateClient + Backend + backend_entry.dart) unblocked.
Co-Authored-By: Claude <noreply@anthropic.com>
Complete three overdue cleanups discovered during macOS health check:
D-56 daemon dissolution: delete bin/clide.dart, DaemonServer,
and orphaned tests (test/cli/, subprocess_test, in_process_test).
Update stale "clide --daemon" references in i18n catalogs, error
messages, editor_commands, CI scripts, and decision records.
ptyc retirement: delete ptyc/ source tree, PtySession, scm_rights.
Remove from Toolchain resolution, ToolCheck gate, backend
serialization, testmode harness, Makefile, CI, and sandbox
entitlements. PTY spawning uses NativePty (Dart FFI forkpty) since
the terminal was absorbed in-tree. D-5 amended.
Golden tests: wire the existing but never-applied clideGoldenConfig
via flutter_test_config.dart. Disable CI goldens (Skia anti-aliasing
differs between macOS/Linux even with Ahem). Keep platform-keyed
goldens only — goldens/linux/ and goldens/macos/ each run on their
own OS.
Test suite: 826 pass, 0 fail on macOS (was 829 pass, 11 fail).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
DaemonBus (was EventBus): typed events for system/IPC layer.
MessageBus: channel-based pub/sub for UI/extension coordination.
Messages carry publisher (auto-stamped from extension ID),
channel (required), timestamp, and payload. Subscribe by
publisher, channel, or both — zero collision across extensions.
Extension context gains publish() and subscribe() convenience
methods that auto-stamp the extension's ID as publisher.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Single Flutter package at the repo root. All code, tests, assets,
and platform directories moved from app/ to root. Package renamed
from clide_app to clide — all imports rewritten. Merged pubspec
combines core (ffi) and app (flutter, yaml, xterm) dependencies.
Makefile simplified: no APP_PRESENT conditionals, no cd, no daemon
lifecycle. 317 tests pass.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>