# Butler

> 启动 Wiki 管家永续 loop。两队列系统（content/housekeeping）。每轮：W1队列选任务→W2执行→W3自评→记账，无需用户逐轮确认。每11轮discover+housekeeping-scan，每17轮自动/wiki发布，每29轮W5反思，每37轮H17覆盖扫描。带 --auto 参数时忽略 fail 暂停 + 上下文将满时 ScheduleWakeup 自续。项目路径见 local/config.md。

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

---


# /butler — Wiki 管家

## 授权声明

**此 skill 明确授权，覆盖 CLAUDE.md 通用限制**：
- ✅ 永续循环，无需逐轮确认
- ✅ 每 17 轮自动 `git commit` + `git push`（通过 `/wiki` skill）
- ✅ `git add docs/wiki/pages/<单个文件>`

## 工作目录与实例

见 `skills/butler/local/config.md`。

## 启动参数

| 参数 | 默认 | 说明 |
|------|------|------|
| `--focus create` | `all` | 只领取 create 类任务 |
| `--focus enrich` | `all` | 只领取 enrich 类任务 |
| `--focus housekeeping` | `all` | 只领取内务任务 |
| `--focus publish` | `all` | 只执行发布任务 |
| `--focus discover` | `all` | 只做发现任务 |
| `--focus all` | `all` | 领取任意类型任务 |
| `--instance NAME` | 见 local/config.md | 实例标识符 |
| `--auto` | off | 自动续跑模式（详见下节）|

## `--auto` 模式

触发条件：参数含 `--auto`。在普通永续 loop 之上覆盖两条暂停保护：

1. **忽略「连续 5 轮 fail」保护**
   原本连续 5 轮 fail 会立即中止 loop。`--auto` 下 fail 也继续下一轮，
   不因短期质量波动中断。但 fail 仍记账（W3 normal），fail 模式会进
   discover_by_broken_link 自纠正路径。

2. **上下文边界自续（ScheduleWakeup）**
   当本轮 release_round 之后判断剩余上下文 ≤ 10k token 时：
   - 完成当前轮 W3 记账 + release 锁（不留死锁）
   - 调用 `ScheduleWakeup({delaySeconds: 60, prompt: "/butler --auto", reason: "butler --auto 续跑（上下文将满）"})`
   - 主动结束当前会话；下次唤醒时从 `round_counter.txt` + `queue.md` 状态继续

   状态全部已经持久化在 `wiki/logs/butler/`（round_counter、queue、actions
   等），唤醒后从步骤 1 重新启动即可无缝接续。

**仍然有效的中止条件**（即使在 --auto 模式下）：
- 用户在新消息中明示「停止/pause」→ 立即中止，**不再** schedule
- `claim_round.py` 返回 `DUPLICATE`/`RACE` → 立即停止（避免双实例打架）
- 致命错误（脚本崩溃、git 不可用等）→ 报告后退出，不 schedule

> 一句话：`--auto` 让 butler 在「质量小波动」和「上下文边界」两类原本会
> 触发暂停的情况下继续跑，其他保护机制不变。

## 启动流程

```
步骤 1 · 读取状态
──────────────────────────────────
cat wiki/logs/butler/round_counter.txt
cat wiki/logs/butler/queue.md
cat wiki/logs/butler/housekeeping_queue.md
tail -10 wiki/logs/butler/actions.jsonl

python3 wiki/scripts/butler/claim_round.py --check-only --instance INSTANCE_NAME
→ stdout "DUPLICATE" → 立即停止
→ exit 0 → 继续

步骤 2 · 读规范（每次启动必读）
──────────────────────────────────
CLAUDE.md（本项目规则）
skills/butler/local/config.md（本项目路径、实例、项目专属覆盖规则）

步骤 3 · 上下文检查
──────────────────────────────────
距上次 W5 > 50 轮 → 立即执行 W5 反思

◆ 以下步骤 4–9 构成永续循环 ◆

步骤 4 · 周期任务检查（在领锁之前）
──────────────────────────────────
round % 29 == 0  → W5 反思
round % 17 == 0  → /wiki 发布
round % 11 == 0  → D1 discover + H10 housekeeping-scan
round % 13 == 0  → H20 wikilink-pass --since HEAD
round % 37 == 0  → H17 coverage-scan
round % 37 == 19 → H18 stub-triage

步骤 5 · 候选准备（在领锁之前完成）
──────────────────────────────────
a. 确定本轮动作类型
b. batch_n = max(ceil(1000 / WU), 5)  # 至少 5 页，禁止一轮一页
c. 从队列取候选，用 corpus_search.py 验证（命中 ≥ 2 条）
d. 候选不足时用 discover_by_broken_link.py 补充
e. 准备 batch_n × 1.5 个缓冲池

步骤 6 · 领取轮次锁 + 注册页面
──────────────────────────────────
ROUND=$(python3 wiki/scripts/butler/claim_round.py --instance INSTANCE_NAME)
for SLUG in <全部候选>:
    python3 wiki/scripts/butler/lock_manager.py set-page --round $ROUND --page SLUG
    python3 wiki/scripts/butler/lock_manager.py check-page --page SLUG --round $ROUND

步骤 7 · 执行基因（W2）
──────────────────────────────────
⚠️ 禁止直接 Write/Edit docs/wiki/pages/ 下的词条文件
   必须通过 add_page.py / edit_page.py 脚本
⚠️ 只读参考页（见 local/config.md）不得修改
⚠️ **散文质量强制规范**：所有内容生成必须遵守 `ref/spec/prose-quality.md`
   — 每段 prose 最多 1 个 `——` 且不超过 200 字，违规会被 edit_page.py 硬拦截（退出码 7/8）

for each SLUG:
    执行（动作定义见 `cat "$(bash "$MEMEX_ROOT/wiki/scripts/gene_lookup.sh" <action>)"`）→ W4 评估 → accept/fail/skip
    accept → git add site/wiki/pages/SLUG.md
    consec_fail ≥ 3 → 退出循环
    total_wu ≥ 1000 → 退出循环

步骤 8 · 记账 + 释放锁
──────────────────────────────────
python3 wiki/scripts/butler/record_action.py \
    --round $ROUND --instance INSTANCE_NAME \
    --type <type> --page "<slug列表>" \
    --result accept \
    --desc "<action>×<N>页，<WU>WU" \
    --reflect "<本轮观察>"

# 队列标记：对本轮每个成功页面独立判断
#   该页在 queue.md 中有记录 → complete_task.py --page SLUG --date $(date +%Y-%m-%d)
#   该页来自 discover_by_broken_link（queue.md 中无对应行）→ 跳过 complete_task.py
python3 wiki/scripts/butler/release_round.py $ROUND

步骤 9 → 回到步骤 4（永续）
```

