# Smoke Test

> CloseCrab bot 部署/重启后健康检查。当用户说"体检"、"smoke test"、"health check"、"检查 bot"、"bot 还好吗"、"刚部署完确认下"或部署/重启后想验证状态时触发。覆盖进程、Firestore、worker secrets、日志活性、近期错误，并扫描 `~/.closecrab/smoke-tests.d/` 下用户自定义 drop-in 检查。

- Skill: `yangwhale/smoke-test` (Agent Skill)
- Install (CLI): `npx skillmds@latest add yangwhale/smoke-test`
- Raw SKILL.md: https://api.skillmd.com/api/skills/yangwhale/smoke-test/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: yangwhale (https://skillmd.com/u/yangwhale)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/yangwhale/smoke-test

---


# CloseCrab Bot Smoke Test

部署/重启 CloseCrab bot 后的一站式健康检查。基于 GBrain `smoke-test` 设计模式移植 — 但 **v1 是 detect-only**（不做 auto-fix，因为 production bot 上自动修复风险太高）。

**v2（Round 3 沉淀, 2026-05-20）**: 加入 evolution loop 学到的两项 anti-pattern 检查:
- `binary_alignment` — 比对 bot 进程 lstart vs git HEAD commit time，落后即 FAIL，并打印 SIGHUP 重启命令。防 Round 3 anti-pattern 1（stale binary 跑 fast-path test 测出假 PASS）。
- `fast_path_callbacks` — 静态扫 `closecrab/channels/*.py`，所有 `_make_input_callback` 必须有 `is_inbox` 参数。防 Round 2/3 anti-pattern（inbox 派活走 user-facing 5min × N timeout）。

这两个 check 由 `scripts/check-binary-alignment.py` + bash 内嵌 grep 实现，无新依赖。

## 触发条件

- 用户说："体检 bunny"、"检查 bot 状态"、"smoke test"、"刚重启完确认下"
- 任何 bot deploy / restart 后的自我验证
- Inbox 收到 `/health` 命令（agent 自动回 JSON）
- 调度脚本 `dispatch-bot.sh` 之后

## 用法

```bash
# 检查单个 bot（带颜色，人读）
~/CloseCrab/scripts/closecrab-smoke-test.sh bunny

# 检查本机所有跑着的 bot
~/CloseCrab/scripts/closecrab-smoke-test.sh --all

# 只看总结行（适合脚本调用）
~/CloseCrab/scripts/closecrab-smoke-test.sh bunny --quiet

# JSON 输出（适合 agent 自动消费 / inbox 回复）
~/CloseCrab/scripts/closecrab-smoke-test.sh bunny --json

# JSON + 修复建议（skillpack-check 模式，给 agent 看）
~/CloseCrab/scripts/closecrab-smoke-test.sh bunny --json --actions
```

**Exit code = failed check 数**（`0` = 全过）。任何 caller 都可以拿 exit code 直接决策。

### JSON Schema（`--json` 输出）

```json
{
  "status": "ok | warn | fail",     // 总状态：fail>0 = fail, skip>0 = warn, all pass = ok
  "pass": 8,
  "fail": 1,
  "skip": 3,
  "bots": ["bunny"],
  "results": [
    {"name": "bot_process", "status": "pass", "detail": "pid=12345 rss=265MB"},
    ...
  ],
  "actions": [                       // 仅 --actions 时填充
    {
      "check": "bot_process",
      "cmd":   "cd ~/CloseCrab && nohup ./run.sh bunny > /tmp/bunny.run.log 2>&1 &",
      "reason": "Bot process not running; relaunch via run.sh supervisor."
    }
  ]
}
```

### Agent 用法（受 GBrain skillpack-check 启发）

当 agent / inbox / 其他 bot 想问 "这个 bot 还好吗"，跑：

```bash
~/CloseCrab/scripts/closecrab-smoke-test.sh <bot> --json --actions
```

