---
name: unidesk-gc
description: >-
  UniDesk 磁盘 GC 与主机压力处置流程。用于 `bun scripts/cli.ts gc ...`、
  根盘高水位、worktree/缓存/日志清理、Web observe/Chrome 内存压力和安全停止判定。
---

# UniDesk GC

- 处理 UniDesk 磁盘或内存压力时，优先使用受控 UniDesk CLI。
- 到达保护边界后停止，不得扩大为临时 `rm -rf`、Docker prune、数据库清理、原生 Kubernetes 删除或运行时状态删除。

- 长期规则以 `docs/reference/gc.md` 为准。
- 执行远端 GC、k3s/PVC 归因、Web observe/Chrome 压力处置、registry/CI 留存或安全停止判定前，必须先读该文档。

## 本机流程

先执行只读归因：

```bash
df -h /
df -BG /
bun scripts/cli.ts gc plan --target-use-percent 69 --limit 50
```

默认计划存在缺口时，显式启用候选类别：

```bash
bun scripts/cli.ts gc plan --target-use-percent 69 --limit 2000 \
  --include-tool-caches \
  --include-stale-tmp \
  --include-vscode-stale-servers \
  --include-vscode-stale-extensions \
  --include-vscode-cached-vsix \
  --include-baidu-staging \
  --include-state-artifacts \
  --include-state-stale-scratch \
  --include-codex-sessions \
  --include-merged-worktrees \
  --include-vpn-diagnostic-logs
```

复核计划后，只执行相同候选范围：

```bash
bun scripts/cli.ts gc run --confirm --target-use-percent 69 --limit 2000 \
  --include-tool-caches \
  --include-stale-tmp \
  --include-vscode-stale-servers \
  --include-vscode-stale-extensions \
  --include-vscode-cached-vsix \
  --include-baidu-staging \
  --include-state-artifacts \
  --include-state-stale-scratch \
  --include-codex-sessions \
  --include-merged-worktrees \
  --include-vpn-diagnostic-logs
```

worktree 因 merge/cherry 超时而受保护时，只提高 worktree 临时预算：

```bash
bun scripts/cli.ts gc plan --target-use-percent 69 --limit 2000 \
  --include-merged-worktrees \
  --worktree-scan-budget-ms 120000 \
  --worktree-cherry-check-timeout-ms 10000 \
  --no-file-logs --no-docker-logs --no-journal --no-build-cache --no-tmp --no-db-summary
```

- 只对计划仍显示的候选执行匹配的 `run --confirm`。
- dirty、近期、活跃、未合并或超时保护的 worktree 必须继续受保护。

## 缓存与日志

单独检查 Docker 镜像候选：

```bash
bun scripts/cli.ts server cleanup plan --min-age-hours 24 --limit 80
```

- 返回零个过期镜像候选时，不得使用 `docker image prune` 或 `docker system prune`。
- 受保护镜像可能仍是当前运行或回滚真相。

默认 BuildKit 清理可能因缓存均较新而实际释放 `0B`。只有明确止压时才使用 `--build-cache-all`：

```bash
bun scripts/cli.ts gc plan --target-use-percent 69 --build-cache-all --limit 50 \
  --no-file-logs --no-docker-logs --no-journal --no-tmp --no-db-summary
bun scripts/cli.ts gc run --confirm --target-use-percent 69 --build-cache-all --limit 50 \
  --no-file-logs --no-docker-logs --no-journal --no-tmp --no-db-summary
```

Journald 上限也通过同一 CLI 管理：

```bash
bun scripts/cli.ts gc plan --target-use-percent 69 --journal-target-size 128M --limit 50 \
  --no-file-logs --no-docker-logs --no-build-cache --no-tmp --no-db-summary
```

计划可接受时，使用参数完全匹配的 `run --confirm`。

## 临时转储

- `/tmp/unidesk-cli-output` 保存超大 JSON/stdout。
- 截断的 plan/run 会持续写入新转储，因此 GC 诊断时可能增长到 GiB 级。
- 提取必要证据并确认没有活跃写入者后，可以删除该目录：

```bash
fuser -v /tmp/unidesk-cli-output 2>&1 || true
rm -rf -- /tmp/unidesk-cli-output
```

重复出现时应补受控 CLI 留存策略，不得把手工删除变成常规入口。

- 其他 `/tmp` 目录先检查大小、mtime 和活跃 fd。
- 未明确归属和可重建性时，不得删除可能属于并行任务的当日 source/workspace scratch。

## 远端节点

- provider host 统一使用 `bun scripts/cli.ts gc remote <providerId> ...`。
- 远端长任务必须异步执行，并用 `status --job-id` 短查询，不得维持长 SSH 会话。

## WebProbe 低内存人工处置

- 唯一资格指标：
  - 只读取 `/proc/meminfo` 的 `MemAvailable`；
  - 输出必须披露 `metric=MemAvailable`、`source=/proc/meminfo` 和 `swapExcluded=true`；
  - swap 只作为独立压力信号，绝不计入启动可用量；
  - 当前阈值为 `4 GiB`：人工启动在 `MemAvailable <= 4 GiB` 时阻断，sentinel 在 `< 4 GiB` 时跳过本轮。
- 人工处置命令必须按顺序执行：

```bash
bun scripts/cli.ts gc remote <node> plan --memory-pressure-only
bun scripts/cli.ts gc remote <node> run --confirm --memory-pressure-only
bun scripts/cli.ts gc remote <node> status --job-id <id>
bun scripts/cli.ts gc remote <node> memory
```