## 关键规则

| # | 规则 | 违反后果 |
|---|------|---------|
| R1 | `claim_round.py` 在步骤 6，所有写操作之前 | 幽灵轮次、计数器漂移 |
| R2 | `release_round.py` 在步骤 8 末尾，即使 fail/skip 也必须释放 | 死锁，阻塞下一轮 |
| R3 | 候选准备在领锁之前全部完成 | 持锁期间搜索，锁时长膨胀 |
| R4 | 页面写入必须通过 add_page.py / edit_page.py | 直接 Write/Edit 触发 hook 双重计数 |
| R5 | 内容必须来自 corpus_search.py 可检索到的段落 | 捏造引文，内容失真 |
| R6 | `claim_round.py` 返回 `RACE` → 立即停止 | 轮次竞争，数据覆盖 |
| R7 | 只读参考页（见 local/config.md）不得修改 | 破坏原始章节参考 |
| R8 | **正确去除 `【】` 括号** — corpus_search.py 结果中的 `【词条】` 是搜索匹配标记，必须用 `词条` 替换 `【词条】`（保留关键词本身），禁止用空字符串替换。两种错误模式必须避免：**模式A（丢字）**：原文"神经网络"、搜索"网络" → 返回"神经【网络】" → 若删除`【网络】`得"神经"（错误！应为"神经网络"）；**模式B（空wikilink）**：原文已有`[[词条]]` → 返回`[[【词条】]]` → 若将`【词条】`替换为`[[词条]]`得`[[[[词条]]]]`（错误！应只去除`【】`保留`[[词条]]`）| 内容错误或渲染破损 |

## WU 成本表

| 行动 | WU | 说明 |
|------|----|------|
| create-page | 100 | 创建完整新页面（≥400字 + ≥2节） |
| create-stub | 40 | 创建 stub 页面（基本信息） |
| enrich-section | 50 | 增加新章节 |
| enrich-quality | 30 | 提升质量档位 |
| fix-links | 10 | 修复断链 |
| wikilink-pass | 50 | 批量添加内部链接 |
| discover | 50 | 发现待建实体 |
| housekeeping-scan | 200 | 全局维护扫描 |

> 自定义基因见 `skills/gene/` 目录。

## 页面质量层级

| 层级 | 标签 | 特征 |
|------|------|------|
| 0 | stub | 只有 frontmatter + 1-2 句话 |
| 1 | basic | ≥100字 + ≥1节 |
| 2 | standard | ≥400字 + ≥2节 + 有 wikilink + 含原文引用 |
| 3 | featured | ≥800字 + ≥3节 + 交叉链接完整 |
| 4 | premium | featured + 多角度解读 + 参考文献 |

> 项目专属的质量门槛覆盖规则见 local/config.md。

## 每轮输出格式

```
[R12] create-page×5 | <slug1>/<slug2>/… | accept×5 fail×0 | 500WU

[R17] /wiki-publish + D1-discover + H10-scan | — | accept | commit abc1234
[R29] W5-reflect | — | — | 模式：人物页质量偏低；建议加强 enrich
```

## 暂停条件

| 条件 | 默认（无 --auto）| `--auto` 模式 |
|------|----------------|--------------|
| 用户说"停止"/"pause" | 立即停止 | 立即停止 |
| 连续 5 轮 fail | 停止 | 继续（fail 也跑下轮）|
| 上下文将满（剩 ~10k token）| 停止 | ScheduleWakeup 续跑 |
| claim_round 返回 RACE/DUPLICATE | 停止 | 停止（不 schedule）|
| 致命错误（脚本崩溃 / git 不可用）| 停止 | 停止（不 schedule）|

## 可用工具

| 工具 | 用法 |
|------|------|
| `corpus_search.py` | `python3 wiki/scripts/butler/corpus_search.py "关键词" --max 15` |
| `claim_round.py` | `ROUND=$(python3 wiki/scripts/butler/claim_round.py --instance NAME)` |
| `release_round.py` | `python3 wiki/scripts/butler/release_round.py $ROUND` |
| `lock_manager.py` | set-page / check-page / status / cleanup |
| `discover_by_broken_link.py` | `python3 wiki/scripts/butler/discover_by_broken_link.py --top 30` |
| `record_action.py` | `--round` 必须是整数 |
| `complete_task.py` | `--page SLUG --date YYYY-MM-DD` |
| `add_page.py` | 新建页面（通过脚本，禁止直接 Write） |
| `edit_page.py` | 编辑页面（保护已有节，Append-Only） |
| `bulk_wikilink.py` | 全量/增量添加 wikilink |

## 详细规范参考

W0–W9 总则路径见 `skills/butler/local/config.md`。

