# Gitea host 上的 act_runner 撑不住，capacity=0 ≠ 不接活

## 现象

2026-05-01 深夜，PR #218 因 backend-integration 失败需要重跑 CI。短时间内连续推 4 次提交（每次都触发完整 CI：build-check + backend-integration + 5 个 quality-gates），观察到：

- 第 4 次 push 后约 30 分钟，Gitea web (`http://43.130.59.228`) 开始返回 504
- 同时刻 build-check 和 backend-integration **同一秒变 failure**
- 几分钟后 Gitea API 又恢复，但 PR 状态显示两个 check 都 fail

## 根因

`docs/ops/02-gitea-config.md` 当前注释认为 Gitea host 上的兜底 `act-runner` (`workspace-runner`) 配置 `capacity: 0` 是"软优先级"——"重型 job 会被路由到 dedicated-runner-1，轻量 job 偶尔仍会落到这里，不会 OOM"。

实测**这个假设站不住**：

1. `capacity: 0` 在 act_runner 里语义是 **"无并发上限"**，**不是 "不接活"**。两台 runner 都标了 `ubuntu-latest:host`，调度按 round-robin / 任务到达。
2. Gitea host 总内存只有 1.9G，跑 Gitea web (~220MB) 已占大头，几乎没余量给 build-check (next.js build ~3-4GB) 或 backend-integration (起 Postgres + Redis + 跑 jest)。
3. SSH 上去看 `journalctl -b -1`：23:19 开始持续 30+ 条 `Under memory pressure, flushing caches`，23:26 最后一条 log，23:28 整机 reboot（`panic=5` 内核命令行参数 → kernel panic + 5s 自动重启）。
4. `last -x reboot` 印证：23:28 硬重启，没有 graceful shutdown。

## 修复

```bash
ssh ubuntu@43.130.59.228 'sudo systemctl stop act-runner && sudo systemctl disable act-runner'
```

停掉并禁止开机启动。重型 job 全走 dedicated-runner-1（43.166.205.48 / 16G）。

## 后续

- 修 `docs/ops/02-gitea-config.md` 第 78-80 行关于 `capacity=0` 的错误注释
- 如果将来需要兜底 runner（dedicated 挂掉时），方案是：在 Gitea host 上跑 runner 但**加 systemd cgroup 内存上限**（`MemoryMax=512M`）防止 OOM 拖死整机；或者**不同 label**（如 `ubuntu-light:host`）只接超轻量 job

## 排查路径

CI job 同时刻整批 fail（5 个 check 同一秒变 failure），且 web UI 短暂 504/慢 → **怀疑基础设施挂了，而不是代码问题**：

```bash
# 1. 看 host 是否重启
ssh ubuntu@<host> 'uptime; last -x reboot | head -3'

# 2. 看 OOM 痕迹
ssh ubuntu@<host> 'sudo journalctl -b -1 --since "<crash time -30min>" | grep -iE "memory pressure|oom|killed|panic"'

# 3. 看当前内存
ssh ubuntu@<host> 'free -m'
```

uptime 几分钟 + memory pressure log + 同时刻 reboot → OOM panic，不是代码问题，重跑 CI 即可。

## 触发场景

- Gitea host 上跑 act_runner 兜底
- 短时间密集 push 触发多次重 CI
- "为什么我代码没动 CI 突然 fail 了？"

## AI 操作建议

我（AI）这次的失误：
- 同 PR 2 次相邻 push（factory fix + 单独的 learning commit）应该合一个 commit，少触发一次完整 CI（约 12min）
- CLAUDE.md 早写明"learnings 必须**在 git commit 之前**写入 .learnings/"，遵守这条规则就少诱发一次 OOM 风险
