emrg: GUI connect canonical-home fallback + probe-before-give-up + diagnostics (rant 18:47:37) - #597
Conversation
…agnostics (rant 18:47:37)
argszero
left a comment
There was a problem hiding this comment.
✅ LGTM — cycle
Reviewed the full diff (daemon_client.js 181±, daemon.py 7±, +2 tests).
Root-cause correctness: confirmed — daemon.py config_dir() = Path.home()/.emrg writes emrgd.port unconditionally to canonical home; connect.py reads ~/.emrg/emrgd.port unconditionally. When gui.project_dir ≠ home, the GUI's projectDir-only reads hit nonexistent files → false 'daemon not running' → spawn collides with the live daemon's PID lock → false 'failed to start after 3 attempts'. The _readPortToken() projectDir→home fallback (with source log) is the right fix and is applied consistently across port/token, log tail, and pid-file reads.
Probe-before-give-up (_spawnOrProbe → _probeExistingDaemon): correct — spawn failure no longer implies daemon absence; 4-state diagnostic (port_file_exists / port_file_content / daemon_alive(ping) / spawn_result) gives the host actionable context, and reuse of the live daemon directly resolves the reported regression.
Verification (both states):
- Positive: projectDir port missing → home fallback → direct connect, no spawn (test asserts
spawned === false). - Negative: stale projectDir port + spawn throttle → probe finds live home daemon → reuse (test asserts reuse log + 4-state probe line).
- daemon.py startup self-assertion (pid | port_file | port_file_written_ok) is a good host/CI-symmetric diagnostic.
Local verification: npm test → 103 pass (29 daemon_client); doc counts 103 consistent across README.md/README.cn.md/Agent.md (#511 guard); CI test 31309720963 SUCCESS.
Minor non-blocking observation: _daemonProcessAlive() returns false immediately if a projectDir pid file exists but is corrupt (non-integer), skipping the home fallback — benign (worst case one extra spawn attempt that then probes/reuses), not blocking.
LGTM — merging criteria not yet met (1/3).
argszero
left a comment
There was a problem hiding this comment.
✅ LGTM — cycle
Verified the fix end-to-end:
- Root cause confirmed: daemon always writes state to canonical ~/.emrg (daemon.py config_dir()), GUI read projectDir/.emrg — mismatch when gui.project_dir ≠ home
- _readPortToken() projectDir→home fallback covers the host scenario (new test 1: no spawn, direct connect)
- _spawnOrProbe() probe-before-give-up covers the throttle path (new test 2: stale projectDir port + 3-attempt throttle → probe finds live daemon at canonical home → reuse)
- 4-state diagnostic fields (port_file_exists/port_file_content/daemon_alive/spawn_result) asserted in test 2
- Both positive (normal connect) and negative (missing/stale port) states covered
- 652 pytest + 103 npm green locally; CI test workflow pass
|
I tested this PR on a real Windows host (zh-CN, UTC+8) — cycle 20260809-190150. |
argszero
left a comment
There was a problem hiding this comment.
✅ LGTM — cycle (3rd ✅, merging)
Full review performed in an earlier cycle (root cause verified: daemon always writes canonical ~/.emrg; projectDir≠home → GUI false '3 attempts' error; fix = canonical-home fallback + probe-before-give-up + diagnostics). This cycle re-confirmed live: CI test 31309720963 SUCCESS, MERGEABLE/CLEAN, 2 prior ✅ from different cycles (11:02:55Z, 11:03:20Z), plus pm25coder real-Windows-host verification comment. Merge condition met (3 consecutive ✅, no ❌).
Summary
Fixes the v0.2.16 regression where the GUI shows
ensureConnected failed: daemon failed to start after 3 attemptswhile the daemon is actually running (host report, rant 2026-08-09T18:47:37, Windows).Root cause
The GUI reads the daemon port/pid/log files from
projectDir/.emrg/whereprojectDir = cfg.gui.project_dir. The daemon always writes its state to the canonical~/.emrg/(daemon.py config_dir()=Path.home()/".emrg";connect.pyreads~/.emrg/emrgd.portunconditionally). Whengui.project_dir ≠ home, the GUI reads a nonexistent file → assumes no daemon → spawns → PID lock blocks the already-running daemon → 3× spawn timeout → false "failed to start after 3 attempts" error.Changes
emrg/gui/daemon_client.js_readPortToken()— authoritative port/token read:projectDirfirst, canonical~/.emrgfallback (warns + logs which source won)isRunning()/_daemonProcessAlive()/_readLogTail()— all fall back to canonical~/.emrgpaths_probeExistingDaemon()— 4-state diagnostic probe:port_file_exists / port_file_content / daemon_alive(ping) / spawn_result(rant B1/B3)_spawnOrProbe()— on spawn failure (incl. 3-attempt throttle) probe for an existing daemon and reuse it instead of giving up (rant A1)ensureConnected()— structured per-attempt logging + finalresult=connected, daemon_running, portline (rant B5)startDaemon()— logs spawned pid (rant B2)emrg/server/daemon.pypid | port_file | port_file_written_ok(rant B4)Tests (+2 → 103 GUI)
~/.emrgfallback → direct connect, no spawnDocs: README.md / README.cn.md / Agent.md GUI counts 101 → 103 (29 daemon_client).
Acceptance (rant)