From b7c1cde3b036b33f028771710e34b004258eb701 Mon Sep 17 00:00:00 2001 From: EMRG Evolution Date: Tue, 11 Aug 2026 19:01:54 +0800 Subject: [PATCH 1/2] =?UTF-8?q?emrg:=20stop-emrg.cmd=20=E2=80=94=20kill=20?= =?UTF-8?q?only=20EMRG-owned=20git=20trees,=20never=20host=20Git=20Bash=20?= =?UTF-8?q?sh/vim=20(rant=202026-08-11T18:56:58)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bin/stop-emrg.cmd | 36 +++++++++++++++++++-------------- emrg/server/evolution_prompt.md | 2 +- tests/test_installer_stop.py | 24 ++++++++++++++++------ 3 files changed, 40 insertions(+), 22 deletions(-) diff --git a/bin/stop-emrg.cmd b/bin/stop-emrg.cmd index 69c2107..03919ba 100644 --- a/bin/stop-emrg.cmd +++ b/bin/stop-emrg.cmd @@ -13,12 +13,12 @@ REM fallback), excludes the daemon (pythonw.exe -m emrg.server) REM 3. daemon: `emrg server stop` protocol shutdown via the OLD install's CLI REM (present since #364 -- version-safe), emrgd.pid poll (<=10s), then REM taskkill /F /PID fallback -REM 4. bundled git (rant 2026-08-11T17:56:25): kill orphaned git/ssh/bash -REM processes whose executable lives under %INSTALL%\git\ (the portable Git -REM the daemon spawns for evolution-cycle git ops). When the daemon is killed -REM mid-operation these become orphans holding install\git\usr\bin\msys-2.0.dll -REM -> Inno "DeleteFile failed; code 5" on upgrade. Filter is by executable -REM PATH so system Git (C:\Program Files\Git) is never touched. +REM 4. bundled git: kill ONLY EMRG-owned git/ssh/bash subprocess trees under +REM %INSTALL%\git\ (portable Git the daemon spawns for evolution-cycle ops). +REM Rant 2026-08-11T17:56:25: orphans hold msys-2.0.dll -> Inno code 5. +REM Rant 2026-08-11T18:56:58: blanket path kill also hit the HOST's Git Bash +REM sh/vim -> only EMRG trees (ancestor = daemon/TUI) are killed; host tools +REM are never touched (same class as R125). REM REM Returns 0 when nothing EMRG-related survives; 1 if a process could not be REM stopped (installer aborts with a clear message instead of hanging). @@ -67,12 +67,17 @@ set "DPID=" for /f "usebackq delims=" %%p in ("%EMRG_DIR%\emrgd.pid") do set "DPID=%%p" if defined DPID taskkill /F /PID %DPID% >nul 2>&1 -REM --- 4. bundled git: orphaned git/ssh/bash under %INSTALL%\git (msys-2.0.dll lock) --- -REM (host 2026-08-11T17:56:25: daemon killed mid-git-op leaves orphans holding -REM install\git\usr\bin\msys-2.0.dll -> Inno DeleteFile code 5. Filter by -REM ExecutablePath prefix so system Git is never touched. `\"` = PowerShell -REM quote escape precedent: TUI branch line 44.) -powershell -NoProfile -Command "Get-CimInstance Win32_Process | Where-Object { $_.ExecutablePath -like \"$env:USERPROFILE\.emrg\install\git\*\" } | ForEach-Object { Stop-Process -Id $_.ProcessId -Force }" >nul 2>&1 +REM --- 4. bundled git: kill ONLY EMRG-owned git/ssh/bash subprocess trees --- +REM (rant 2026-08-11T17:56:25: daemon killed mid-git-op leaves orphans holding +REM install\git\usr\bin\msys-2.0.dll -> Inno DeleteFile code 5.) +REM (host 2026-08-11T18:56:58: the old blanket ExecutablePath-prefix kill also +REM caught the HOST's own Git Bash sh/vim started from install\git\ -- killing +REM vim with unsaved edits and aborting the installer. Same class as R125: +REM non-EMRG processes must not be touched.) A process is EMRG-owned iff its +REM ancestor chain (<=5 levels, ParentProcessId walk) contains the daemon +REM (pythonw.exe -m emrg.server) or the TUI (python.exe -m emrg). Host Git Bash +REM sh/vim have explorer/terminal ancestors -> never matched, never killed. +powershell -NoProfile -Command "$all = Get-CimInstance Win32_Process; $git = $all | Where-Object { $_.ExecutablePath -like \"$env:USERPROFILE\.emrg\install\git\*\" }; foreach ($p in $git) { $cur = $p; $emrg = $false; for ($i = 0; $i -le 5 -and $cur; $i++) { if ($cur.CommandLine -match '-m emrg') { $emrg = $true; break }; $cur = $all | Where-Object { $_.ProcessId -eq $cur.ParentProcessId } }; if ($emrg) { Stop-Process -Id $p.ProcessId -Force -ErrorAction SilentlyContinue } }" >nul 2>&1 :verify set "EXIT_CODE=0" @@ -86,8 +91,9 @@ if exist "%EMRG_DIR%\emrgd.pid" ( tasklist /FI "PID eq !DPID!" 2>nul | findstr /i "!DPID!" >nul && set "EXIT_CODE=1" ) ) -REM bundled-git survival check: any process still under install\git\ -> exit 1 -REM (installer aborts with a clear message instead of a repeat "Try again" loop) -powershell -NoProfile -Command "if (Get-CimInstance Win32_Process | Where-Object { $_.ExecutablePath -like \"$env:USERPROFILE\.emrg\install\git\*\" }) { exit 1 }" >nul 2>&1 +REM EMRG-owned bundled-git survival check: only an EMRG git subprocess tree that +REM could not be killed -> exit 1 (installer aborts with a restart hint). Host Git +REM Bash sh/vim still alive is NOT a failure (rant 2026-08-11T18:56:58). +powershell -NoProfile -Command "$all = Get-CimInstance Win32_Process; $git = $all | Where-Object { $_.ExecutablePath -like \"$env:USERPROFILE\.emrg\install\git\*\" }; $emrg = $false; foreach ($p in $git) { $cur = $p; for ($i = 0; $i -le 5 -and $cur; $i++) { if ($cur.CommandLine -match '-m emrg') { $emrg = $true; break }; $cur = $all | Where-Object { $_.ProcessId -eq $cur.ParentProcessId } }; if ($emrg) { break } }; if ($emrg) { exit 1 }" >nul 2>&1 if errorlevel 1 set "EXIT_CODE=1" endlocal & exit /b %EXIT_CODE% diff --git a/emrg/server/evolution_prompt.md b/emrg/server/evolution_prompt.md index 74309a6..e14cec3 100644 --- a/emrg/server/evolution_prompt.md +++ b/emrg/server/evolution_prompt.md @@ -418,7 +418,7 @@ When reading rants, follow these rules: > - README fact fixes + trust copy (#677 宿主 rant 2026-08-11T17:28:02:竞品表 Codex 开源列 ❌→✅ *Apache-2.0*(此前误标 ❌——Codex 是开源但无自进化,差异化注:开源本身不是差异点,闭环进化才是);FAQ 新增安全边界条目 EN/CN(自进化只改 `~/.emrg/evolution/emrg`,绝不碰宿主项目文件;宿主项目上的工具调用仅宿主指令触发;每处改动都过 pytest + PR 评审);Quick Start 新增 BYOK 句 EN/CN(自带 API Key、额度/账单归宿主、软件 MIT 免费);README.md/README.cn.md 同步,MANIFESTO.md 零改动;合并 a499732) ✅ > - README contribution philosophy (#678 宿主 rant 2026-08-11T17:30:31:Rant-Driven Evolution 段(Real example 之后)EN/CN 对称新增"如何贡献?使用它。"——连接 GitHub + 吐槽 → 吐槽变成真实 PR(演化周期负责编码/测试/上线),无需 fork/clone/写代码,"使用 EMRG 就是在为 EMRG 做贡献";完成三段式叙事(产品经理 / 硅基生命宿主 / 使用即贡献);合并 35ca1c3) ✅ > - GUI packaged icon fix (#680 宿主 rant 2026-08-11T17:37:03:打包版曾用 Electron 默认蓝色原子球图标——package.json build.mac/win/linux.icon 从目录形 `../packaging/assets/` 改显式单文件(mac .icns / win .ico / linux .png,全部存在于 packaging/assets);运行时 `BrowserWindow icon` 经 `windowIconPath()`(打包版 extraResources 落 `resources/icon.png`,源码回退 `packaging/assets/icon.png`);fs/path 已导入零新增依赖;node --check 通过;合并 b400267) ✅ -> - Windows installer kill bundled-git orphans (#683 宿主 rant 2026-08-11T17:56:25:Windows 升级安装卡 Inno "DeleteFile failed; code 5"——daemon 演化周期 git 操作中途被杀 → 孤儿 git/ssh/bash 进程锁 `install\git\usr\bin\msys-2.0.dll`,CloseApplications 看不见(非 EMRG 名);修复=stop-emrg.cmd step 4 按**可执行文件路径前缀**杀 bundled git 孤儿(`Get-CimInstance Win32_Process` + `ExecutablePath -like "$env:USERPROFILE\.emrg\install\git\*"`——只命中打包便携 Git,绝不误杀系统 Git C:\Program Files\Git),置于 daemon 停止后、:verify 前;:verify 存活判定 → exit 1 → 安装器中止给可操作消息(提示重启电脑);`\"` 引号转义沿用 TUI 分支先例;Get-CimInstance 兼容 Win11 24H2+(无 wmic 依赖);测试锚定判别信号;合并 c9131c6) ✅ +> - Windows installer kill bundled-git orphans (#683 宿主 rant 2026-08-11T17:56:25,**18:56:58 修正方向**:Windows 升级安装卡 Inno "DeleteFile failed; code 5"——daemon 演化周期 git 操作中途被杀 → 孤儿 git/ssh/bash 进程锁 `install\git\usr\bin\msys-2.0.dll`;初版修复=stop-emrg.cmd step 4 按**可执行文件路径前缀**杀 bundled git 孤儿(`ExecutablePath -like "$env:USERPROFILE\.emrg\install\git\*"`),:verify 无差别存活判定 → exit 1 中止;**宿主实测 18:56:58 发现方向错误**:路径前缀会误杀宿主自己的 Git Bash sh/vim(也从 install\git 启动),vim 未存盘内容丢失 + verify 抓存活 → 安装中止(R125 同族:Inno 不该干涉宿主工具);**修正**=step 4 只杀 **EMRG 自己的 git 子进程树**(`Get-CimInstance` + `ParentProcessId` 祖先回溯 ≤5 层,祖先含 daemon `pythonw -m emrg.server` / TUI `python -m emrg` 才杀;宿主 sh/vim 祖先链是 explorer/终端 → 不杀),:verify 存活判定同步改为"仅 EMRG 子树残留 → exit 1",宿主工具存活不再是失败;测试锚定判别信号(ParentProcessId + -m emrg + verify 无旧无差别检查);合并 c9131c6(#683)→ 修正 PR 待合并) ✅ > - README emoji restraint (#684 宿主 rant 2026-08-11T17:58:11:README 标题/特性表/流程图 emoji 泛滥——装饰性 emoji 从标题(# 🧱→#、## ✨→##、## 🔄→##、## 🚀→##、### 📦→###)与特性表行移除,EN/CN 对称;演化流程图改纯文本(Inputs / 演化循环 / 编号步骤);doc-count 新增标题无 emoji 守卫(`^#{1,3} ` 标题行禁 emoji,对比表 ✅/❌、底部 ❤️、语言切换 🇬🇧/🇨🇳 保留在正文非标题);MANIFESTO.md 零改动;合并 80a338a) ✅ > - Release v0.2.26 (#686 宿主 rant 2026-08-11T18:16:52:master 自 v0.2.25 (c8eb77a) 以来 11 commits / 11 PRs(#676-#686),宿主确认发布下一版本;版本号 0.2.25→0.2.26 同步 7 files / 8 refs(emrg/__init__.py、pyproject.toml、uv.lock、gui/package.json、gui/package-lock.json、build-runtime.sh、make-installer.sh),grep 0.2.25 仅历史;pytest 694 + GUI 212 绿;Build Release v0.2.26 成功,7 assets(含 Windows zip #649),latest=v0.2.26;合并 f2ba4be) ✅ diff --git a/tests/test_installer_stop.py b/tests/test_installer_stop.py index 726d1ec..ecf3773 100644 --- a/tests/test_installer_stop.py +++ b/tests/test_installer_stop.py @@ -39,12 +39,16 @@ def test_stop_emrg_cmd_covers_gui_tui_daemon_in_order(): # 顺序:GUI 在 daemon 之前(GUI 不能复活 daemon) daemon_line = content.index('call "%INSTALL%\\bin\\emrg.cmd" server stop') assert content.index("taskkill /IM EMRG.exe") < daemon_line - # step 4(rant 2026-08-11T17:56:25):按可执行文件路径杀 bundled git 孤儿 - # (daemon 演化周期 git 操作中途被杀 → git/ssh/bash 孤儿锁 msys-2.0.dll → Inno - # DeleteFile code 5)。判别:ExecutablePath 前缀过滤只命中 %INSTALL%\git\, - # 不误杀系统 Git;daemon 停止之后、:verify 之前。 + # step 4(rant 2026-08-11T17:56:25 → 18:56:58 修正):只杀 EMRG 自己 git 子进程树 + # (祖先链含 daemon/TUI),不碰宿主 Git Bash sh/vim(R125 同族——宿主工具不干涉)。 + # 判别:ExecutablePath 前缀过滤只命中 %INSTALL%\git\(不误杀系统 Git), + # 且用 ParentProcessId 祖先回溯(≤5 层)+ `-m emrg` 判定 EMRG 归属; + # daemon 停止之后、:verify 之前。 assert 'Get-CimInstance Win32_Process' in content assert r'$env:USERPROFILE\.emrg\install\git\*' in content + # 祖先回溯是 step 4 的核心判别(宿主 Git Bash sh/vim 的祖先链是 explorer/终端 → 不杀) + assert "ParentProcessId" in content + assert "-match '-m emrg'" in content # 锚定实际 kill 命令($_.ExecutablePath 只在 step 4 出现——TUI 分支是 # -Filter Name='python.exe',不共享此模式),勿用 index() 撞到 TUI/REM 注释 git_kill = content.index("$_.ExecutablePath -like") @@ -62,10 +66,18 @@ def test_stop_emrg_cmd_covers_gui_tui_daemon_in_order(): assert "PID eq !DPID!" in verify_block # 非延迟展开 %DPID% 不得出现在括号块内(块解析时展开=恒旧值) assert "%DPID%" not in verify_block - # :verify 的 bundled-git 存活判定:仍有进程在 install\git\ 下 → exit 1 + # :verify 的 EMRG-owned bundled-git 存活判定:只有 EMRG git 子树残留 → exit 1; + # 宿主 sh/vim 存活不是失败(rant 2026-08-11T18:56:58)——判别:verify 块含 + # 祖先回溯判定(ParentProcessId + -m emrg),但不再有无差别 ExecutablePath 存活检查 assert "exit 1" in verify_block - assert r'ExecutablePath -like \"$env:USERPROFILE\.emrg\install\git\*\"' in verify_block assert "if errorlevel 1 set \"EXIT_CODE=1\"" in verify_block + assert "ParentProcessId" in verify_block + assert "-match '-m emrg'" in verify_block + # 无差别 bundled-git 存活检查必须已删除:verify 块里不得再有"任何 install\git\ 进程 + # 即 exit 1"的旧逻辑(旧式:if (Get-CimInstance ...) { exit 1 }) + assert "}) { exit 1 }" not in verify_block + # 而 step 4 的 kill 命令必须保留(EMRG-owned 判定在 kill 与 verify 都出现) + assert "Stop-Process" in content def test_emrgd_cmd_has_stop_branch(): From e67e6044d23d0117df0e2502d3b3fa3db0aba324 Mon Sep 17 00:00:00 2001 From: EMRG Evolution Date: Tue, 11 Aug 2026 19:07:00 +0800 Subject: [PATCH 2/2] =?UTF-8?q?emrg:=20stop-emrg.cmd=20=E2=80=94=20snapsho?= =?UTF-8?q?t=20EMRG=20tree=20downward=20before=20kill=20(fix=20dead-parent?= =?UTF-8?q?=20orphan=20case)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bin/stop-emrg.cmd | 44 ++++++++++++++++++++++++------------ tests/test_installer_stop.py | 37 +++++++++++++++++------------- 2 files changed, 51 insertions(+), 30 deletions(-) diff --git a/bin/stop-emrg.cmd b/bin/stop-emrg.cmd index 03919ba..bf21614 100644 --- a/bin/stop-emrg.cmd +++ b/bin/stop-emrg.cmd @@ -26,6 +26,19 @@ REM Safe on clean install: no old install dir -> everything is skipped -> 0. setlocal enabledelayedexpansion set "EMRG_DIR=%USERPROFILE%\.emrg" set "INSTALL=%EMRG_DIR%\install" +REM PID snapshot file: EMRG-owned process tree captured BEFORE any kill, so dead +REM parents are irrelevant (review 2026-08-11T19:03: ancestor walk cannot resolve +REM dead daemon parents -- the #683 primary orphan case regresses). +set "PIDFILE=%TEMP%\emrg-stop-pids.txt" +del /q "%PIDFILE%" >nul 2>&1 + +REM --- 0. snapshot EMRG-owned tree (daemon + TUI + GUI + descendants) --- +REM Walk DOWN from the roots (emrgd.pid, EMRG.exe, python.exe -m emrg) while they +REM are still alive, collecting the full descendant PID set. Step 4 / :verify use +REM ONLY this recorded set -> host Git Bash sh/vim (not descendants of EMRG roots) +REM are never touched (rant 2026-08-11T18:56:58), and orphans of an already-dead +REM daemon are still captured (their PID was recorded while the daemon lived). +powershell -NoProfile -Command "$f=Get-CimInstance Win32_Process; $roots=New-Object System.Collections.Generic.List[int]; if(Test-Path \"%EMRG_DIR%\emrgd.pid\"){ $d=Get-Content \"%EMRG_DIR%\emrgd.pid\" -ErrorAction SilentlyContinue; if($d -match '^\d+$'){ $roots.Add([int]$d) } }; $f | Where-Object { $_.Name -eq 'EMRG.exe' -or ($_.Name -eq 'python.exe' -and $_.CommandLine -match '-m emrg' -and $_.CommandLine -notmatch 'emrg\.server') } | ForEach-Object { $roots.Add([int]$_.ProcessId) }; $set=New-Object 'System.Collections.Generic.HashSet[int]'; foreach($r in $roots){ [void]$set.Add($r) }; $changed=$true; while($changed){ $changed=$false; foreach($p in $f){ if($set.Contains([int]$p.ParentProcessId) -and -not $set.Contains([int]$p.ProcessId)){ [void]$set.Add([int]$p.ProcessId); $changed=$true } } }; $set | ForEach-Object { $_ } | Set-Content -Path \"%PIDFILE%\" -Encoding ascii" >nul 2>&1 REM --- 1. GUI: graceful WM_CLOSE, then unconditional /F after the grace window --- REM (host report 2026-08-10T01:27:07Z: a long-lived ~15h GUI session deferred @@ -67,17 +80,17 @@ set "DPID=" for /f "usebackq delims=" %%p in ("%EMRG_DIR%\emrgd.pid") do set "DPID=%%p" if defined DPID taskkill /F /PID %DPID% >nul 2>&1 -REM --- 4. bundled git: kill ONLY EMRG-owned git/ssh/bash subprocess trees --- +REM --- 4. bundled git: kill only RECORDED EMRG-owned git/ssh/bash (snapshot step 0) --- REM (rant 2026-08-11T17:56:25: daemon killed mid-git-op leaves orphans holding REM install\git\usr\bin\msys-2.0.dll -> Inno DeleteFile code 5.) -REM (host 2026-08-11T18:56:58: the old blanket ExecutablePath-prefix kill also -REM caught the HOST's own Git Bash sh/vim started from install\git\ -- killing -REM vim with unsaved edits and aborting the installer. Same class as R125: -REM non-EMRG processes must not be touched.) A process is EMRG-owned iff its -REM ancestor chain (<=5 levels, ParentProcessId walk) contains the daemon -REM (pythonw.exe -m emrg.server) or the TUI (python.exe -m emrg). Host Git Bash -REM sh/vim have explorer/terminal ancestors -> never matched, never killed. -powershell -NoProfile -Command "$all = Get-CimInstance Win32_Process; $git = $all | Where-Object { $_.ExecutablePath -like \"$env:USERPROFILE\.emrg\install\git\*\" }; foreach ($p in $git) { $cur = $p; $emrg = $false; for ($i = 0; $i -le 5 -and $cur; $i++) { if ($cur.CommandLine -match '-m emrg') { $emrg = $true; break }; $cur = $all | Where-Object { $_.ProcessId -eq $cur.ParentProcessId } }; if ($emrg) { Stop-Process -Id $p.ProcessId -Force -ErrorAction SilentlyContinue } }" >nul 2>&1 +REM (host 2026-08-11T18:56:58: blanket path kill also caught the HOST's Git Bash +REM sh/vim -- vim edits lost + installer aborted. Same class as R125.) Only PIDs +REM recorded in step 0 (EMRG tree captured while daemon/TUI were alive) are killed +REM when their executable is under install\git\. Host Git Bash sh/vim are not in +REM the recorded set -> never touched. +if exist "%PIDFILE%" ( + powershell -NoProfile -Command "$f=Get-CimInstance Win32_Process; $ids=@(Get-Content \"%PIDFILE%\" | ForEach-Object { [int]$_ }); $f | Where-Object { $ids -contains [int]$_.ProcessId -and $_.ExecutablePath -like \"$env:USERPROFILE\.emrg\install\git\*\" } | ForEach-Object { Stop-Process -Id $_.ProcessId -Force -ErrorAction SilentlyContinue }" >nul 2>&1 +) :verify set "EXIT_CODE=0" @@ -91,9 +104,12 @@ if exist "%EMRG_DIR%\emrgd.pid" ( tasklist /FI "PID eq !DPID!" 2>nul | findstr /i "!DPID!" >nul && set "EXIT_CODE=1" ) ) -REM EMRG-owned bundled-git survival check: only an EMRG git subprocess tree that -REM could not be killed -> exit 1 (installer aborts with a restart hint). Host Git -REM Bash sh/vim still alive is NOT a failure (rant 2026-08-11T18:56:58). -powershell -NoProfile -Command "$all = Get-CimInstance Win32_Process; $git = $all | Where-Object { $_.ExecutablePath -like \"$env:USERPROFILE\.emrg\install\git\*\" }; $emrg = $false; foreach ($p in $git) { $cur = $p; for ($i = 0; $i -le 5 -and $cur; $i++) { if ($cur.CommandLine -match '-m emrg') { $emrg = $true; break }; $cur = $all | Where-Object { $_.ProcessId -eq $cur.ParentProcessId } }; if ($emrg) { break } }; if ($emrg) { exit 1 }" >nul 2>&1 -if errorlevel 1 set "EXIT_CODE=1" +REM EMRG-owned bundled-git survival check: only RECORDED EMRG git PIDs still alive +REM -> exit 1 (installer aborts with a restart hint). Host Git Bash sh/vim alive is +REM NOT a failure (rant 2026-08-11T18:56:58). +if exist "%PIDFILE%" ( + powershell -NoProfile -Command "$f=Get-CimInstance Win32_Process; $ids=@(Get-Content \"%PIDFILE%\" | ForEach-Object { [int]$_ }); $surv = $f | Where-Object { $ids -contains [int]$_.ProcessId -and $_.ExecutablePath -like \"$env:USERPROFILE\.emrg\install\git\*\" }; if ($surv) { exit 1 }" >nul 2>&1 + if errorlevel 1 set "EXIT_CODE=1" +) +del /q "%PIDFILE%" >nul 2>&1 endlocal & exit /b %EXIT_CODE% diff --git a/tests/test_installer_stop.py b/tests/test_installer_stop.py index ecf3773..6984778 100644 --- a/tests/test_installer_stop.py +++ b/tests/test_installer_stop.py @@ -39,22 +39,26 @@ def test_stop_emrg_cmd_covers_gui_tui_daemon_in_order(): # 顺序:GUI 在 daemon 之前(GUI 不能复活 daemon) daemon_line = content.index('call "%INSTALL%\\bin\\emrg.cmd" server stop') assert content.index("taskkill /IM EMRG.exe") < daemon_line - # step 4(rant 2026-08-11T17:56:25 → 18:56:58 修正):只杀 EMRG 自己 git 子进程树 - # (祖先链含 daemon/TUI),不碰宿主 Git Bash sh/vim(R125 同族——宿主工具不干涉)。 - # 判别:ExecutablePath 前缀过滤只命中 %INSTALL%\git\(不误杀系统 Git), - # 且用 ParentProcessId 祖先回溯(≤5 层)+ `-m emrg` 判定 EMRG 归属; - # daemon 停止之后、:verify 之前。 + # step 4(rant 2026-08-11T17:56:25 → 18:56:58 修正):只杀 EMRG 自己 git 子进程树, + # 不碰宿主 Git Bash sh/vim(R125 同族——宿主工具不干涉)。 + # 判别:ExecutablePath 前缀过滤只命中 %INSTALL%\git\(不误杀系统 Git)。 + # ⚡ review 2026-08-11T19:03:祖先回溯(ancestor walk)无法解析"已死父进程"—— + # #683 主场景正是 daemon 已死后的孤儿 → 改为 step 0 **向下**快照 + # (daemon/TUI/GUI 根 + BFS 子孙集,写入 %TEMP%\emrg-stop-pids.txt), + # step 4 / :verify 只作用于快照记录集。 assert 'Get-CimInstance Win32_Process' in content assert r'$env:USERPROFILE\.emrg\install\git\*' in content - # 祖先回溯是 step 4 的核心判别(宿主 Git Bash sh/vim 的祖先链是 explorer/终端 → 不杀) + # step 0 快照:向下 BFS(ParentProcessId → 子进程加入集合)在杀任何进程之前 + assert "emrg-stop-pids.txt" in content assert "ParentProcessId" in content assert "-match '-m emrg'" in content - # 锚定实际 kill 命令($_.ExecutablePath 只在 step 4 出现——TUI 分支是 - # -Filter Name='python.exe',不共享此模式),勿用 index() 撞到 TUI/REM 注释 - git_kill = content.index("$_.ExecutablePath -like") + assert "Set-Content" in content # 快照写盘 + # step 4 的 kill 必须基于快照记录集($ids -contains)+ ExecutablePath 过滤 + step4_idx = content.index("REM --- 4. bundled git: kill only RECORDED") daemon_line2 = content.index('call "%INSTALL%\\bin\\emrg.cmd" server stop') verify_idx = content.index(':verify\nset "EXIT_CODE=0"') # 标签定义处(goto :verify 在前,勿用裸 index(":verify")) - assert daemon_line2 < git_kill < verify_idx + assert daemon_line2 < step4_idx < verify_idx + assert "$ids -contains [int]$_.ProcessId" in content # 干净安装安全:无旧 install 目录时跳过 assert 'set "INSTALL=%EMRG_DIR%\\install"' in content # 括号块内 pid 判定必须用延迟展开(!DPID!)——%DPID% 在块解析时展开, @@ -66,18 +70,19 @@ def test_stop_emrg_cmd_covers_gui_tui_daemon_in_order(): assert "PID eq !DPID!" in verify_block # 非延迟展开 %DPID% 不得出现在括号块内(块解析时展开=恒旧值) assert "%DPID%" not in verify_block - # :verify 的 EMRG-owned bundled-git 存活判定:只有 EMRG git 子树残留 → exit 1; - # 宿主 sh/vim 存活不是失败(rant 2026-08-11T18:56:58)——判别:verify 块含 - # 祖先回溯判定(ParentProcessId + -m emrg),但不再有无差别 ExecutablePath 存活检查 + # :verify 的 EMRG-owned bundled-git 存活判定:只有快照记录集内的 EMRG git PID 残留 + # → exit 1;宿主 sh/vim 存活不是失败(rant 2026-08-11T18:56:58)——判别:verify 块 + # 含 $ids -contains(快照集过滤),但不再有无差别 ExecutablePath 存活检查 assert "exit 1" in verify_block assert "if errorlevel 1 set \"EXIT_CODE=1\"" in verify_block - assert "ParentProcessId" in verify_block - assert "-match '-m emrg'" in verify_block + assert "$ids -contains" in verify_block # 无差别 bundled-git 存活检查必须已删除:verify 块里不得再有"任何 install\git\ 进程 # 即 exit 1"的旧逻辑(旧式:if (Get-CimInstance ...) { exit 1 }) assert "}) { exit 1 }" not in verify_block - # 而 step 4 的 kill 命令必须保留(EMRG-owned 判定在 kill 与 verify 都出现) + # 而 step 4 的 kill 命令必须保留(快照集过滤 + ExecutablePath) assert "Stop-Process" in content + # 快照文件清理 + assert 'del /q "%PIDFILE%"' in content def test_emrgd_cmd_has_stop_branch():