然后根据返回的 `status` + `actions[]` 决策：
- `status: "ok"` → 啥都不做
- `status: "warn"` → 把 `results[]` 里 `skip` 的项目当 review 候选展示给人
- `status: "fail"` → 把 `actions[]` 里的 `cmd` paste 出来让 oncall 决定执行（**不要自动跑**）

## 输出语义

| 符号 | 状态 | 含义 |
|------|------|------|
| `✓` | pass | 检查通过 |
| `✗` | fail | 真问题，需要处理（计入 exit code） |
| `⊘` | skip | 检查不适用（如 secret 没设但可能用 ADC）— **不算 fail** |

## 内置检查

| Check | 说明 |
|-------|------|
| `bot_process` | `pgrep` 找 `python3 -m closecrab --bot {name}` 进程 |
| `run_sh_wrapper` | 是否有 `run.sh` supervisor（手动启动是 skip 不是 fail） |
| `firestore_sa_key` | `GOOGLE_APPLICATION_CREDENTIALS` 文件可读且 JSON 合法（未设 → skip，假设走 ADC） |
| `firestore_reachable` | `bots/{name}` doc 存在（用 8s timeout 防卡） |
| `worker_type` | 显示当前 worker（claude/gemini/openclaw/kilo） |
| `claude_settings` | `~/.claude/settings.json` 解析 OK |
| `worker_secret_*` | 按 worker_type 检查 — Claude 看 Vertex/API key；Gemini 看 API key；OpenClaw 看 Gateway `:18789`；Kilo 看 `kilo` 二进制 |
| `bot_log_recent` | `bot.log` mtime — <1h pass；<24h skip；>24h fail |
| `recent_errors` | 末尾 200 行的 ERROR/CRITICAL/Traceback 计数 |
| `drop_ins` | 扫描自定义检查 |

## Drop-in 自定义检查

放在 `~/.closecrab/smoke-tests.d/` 下（**所有 bot** 通用）或 `~/.closecrab/smoke-tests.d/{bot}/`（**单 bot** 专属）的 `*.sh` 文件会自动被发现执行。

合约：脚本第一行 stdout 必须以 `OK ` / `FAIL ` / `SKIP ` 开头。`BOT` 环境变量已注入。

**示例**：`~/.closecrab/smoke-tests.d/bunny/discord-token.sh`：

```bash
#!/usr/bin/env bash
set -u
token=$(timeout 5 python3 -c "
from google.cloud import firestore
db = firestore.Client(project='chris-pgp-host', database='closecrab')
d = db.collection('bots').document('$BOT').get().to_dict() or {}
print((d.get('discord') or {}).get('token','')[:10])
" 2>/dev/null)
if [ -n "$token" ]; then echo "OK discord token present (${token}...)"; else echo "FAIL no discord token"; exit 1; fi
```

## 设计原则（从 GBrain 移植 + CloseCrab 适配）

1. **Detect-only, no auto-fix** — 跑着的 production bot 自动重启会丢用户上下文，先 v1 detect → 让人/agent 决策
2. **Timeout everything** — 所有外部调用都 `timeout 5-8s`，不卡死
3. **Skip 不是 fail** — 可选能力没配（如 SA key、drop-ins 目录空）只 skip
4. **JSON-first for agents** — `--json` 输出让 inbox 消息 / cron / 其他 bot 能机器消费
5. **Drop-in 扩展** — 不改核心脚本就能为某 bot 加专属检查

## 后续可加（v2 候选）

- 按 worker_type 拉最近一次 firestore log 看 `status` / `error` 字段
- ✁ `--fix` flag 启用 careful auto-fix（如重启 cron-daemon、re-mount gcsfuse）— 需要 case-by-case 评估
- Cross-bot `/health` 聚合面板（Jarvis 一次查所有 teammate）

## 来源

移植自 [gbrain skills/smoke-test/SKILL.md](https://github.com/garrytan/gbrain/blob/main/skills/smoke-test/SKILL.md)。
GBrain 评估报告：https://cc.higcp.com/assets/gbrain-evaluation-20260519-101510.html

