Skip to content

fix(cli): wait for TUI worker readiness before startup RPC - #14849

Closed
lambertjosh wants to merge 1 commit into
mainfrom
fix/tui-worker-readiness
Closed

lambertjosh wants to merge 1 commit into
mainfrom
fix/tui-worker-readiness

Conversation

@lambertjosh

Copy link
Copy Markdown
Contributor

Prevent the terminal interface from hanging on a blank screen during startup, which blocked release run 37336767041 and the subsequent release attempt.

Bun 1.4.2 drops messages sent while a worker is still initializing, whereas 1.3.14 buffered them. Kilo awaits logging initialization before installing the worker RPC handler; release logging performs asynchronous filesystem work during that window. Early bootstrap requests can therefore disappear and leave the TUI waiting indefinitely. The native Worker open event also fires too early to serve as a readiness guarantee.

Have the worker explicitly announce readiness after installing its RPC handler, and make the parent wait before sending startup requests. Bound that wait to 30 seconds and use the existing shutdown path on failure. This retains Bun 1.4.2 and the existing release smoke gate rather than rolling back the subprocess fix or weakening validation.

@lambertjosh lambertjosh closed this Oct 6, 2026
kirillk added a commit that referenced this pull request Oct 6, 2026
…timeouts

Adopts three ideas from #14849, which diagnosed the same startup race
independently, and leaves util/rpc.ts byte-identical to upstream.

Real-worker coverage. The in-process channel tests cannot reproduce the actual
failure, because the bug is Bun discarding messages rather than anything in our
own code. A fixture now holds a real Worker in the window that breaks, and
measurement decided its shape: a message is lost only while the worker's event
loop is alive with no `onmessage` installed, which is what a top-level await
during module evaluation produces. A thread parked in `Atomics.wait` queues the
message instead, so a blocking gate reproduces nothing -- the fixture polls a
shared gate to keep the loop running, and the gate keeps it deterministic.
Confirmed by deleting the outbox gating: the startup test then fails with
"request lost during worker startup", and passes once restored.

User-facing timeout. `client.ready` resolves false when the bound elapses, so
tui.ts reports "TUI worker did not start within 30s" and leaves through the
normal shutdown path instead of sitting on a blank screen. Transport-level
gating stays as the guard for any call issued before that check.

No upstream footprint. worker.ts and tui.ts now call KiloRpc directly, so
util/rpc.ts is restored to upstream exactly and can never conflict on a fork
merge. `Rpc.emit` still comes from upstream; the now-dead `Rpc` import in
tui.ts is removed.

Also tolerate non-envelope traffic on the channel. The fixture posts a
SharedArrayBuffer and callers may postMessage anything, and a JSON.parse throw
inside onmessage would take down the handler.

Restores the temporary push trigger so the compiled smoke runs on this branch;
it must come out before merge.
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.

1 participant