diff --git a/emrg/server/evolution_prompt.md b/emrg/server/evolution_prompt.md index f0027dde..bdd03341 100644 --- a/emrg/server/evolution_prompt.md +++ b/emrg/server/evolution_prompt.md @@ -364,6 +364,9 @@ When reading rants, follow these rules: > - Saturation halt → low-frequency heartbeat full cycles (#585 宿主 rant 2026-08-09T09:35:55,覆盖 #559 完全停机:保护=降频不是停止,**永不停机**——≥30 空轮后不 skip,按 heartbeat 间隔继续跑完整六步循环;`_heartbeat_interval()` = max(interval, min(interval×8, 8h))(60s 任务→480s=8 分钟 / 10min→80min / 1h→8h / ≥8h 任务不变);run loop wait_timeout 饱和时用 heartbeat,manual trigger / 上游 ls-remote 推进立即恢复正常频率(计数清零);`_saturation_halt_active()` → `_saturation_heartbeat_active()`,日志 "never halting";新 rant 最迟一个心跳周期内处理;持久化语义不变;+3 pytest 636→639(公式 7 例 / 饱和 tick 仍跑完整 cycle / 日志无 "skipping" / 上游恢复 / 无 git 仓库不崩),639 全绿,README/Agent/README.cn 计数同步) ✅ > - Windows integration-test tree-kill (#587 外部贡献 pm25coder,G130 同族于 G129 rant 2026-08-09T08:03:46:Windows 集成测试 `process.kill(-pid)` 进程组杀是 no-op(负 PID 不受支持,抛 ESRCH)→ 被 kill 的 daemon 变孤儿进程残留,G43 stale-port 重连测试本地必挂;修复=新增 `killProcessTree` 帮助函数(win32 分支 `taskkill /PID /T /F` 树杀+强杀,windowsHide:true + stdio:ignore + 错误吞掉;POSIX 分支保持既有 group-kill),killDaemon SIGTERM→3s→SIGKILL 回退链与 stale-port 测试两处调用点全部改走帮助函数;纯测试改动(21+/3-,GUI 96 / pytest 639 计数不变);判别力=负 PID 语义平台差异,win32 分支由本地实跑验证,POSIX 分支 CI 31289054189 SUCCESS 无回归) ✅ > - Stale-check exception narrowing (#589 外部贡献 pm25coder,G129 同族续:`check_and_restart_if_stale()` 裸 `except Exception` 吞掉一切——AuthError(token 不匹配=配置/安装问题,用户必须看到)与编程错误(AttributeError 等真 bug)被静默隐藏,掩盖 #583 家族根因;修复=收窄为仅瞬态清单(ConnectionRefusedError/FileNotFoundError/OSError/JSONDecodeError/TimeoutError/**ConnectionClosed**——websockets 握手后 ping 中途断线场景),AuthError 与编程错误向上传播给 ensure_connected 调用者;+2 测试 639→641(AuthError 传播 / AttributeError 传播,负态 test_server_unreachable_silent 仍吞瞬态),三处文档计数同步;判别力=删 `not error` 即红/删 return 即 "log" not in captured 红) ✅ +> - Windows cmd-window storm + daemon spawn throttle (#592 宿主紧急 rant 2026-08-09T13:16:36(最高优先级,Windows v0.2.15 宿主实测 cmd 弹窗风暴被迫重启 + daemon 启动失败 GUI/scheduler 无法连接):根因①代码库零处 CREATE_NO_WINDOW——每个 subprocess(git/gh/bash 工具/scheduler/daemon spawn)在 Windows 都弹控制台;②GUI 重连循环每 ~5s 重拉 emrgd.cmd 且每次与前一 daemon 启动竞争(G43 stale-port unlink 删掉健康 daemon 的 port 文件 → scheduler 连不上 93 次);③scheduler 无连接失败退避。修复=新增 `emrg/_win.py` `win32_no_window_kwargs()`(win32 返回 {creationflags: CREATE_NO_WINDOW},其余平台 {})splat 进全部 34 处 subprocess 调用点;daemon spawn 加退避/节流;G43 不删活 daemon 的 port 文件(#593 深化);+测试覆盖) ✅ +> - Port-file self-heal + G43 PID guard (#593 外部贡献 pm25coder/#592 根因深化:daemon 侧新增 `_port_keepalive_loop` 每 60s 检查 `emrgd.port` 缺失即用当前 listener 端口重新 `_assert_port_file`(提取自 startup 的原子写逻辑,外部 G43 unlink 自愈),shutdown 干净 cancel;GUI `daemon_client.js` 新增 `_daemonProcessAlive()`(读 `~/.emrg/emrgd.pid` + `process.kill(pid,0)` 探测,EPERM=存活(Windows 权限),ESRCH/ENOENT=死亡),G43 捕获路径先查 pid——daemon 活着【绝不删 port 文件】(瞬时失败→退避重试 + 抛 "daemon unreachable (pid alive)"),只有真死才 unlink+重拉;+57 pytest(_assert_port_file 提取/keepalive 重断言/shutdown cancel)、+49 GUI 测试(_daemonProcessAlive 正反两态)641→650 / 96→101,README/Agent 计数同步) ✅ +> - TUI daemon spawn throttle (#594 #592 客户端侧总闸补完:`daemon_manager.py` 模块级 `_MAX_SPAWN_ATTEMPTS=3` + `_spawn_attempts` 计数——`start_daemon()` 超限抛 `RuntimeError("daemon failed to start after N attempts — please run 'emrg server' manually...")`(TUI app.py `_reconnect` 循环每 1s 调 ensure_connected → 无限 spawn 即 Windows 弹窗风暴源),`ensure_connected` 成功连接后计数归零(连接生命周期语义,与 GUI daemon_client.js auth_ok 归零一致);`app.py _reconnect` 捕获节流错误一次(`_throttle_warned`)→ 一次性系统消息 + status 提示 "daemon down — run 'emrg server'";+56 pytest(3 次 spawn 无第 4 次 await_count 断言 / 成功连接归零 / autouse fixture 防跨测试泄漏)→ 合并后 652 全绿) ✅ #### 2.2 Latest GitHub code changes