iter-123: daemon survives tabless-Firefox autostart + per-port registry - #162
Conversation
iter-123 fixes two daemon lifecycle defects found in dogfooding session 61
(ff-rdp v0.3.0 / Firefox 152):
Theme A — autostart no longer dies when Firefox has no page tab yet. The
`tabs.first().context("no tabs available")?` bail in run_daemon is gone; tab
resolution moved into establish_watcher (returns Ok(None) on zero tabs) +
establish_watcher_with_retry. On a persistently tabless start the registry is
still written and the daemon reaches running:true, with a supervised
watcher-establisher thread resolving the watcher lazily once a tab appears
(dedicated 2nd connection, handed to the dispatcher via a rendezvous channel;
watcher_actor became Mutex<String>). The daemon_autostart_failed warning is now
also rendered in --format text (render_warnings on stderr), not only via
--jq '.warnings'.
Theme B — the registry is keyed per firefox_port (daemon.<port>.json +
daemon.<port>.spawn.lock) so concurrent instances on different ports no longer
clobber each other. All registry call sites thread the port; the stale legacy
single-slot daemon.json is retired on the next write.
Tests: 3 live tests (all pass against real Firefox), 5 registry + 2 watcher +
2 output-pipeline unit tests; serialized the process-global warning-recorder
tests to fix a pre-existing flake. Updated e2e/live tests that hard-coded
daemon.json.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Reviewer focus request (orchestrator): live-test evidence is present — thanks. Please spend the review budget on the concurrency of the lazy watcher path: (1) the establisher thread's rendezvous hand-off vs daemon shutdown — no deadlock/leak if the daemon stops before the watcher resolves; (2) |
stop_prior_instance(cli, port) resolves an explicit target port (from --debug-port), but run_daemon_stop and daemon_rpc still hardcoded cli.port internally after the registry became per-port keyed. When --port and --debug-port diverge (launch --replace --debug-port N with --port != N), the graceful-stop RPC/registry path silently acted on whatever daemon was registered under --port instead of the addressed --debug-port daemon. Thread an explicit port parameter through daemon_rpc and run_daemon_stop instead of implicitly reading cli.port, so stop_prior_instance always acts on the daemon it actually resolved. Adds a live regression test that reproduces the pre-fix misrouting (the port-still-listening error named the wrong port) and pins the post-fix behavior. Found during self-review of PR #162 (iter-123).
PR Review Summary — PR #162
FixThreaded an explicit Verification
No other issues found in local review (Copilot review skipped — local-only mode). |
Corrects the Theme B task note that claimed all registry::* call sites pass cli.port (the exact assumption that caused the stop_prior_instance port-scoping bug), and adds AC 5/5 for the new regression test.
Summary
Fixes two daemon-lifecycle defects found in dogfooding session 61 (ff-rdp v0.3.0 / Firefox 152), both in the client/daemon lifecycle code (no RDP spec-method changes).
Theme A — autostart no longer dies on a tabless Firefox. A freshly-launched headless Firefox can come up with zero page tabs (
listTabs→total: 0) until the first navigation lazily creates one. The oldrun_daemonbailed attabs.first().context("no tabs available")?before writing the registry, so the daemon never ran in this environment and every command silently fell back to a per-command direct connection with adaemon_autostart_failedwarning.establish_watcher(returnsOk(None)on zero tabs) +establish_watcher_with_retry(short bounded retry for the momentary-tab case).running:true, and a supervisedwatcher-establisherthread (background_establish_watcher_loop) resolves the watcher lazily once a tab appears — via a dedicated second RDP connection (the split main connection can't serve synchronous replies), handed to the dispatcher over a rendezvous channel.watcher_actorbecameMutex<String>(empty until established).daemon_autostart_failedwarning is now rendered in--format texttoo (render_warnings→ stderr), not only via--jq '.warnings'.Theme B — registry keyed per Firefox port.
DaemonInfois now written todaemon.<port>.json(and the spawn lock todaemon.<port>.spawn.lock), so concurrent instances on different ports no longer clobber each other's record. Allregistry::*call sites thread the port (cli.port/firefox_port/expected_port);find_running_daemon/wait_for_registryneeded no logic change (alreadyfirefox_port-validated). A stale legacy single-slotdaemon.jsonis retired on the next write.Plan:
kb/iterations/iteration-123-daemon-autostart-and-per-port-registry.md(ACs 4/4).Test plan
cargo fmt/cargo clippy --workspace --all-targets -- -D warnings/cargo test --workspace -q— all clean.cargo run -p xtask -- check-iteration-ready— 10/10 gates pass; pluscheck-daemon-locksandcheck-oneway-conformance.FF_RDP_LIVE_TESTS=1 cargo test-live --test live live_123): all 3 pass —live_daemon_autostart_tabless— daemon reachesrunning:truedespite zero tabs at start, nodaemon_autostart_failedwarning.live_daemon_two_ports_no_clobber— two daemons on distinct ports each keep their ownrunning:truerecord (bothdaemon.<port>.jsonfiles coexist).live_daemon_warning_text_parity— JSON↔text warning parity.per_port_writes_do_not_clobber,remove_only_affects_the_named_port,spawn_lock_is_per_port_and_does_not_cross_block,write_removes_stale_legacy_registry,establish_watcher_returns_none_on_zero_tabs,establish_watcher_with_retry_gives_up_when_no_tab_appears,render_warnings_handles_array_and_none,render_warnings_emits_line_for_each_entry.daemon_status::test_lock) to fix a pre-existing concurrency flake; updated e2e/live tests that hard-codeddaemon.json.🤖 Generated with Claude Code## Claims vs code
<generated 2026-07-19T12:45:19Z by ralph-loop>
two→ ✅ matched in diff