emrg: sync test counts to 484 + guard test against doc drift (recurs #426/#430/#510) - #511
Conversation
argszero
left a comment
There was a problem hiding this comment.
✅ LGTM — cycle 20260806-232437 (emrg-00c41753, author self-check, 1/3)
Verified on master 4058022: the #510 sync was correct as of its base (480), but #509 (+2 evolution_summary e2e tests) merged on top, so uv run pytest tests/ -q now reports 482. This PR syncs README.md + Agent.md to 482. Doc-only; GUI count (86) unchanged and still correct.
argszero
left a comment
There was a problem hiding this comment.
✅ LGTM — cycle 20260806-233406 (emrg-00c41753, 2/3)
Independently verified in an isolated worktree at head 37543f7:
- Count consistency: master collects 482; #511 adds exactly 2 guard tests → 484 documented = 484 collected. Self-consistent (no off-by-2).
- Guard validated in BOTH states (per #455 lesson): positive (484=484) passes; negative (temporarily set README to 999) fails with a precise message 'documents 999 Python tests but 484 are collected'. GUI breakdown guard also passes (86 = 22+22+17+15+7+3).
- Full suite on head: 484 passed (482 + 2 new guard tests). Docs-only apart from the new test file; CI test workflow green.
- This guard directly kills the recurring #426→#430→#510→#511 drift pattern. Excellent — the count can no longer silently rot.
argszero
left a comment
There was a problem hiding this comment.
✅ LGTM — cycle 20260806-233252 (emrg-00c41753, 2/3 — final state)
Final PR state verified (supersedes the 1/3 note about the pre-guard diff):
- README.md + Agent.md Python count 480→484; GUI breakdown (86) untouched
- tests/test_doc_counts.py guard: (1) test_python_count_matches_docs runs
pytest --collect-onlyand asserts README (currently N items) + Agent.md ((N)) match the real collection; (2) test_gui_breakdown_sums_to_headline asserts each documented npm-test breakdown sums to its headline (parses per-part leading numbers — avoids the 'i18n' digit trap, e.g. 15 i18n must not contribute '18') - Consistency check: 484 = 482 pre-existing + 2 guard tests; the guard caught the 482 vs 484 mismatch immediately when added, proving the detector works
- First CI run failed at 'Set up job' (runner infra, no code involved); re-run 31116216570 green
This structurally closes the #426→#430→#510→#511 doc-drift recurrence: any future test add/remove without doc sync now fails CI. Doc + test-only change.
…egression hotfix) (#592) * emrg: fix Windows cmd-window storm + daemon spawn throttle (v0.2.15 regression hotfix) Rant 2026-08-09T13:16:36 (emergency, highest priority): Windows v0.2.15 host observed continuous cmd popups (had to reboot) + daemon startup failure leaving GUI/scheduler unable to connect. Root causes: 1. Zero CREATE_NO_WINDOW anywhere in the codebase — every subprocess spawn (git/gh/bash tool/scheduler/daemon spawn) popped a console window on Windows. 2. GUI reconnect loop respawned emrgd.cmd every ~5s forever; each spawn raced the previous daemon's startup (G43 stale-port unlink deleted a healthy daemon's port file → its scheduler logged 'cannot connect' 93 times while the GUI kept spawning). 3. Scheduler had no connect-failure backoff. Fixes: - NEW emrg/_win.py: win32_no_window_kwargs() = {creationflags: CREATE_NO_WINDOW} on Windows, {} elsewhere. Splatted into all 34 subprocess call sites (bash_tool, scheduler x12, daemon x5, git_utils, daemon_manager, installer, __main__ x3, client/app x9). - GUI daemon_client.js: spawn throttle (max 3 attempts per connect lifecycle, then throw with emrgd.log tail instead of respawning); reset counter on successful auth. Both spawn-timeout errors now surface the real emrgd.log tail (readLogTail). - GUI main.js: reconnect exponential backoff 1s→2s→4s→…cap 60s; daemon_stopped status surfaces the real failure to the renderer (zh/en i18n) instead of infinite 5s respawns. - scheduler.py: connect-failure exponential backoff max(30s, interval*2^n) capped at 10 min — no more per-tick retry storm while the daemon is down. Tests: +6 Python (win32_no_window_kwargs POSIX/Windows/splat; backoff zero/exp/cap/floor; 641→647), +2 GUI (spawn throttle + counter reset; 96→98). Docs synced (#511 guard). macOS/Linux unaffected (win32 branch is a no-op empty dict). * emrg: GUI daemon_stopped warning one-time per connect lifecycle (rant 2026-08-09T13:16:36) After the spawn throttle exhausts, scheduleReconnect keeps retrying with backoff capped at 60s — each retry hit the throttle and re-sent daemon_stopped status, so the renderer appended a duplicate 'run emrg server' system message every minute forever. Add daemonStoppedNotified: the warning is sent once per connect lifecycle (reset on successful connect), symmetric with the TUI _throttle_warned guard (PR #594).
…ate (Windows v0.2.15 root cause) (#593) Rant 2026-08-09T13:16:36 root-cause follow-up: PR #592 contains the window-storm (CREATE_NO_WINDOW + spawn throttle + backoff), but the underlying deletion race remains: G43 stale-port logic deletes a healthy daemon's emrgd.port after ONE transient ws failure → the daemon's own scheduler loses the file (93x 'cannot connect' in the host's emrgd.log) while the PID lock makes every respawn exit instantly (zombie state — daemon alive, forever unreachable). Two complementary fixes: 1. daemon-side self-heal: new _assert_port_file() + _port_keepalive_loop re-asserts emrgd.port every 60s if it was deleted/overwritten — the daemon is the living core and re-asserts its own identity. 2. GUI-side guard: _daemonProcessAlive() checks emrgd.pid before G43 deletes the port file — process alive → keep the file and let the reconnect backoff retry (transient failure); only truly-dead daemons get the delete+respawn path. Tests: +3 Python (test_daemon.py _assert_port_file write/rewrite + keepalive restore), +3 GUI (daemon_client.test.js G43 guard alive/dead/ no-pid). 641→644 py, 96→99 gui, docs synced (#511).
* emrg: TUI daemon spawn throttle — complete the anti-storm fix (rant 2026-08-09T13:16:36) PRs #592 (CREATE_NO_WINDOW + GUI spawn throttle + scheduler backoff) and #593 (port-file self-heal + G43 PID guard) contain the Windows v0.2.15 storm fix, but the TUI client had the same storm pattern the GUI had: app.py _reconnect() loops every 1s calling ensure_connected() → with a down daemon each iteration calls start_daemon() → spawns a NEW daemon process every second. On Windows each spawn was a cmd-window source (pre-#592); even windowless it is process churn + PID-lock exit races post-#592. This completes rant acceptance item ① ("启动 GUI/TUI 后零 cmd 窗口弹出"): - daemon_manager.start_daemon: spawn throttle — max 3 attempts per connect lifecycle, then raise with a clear 'run emrg server manually' message instead of spawning forever; counter resets on successful connect in ensure_connected (mirrors GUI daemon_client.js MAX_SPAWN_ATTEMPTS). - app.py _reconnect: on throttle, surface a one-time system message + status hint so the host knows to start the daemon manually (recovery path intact: host starts daemon → is_running True → connect succeeds → counter resets). Tests: +2 (start_daemon throttles after 3 attempts, no 4th spawn; counter resets on success). 641→643 py, doc counts synced (#511). * emrg: sync doc test counts to 652 (post-#593 merge + #594 tests) --------- Co-authored-by: EMRG Evolution <emrg@argszero.dev>
Summary
The #510 doc sync fixed counts as of master before #509 merged. After #509 (evolution_summary e2e tests, +2 tests) landed, the Python suite is 484 — docs had drifted again (the recurring #426/#430/#510 pattern).
This PR not only re-syncs the numbers but adds a guard test to structurally stop the recurrence:
Changes
tests/test_doc_counts.py(new):test_python_count_matches_docs— runspytest --collect-onlyand asserts README.md (currently N items) and Agent.md ((N)) match the real collection counttest_gui_breakdown_sums_to_headline— asserts every documented GUInpm testbreakdown (README/Agent.md) sums to its headline numbercurrently 482 items→currently 484 items(482)→(484)Verification
uv run pytest tests/ -q→ 484 passed (includes the 2 new guard tests)