- 顺序约束：
  - 先复核 scoped plan，只允许执行其中明确标记安全的内存压力候选；
  - `run` 返回 job id 后，重复执行 `status --job-id <id>` 直到终态；
  - job 未终态时不得提前执行 `memory` 或重试 WebProbe；
  - 终态后执行 `memory`，只有 `MemAvailable > 4 GiB` 才可重试人工启动；
  - 内存处置不得改用未带 `--memory-pressure-only` 的通用 plan/run。
- sentinel cadence：
  - `< 4 GiB` 时返回 `skipped-wait-next-round`；
  - 不创建 observer、不启动 Chromium、不自动执行 GC；
  - 等待下一轮 cadence 重新读取 `MemAvailable`。

以下远端场景必须先读 `docs/reference/gc.md`：

- G14 registry/CI workspace 留存、k3s/PVC 归因和安全停止判定；
- PK01 pikanode 临时目录留存与 Docker provider 安全边界；
- JD01 k3s/PVC 归因，以及 NC01/JD01 Web observe artifact、Chrome/observer 内存增长与 YAML-first 真相源核对。

## Web observe 与 Chrome 逃逸进程

- 正常生命周期：
  - 远端节点的 Chrome 内存增长应先按 observer 生命周期问题处理；
  - sentinel/quick-verify 的全部终态必须停止对应 observer；
  - runner 的 TTL、maxSamples 和 artifact cap 必须来自 YAML；
  - 归属尚未判清时不得按名称终止 Chrome，也不得删除 web-observe 目录。
- 保护边界：
  - active observer、受控任务仍引用的浏览器会话及其完整进程树始终受保护。
- 只有独立且无归属的 Playwright `cliDaemon`/Chrome 进程树才可进入精确终止：
  - 存活时间超过 owning YAML 定义的异常存活窗口；
  - 受控 observe/sentinel 状态未显示该进程树归属于 active observer 或在途受控任务；
  - manifest、heartbeat、PID/session 记录均不能把它归属到有效 run；
  - `PID`、`PPID`、`SID`、`startTicks` 与后代进程能闭合为唯一身份；
  - 任一证据缺失或存在歧义时继续标记 protected。
- 精确终止：
  - TERM 前重新核对 `PID/PPID/SID/startTicks`，防止 PID 复用；
  - 只向已核实的唯一进程树发送 TERM；
  - 按 YAML 定义的有界等待时间重新枚举；
  - 仍未退出时，只以不可变的 `PID/startTicks` 复核残留身份；
  - 同时披露原始与当前 `PPID/SID`，允许 TERM 后重挂载但不放宽 PID 复用保护；
  - 只对复核后的同一残留进程树发送 KILL。
- 禁止批量终止：
  - 不得使用 `pkill -f chrome`、`killall chrome` 或其他按进程名批量终止方式；
  - 证据不完整或归属存在歧义时保持 protected，不得猜测。
- 终止后验证：
  - 确认目标 PID、后代 PID 和 session/process group 全空；
  - 确认 active observer 未受影响；
  - 重新采集 `/proc/meminfo` 的 `MemAvailable`、独立 swap 信号、observer 状态和 Chrome/runner 摘要；
  - 结构化记录判定证据、TERM/KILL 结果、残留进程与终止前后资源数据。

JD01 等远端节点只能显式启用可重建工具缓存：

```bash
bun scripts/cli.ts gc remote JD01 plan --target-use-percent 49 --include-tool-caches --limit 100
bun scripts/cli.ts gc remote JD01 run --confirm --target-use-percent 49 --include-tool-caches --limit 100
```

- 必须先复核 plan。
- 候选仅限固定 allowlist 的 npm/npx/Bun 缓存。
- 不得改用 `rm -rf ~/.npm ~/.bun`，也不得触碰 `node_modules`、auth/config、k3s/containerd、PVC 或 Docker volume。

## 保护边界

以下对象不得作为通用磁盘止压目标：

- `docker system prune`、`docker image prune`、Docker/Compose volume 删除；
- 未经 `gc db-trace`、备份和维护窗口的 PostgreSQL PGDATA/trace 清理；
- `/var/lib/containerd`、`/var/lib/rancher/k3s`、`/var/lib/kubelet`、PVC、registry blob、runtime snapshot；
- Codex auth/config/profile；session 仅能通过 `--include-codex-sessions` 清理，活跃 SQLite 日志还需 `fuser` 与专门留存决策；
- 以下 Web observe 与 Chrome 对象：
  - active run 和 live observer runner；
  - 归属或 stale 分类未闭合的 Chrome process tree；
  - 缺少 manifest/heartbeat/pid/open-fd 证据的 web-observe state root；
  - 只有按“Web observe 与 Chrome 逃逸进程”完成无归属判定的唯一进程树才可精确终止。
- dirty、active、unmerged、recent 或 timeout-protected worktree；
- 用户未明确要求时的 `backend-core` rebuild/restart/replacement。

- 低风险候选执行后仍为 `summary.target.safeStop=true` 时，停止并报告剩余压力源与决策选项。
- 不得绕过 CLI 追求目标百分比。

## 验证

使用以下有界证据收口：

```bash
df -h /
df -BG /
docker system df
du -sh /root/unidesk/.worktree /root/unidesk/.state /tmp /var/log 2>/dev/null || true
```

- 远端内存/Chrome 压力还需记录 `free -h`、Chrome/observer 摘要与 web-observe state 大小。
- 总结起止 `df` 百分比、主要清理类别、受保护失败和剩余高风险压力源。
