diff --git a/.agents/notes/implemented/process/2026-08-28-test-temp-dir-self-cleanup.i18n.yaml b/.agents/notes/implemented/process/2026-08-28-test-temp-dir-self-cleanup.i18n.yaml index b809d76982..11992e0ef1 100644 --- a/.agents/notes/implemented/process/2026-08-28-test-temp-dir-self-cleanup.i18n.yaml +++ b/.agents/notes/implemented/process/2026-08-28-test-temp-dir-self-cleanup.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write .agents/notes/implemented/process/2026-08-28-test-temp-dir-self-cleanup.md -2026-08-28-test-temp-dir-self-cleanup.md: a9190878628d1c7bc7be87ff6c90297256a5a32f -2026-08-28-test-temp-dir-self-cleanup.zh.md: 1b7b80d3e6eeaa66b2ea8e49722bc7e7d88433da +2026-08-28-test-temp-dir-self-cleanup.md: d8c7d6ef3efd0538adfc3b6d0269119f61861e08 +2026-08-28-test-temp-dir-self-cleanup.zh.md: 1e811dd820d2e5ede62ae7d4fb523e317d2372e7 diff --git a/.agents/notes/implemented/process/2026-08-28-test-temp-dir-self-cleanup.md b/.agents/notes/implemented/process/2026-08-28-test-temp-dir-self-cleanup.md index a919087862..d8c7d6ef3e 100644 --- a/.agents/notes/implemented/process/2026-08-28-test-temp-dir-self-cleanup.md +++ b/.agents/notes/implemented/process/2026-08-28-test-temp-dir-self-cleanup.md @@ -15,7 +15,7 @@ Retrofit removal of every `dsh-*` temp dir a spec file creates, at the owning te - Spec files that created dirs without removing any now track each created root in a module-level list and delete the list in `afterEach`/`afterAll` (`rm`/`rmSync` with `recursive: true, force: true`), the convention already used across the session packages. Root-creating helpers (`tmp()`, `tempDir()`, `fakeLauncher()`, harness functions) register the root at creation, so every caller is covered at one point. - Module-scope fixture dirs shared by a whole file (executor spill dirs) are removed in `afterAll` after the last test. - The file list came from the observed-residue inventory on the CI host (a template histogram of current `/tmp/dsh-*` dirs): only spec files whose dirs actually appeared were leak sources. Files that already remove their dirs (agent-team, tool-subagent, list-children, hooks coverage cases) were confirmed clean on the normal-exit path and left unchanged. -- Product cleanup is limited to the per-process spill directory of `dsh-subprocess-local/spawn` (`privateSpillDir`): it is removed at a JavaScript-observable process exit while it is EMPTY — collectors unlink their spill files on dispose, so a normal exit leaves at most empty residue, and a directory still holding spill files keeps them (their content may outlive the process). The removal is best-effort (a Windows-held handle must not change the exit code). `dsh-spill-local`'s default root is deliberately NOT exit-deleted: it is covered by the package's own 30-day startup sweep, and the retention decision in the 2026-07-17 local-spill-startup-cleanup note forbids deleting fresh spill artifacts that resumed or forked sessions may still reference. +- Product cleanup is limited to the per-process spill directory of `dsh-subprocess-local/spawn` (`privateSpillDir`): it is removed at a JavaScript-observable process exit while it is EMPTY — collectors unlink their spill files on dispose, so a normal exit leaves at most empty residue, and a directory still holding spill files keeps them (their content may outlive the process). The removal is best-effort (a Windows-held handle must not change the exit code). `dsh-spill-local`'s default root is deliberately NOT exit-deleted: it is covered by the package's own 30-day startup sweep, and the [retention decision](../architecture/2026-07-17-local-spill-startup-cleanup.md) forbids deleting fresh spill artifacts that resumed or forked sessions may still reference. ## Verification diff --git a/.agents/notes/implemented/process/2026-08-28-test-temp-dir-self-cleanup.zh.md b/.agents/notes/implemented/process/2026-08-28-test-temp-dir-self-cleanup.zh.md index 1b7b80d3e6..1e811dd820 100644 --- a/.agents/notes/implemented/process/2026-08-28-test-temp-dir-self-cleanup.zh.md +++ b/.agents/notes/implemented/process/2026-08-28-test-temp-dir-self-cleanup.zh.md @@ -15,7 +15,7 @@ Status: implemented - 创建目录但从不删除的 spec 文件,现在把每个创建的 root 记入模块级列表,并在 `afterEach`/`afterAll` 里删除(`rm`/`rmSync` 带 `recursive: true, force: true`)——与 session 包既有的 `roots.splice(0)` 约定一致。创建 root 的 helper(`tmp()`、`tempDir()`、`fakeLauncher()`、harness 函数)在创建处登记,一个点覆盖全部调用方。 - 整文件共享的模块级 fixture 目录(executor spill 目录)在最后一个测试之后的 `afterAll` 里删除。 - 目标文件清单来自 CI 主机上的残留实测清单(当前 `/tmp/dsh-*` 目录的模板直方图):只有目录确实出现在残留里的 spec 文件才是泄漏源。已有删除逻辑的文件(agent-team、tool-subagent、list-children、hooks coverage cases)确认在正常结束路径上本来干净,不改。 -- 产品侧清理限定在 `dsh-subprocess-local/spawn` 的每进程 spill 目录(`privateSpillDir`):在 JavaScript 可观察的进程退出时仅当目录**为空**才删除——collector 在 dispose 时删除自己的 spill 文件,因此正常退出最多留下空目录残留;仍持有 spill 文件的目录会保留(其内容可能比进程更长命)。删除是 best-effort(Windows 上被占用的句柄不得改变退出码)。`dsh-spill-local` 的默认 root **刻意不做**退出删除:该 root 由包自带的 30 天启动 sweep 覆盖,且 2026-07-17 local-spill-startup-cleanup note 的保留策略禁止删除 resume/fork 会话仍可能引用的新 spill 产物。 +- 产品侧清理限定在 `dsh-subprocess-local/spawn` 的每进程 spill 目录(`privateSpillDir`):在 JavaScript 可观察的进程退出时仅当目录**为空**才删除——collector 在 dispose 时删除自己的 spill 文件,因此正常退出最多留下空目录残留;仍持有 spill 文件的目录会保留(其内容可能比进程更长命)。删除是 best-effort(Windows 上被占用的句柄不得改变退出码)。`dsh-spill-local` 的默认 root **刻意不做**退出删除:该 root 由包自带的 30 天启动 sweep 覆盖,且[保留策略 note](../architecture/2026-07-17-local-spill-startup-cleanup.zh.md)禁止删除 resume/fork 会话仍可能引用的新 spill 产物。 ## Verification diff --git a/packages/subprocess/subprocess-local/src/spawn.ts b/packages/subprocess/subprocess-local/src/spawn.ts index 6359aa98e0..faaf0e4d1f 100644 --- a/packages/subprocess/subprocess-local/src/spawn.ts +++ b/packages/subprocess/subprocess-local/src/spawn.ts @@ -84,7 +84,10 @@ let defaultSpillDir: string | undefined /** * The default spill location: a private (0700) per-process directory under * the OS tmpdir, created lazily. Predictable world-readable paths would let - * other local users read command output or pre-create symlinks. + * other local users read command output or pre-create symlinks. At a + * JavaScript-observable process exit the directory is removed while empty + * (collectors unlink their spill files on dispose); a directory still holding + * spill files keeps them for external cleanup. */ function privateSpillDir(): string { defaultSpillDir ??= mkdtempSync(join(tmpdir(), 'dsh-subprocess-'))