mirror of
https://github.com/l0ng-ai/tty7.git
synced 2026-09-22 00:02:23 +00:00
* fix(daemon): let a clean version probe clear the mismatch record The restart prompt was armed from a global that only ever accumulated: `note_daemon_mismatch` could set it, and nothing could take it away except the window that consumed it. `ensure_running`'s agreeing path never touched the record at all. That matters because `ensure_running` is the first thing every control-link reconnect attempt runs, and a mismatched daemon is one no connect succeeds against. The link backed off and retried, arming the prompt again each time round — including in the seconds the user spent reading the dialog it had already opened. Restarting the daemon then fixed the daemon and not the record, so the next window built took that last arming and asked a second time about a server that was already gone. Make a probe's verdict settle the record rather than only add to it: a daemon found to be ours wipes what an earlier probe left. The probe judgement moves into `judge_probe`, and the handoff's own return judgement into `judge_handoff_return` / `land_handoff_return`, so both are testable apart from the sockets it takes to reach them. Claude-Session: https://claude.ai/code/session_01GAjHNse9BDu5jSCjU5QTKe * fix(daemon): tell a silent control socket apart from an agreeing one `control_dialect_refusal` returned `Option<DialectRefusal>`, and `None` meant both "it answered with our own dialect" and "it never answered at all" — a connect that failed, the handshake timing out, a peer that hung up before `HelloOk`. That conflation was harmless while silence only meant "record nothing". It stopped being harmless when a clean verdict started *clearing* the mismatch record: a control socket that times out now wipes a refusal the control link had already met, and the window built next opens with no tabs and nothing on screen to explain why — the exact state the record exists to prevent. The function's own doc comment still promised the opposite. Give the answer three shapes (`DialectAnswer::{Agrees, Refuses, Silent}`) and carry the distinction through to the verdict (`MismatchVerdict::{Clear, Found, Unchanged}`). Only `Agrees` clears. Silence leaves the record exactly as it stands, which is the cheap side of the asymmetry: a stale record costs one prompt about a daemon that turned out fine and the next probe takes it away, while a wrongly cleared one costs a window its tabs. The landing also moves out of `ensure_running` into `land_probe`, so the logging and the record write are one thing a second caller can reuse. Claude-Session: https://claude.ai/code/session_01GAjHNse9BDu5jSCjU5QTKe * fix(daemon): settle the mismatch record on the restart path too `ensure_running` only settles the record on the branch where a daemon is already listening and answers. `restart()` is `stop()` + `ensure_running()`, so by the time it runs the connect is refused, the endpoint is reaped, a daemon is spawned, and the startup poll loop returns having written `note_local_daemon` and nothing else. The record still describes the daemon the user just killed. That is the path a mismatch is most likely to take. A daemon from before protocol versioning reports no version at all, so `local_daemon_supports` answers false for the handoff feature and the prompt's Restart takes `restart()`, not `hand_off()` — the one branch that was given a clear. Judge the freshly spawned daemon with `judge_probe` and land it with `land_probe`, the same pair the already-running branch uses. The control listener is up before the pane endpoint binds, so the dialect can be asked this early; a daemon that answers neither handshake is still recorded as nothing, same as before. Claude-Session: https://claude.ai/code/session_01GAjHNse9BDu5jSCjU5QTKe * fix(daemon): drop a probe verdict about a daemon that is already gone A probe is not one instant: it connects, asks the pane endpoint and the control socket, and only then writes what it found. In between, this build can stop the daemon, hand it off, or spawn a new one — and now that a verdict *settles* the record rather than only adding to it, a late one is not a stale read but a wrong write. Against a mismatched daemon that is the normal case, not a corner. The control link retries on a backoff and every retry runs `ensure_running`, so when `land_handoff_return` clears the record, a probe that connected to the outgoing image before the exec lands afterwards and re-arms the prompt about the daemon the user just replaced. The clear was best-effort against its own retry loop. Stamp each verdict with a counter that moves whenever this build deliberately changes which process serves — `stop`, `reap_stranded`, `spawn_detached`, and the handoff at the point the exec is asked for — and drop a landing whose stamp is stale. Probes against the same daemon are still last-one-wins, which is what a record of "what is running now" should do. Claude-Session: https://claude.ai/code/session_01GAjHNse9BDu5jSCjU5QTKe