# 考研数学辅导系统 v1 Implementation Plan

> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.

**Goal:** 落地 DESIGN.md v1 范围 — TG → Hermes router → Mac Claude Code 全链路打通，错题自动归档、知识点反向引用、`#改` hashtag 触发的自我迭代流。

**Architecture:** 见 `DESIGN.md`。三层：TG = 接口；HK Hermes = router（GLM-5.1，仅做转发，不讲题）；Mac Claude Code = 大脑（识别+讲+归档）。数据 source of truth 在 Mac，HK 是 git mirror，`share.rusti.cc/math/` 网页脱机查阅。

**Tech Stack:** Hermes Agent (skill markdown), Claude Code CLI v2.x (`claude -p`), Tailscale SSH（HK→Mac 无公钥反向 ssh）, git (Mac→HK bare repo + post-receive checkout), bash 脚本胶水。

**Privacy note:** 待处理原图存 HK `/opt/math-inbox/`（仅 ssh 访问，不公网暴露）。`share.rusti.cc/math/` 只暴露处理完的 `mistakes/` `kp/` `dashboard.md` 等。

---

## File Structure

**Mac side `~/MyProjects/compass/math/`** (git repo, source of truth):

| Path | Responsibility |
|------|---------------|
| `DESIGN.md` | 一次性架构设计（已存在） |
| `PLAN.md` | 本文件（一次性）|
| `WORKFLOW.md` | **活的**流程指挥棒；Hermes skill 和 Mac Claude Code 启动时都读 |
| `CHANGELOG.md` | 流程演化日志（每次 `#改` 反馈追加一条）|
| `README.md` | `share.rusti.cc/math/` 着陆页 |
| `.gitignore` | 排除 `inbox/*` 和临时文件 |
| `mistakes/YYYY/MM/MMDD-NNN.{md,jpg}` | 错题本（一题一文件）|
| `kp/INDEX.md` | 数二全考纲骨架（链接到细化节点）|
| `kp/<科目>/<章>/<节>.md` | 知识点 taxonomy（按需创建）|
| `hermes-skills/learning/math-tutor/SKILL.md` | Hermes 端 math-tutor skill 的 Mac mirror（push 后 sync 到 HK）|
| `scripts/sync-skills-to-hk.sh` | Mac → HK skill scp + restart 服务 |
| `scripts/pull-inbox-from-hk.sh` | Mac 从 HK 拉新 inbox 图 |
| `scripts/process-inbox.sh` | Mac 上 batch 处理 inbox 的入口（Claude Code 内部调用）|

**HK side `149.104.29.83`**:

| Path | Responsibility |
|------|---------------|
| `/opt/git/math.git/` | bare git repo（Mac push 目标）|
| `/opt/git/math.git/hooks/post-receive` | hook：push 后自动 checkout 到 `/opt/share/math/` |
| `/opt/share/math/` | working tree（`share.rusti.cc/math/` autoindex 暴露）|
| `/opt/math-inbox/` | 待处理原图（**不公网暴露**，仅 ssh）|
| `/root/.hermes/profiles/telegram_bot/skills/autonomous-ai-agents/claude-code/` | mirror from default profile（让 TG bot 能调 Claude Code）|
| `/root/.hermes/profiles/telegram_bot/skills/learning/math-tutor/` | 新写：数学辅导工作流定义 |

---

## Tasks

### Task 0: Prerequisites — HK→Mac SSH 通路 + claude-remote wrapper ✅

**已完成 2026-05-28。** 选了 OpenSSH 路径（非 Tailscale SSH，因 Mac 上的 Tailscale 是 App Store sandboxed 版本不支持 SSH server）。走 Tailscale 网络 + 公钥认证，不暴露公网 22 端口。

**已落地的事实（HK 上跑 `ssh natexu@100.119.8.14 ...` 即可）：**

- Mac OpenSSH Remote Login: ON（已启用）
- HK root 公钥（`ssh-ed25519 ... root@7m1aoesyt1o2dq7`）已加进 Mac `~natexu/.ssh/authorized_keys`
- Mac 用户：`natexu`，hostname：`Nate.local`，Tailscale IP：`100.119.8.14`
- Claude 路径：`/Users/natexu/.local/bin/claude` v2.1.143（Max 账号 OAuth）

**新增的 wrapper：`/Users/natexu/.local/bin/claude-remote`**

非交互 ssh shell 拿不到 macOS Keychain 里的 Claude OAuth token，必须先 unlock。wrapper 内容：

```bash
#!/bin/bash
PWFILE="$HOME/.config/keychain-pwd"
KC="$HOME/Library/Keychains/login.keychain-db"
if [ -r "$PWFILE" ]; then
  security unlock-keychain -p "$(cat "$PWFILE")" "$KC" 2>/dev/null
fi
exec "$HOME/.local/bin/claude" "$@"
```

密码文件 `~/.config/keychain-pwd`（600，内容 = macOS login password = sudo 密码）。**该文件不入 git。** wrapper 本身可以入 git（无密码）。

**所有远程调用 claude 必须通过 wrapper + 加 flag：**

```bash
ssh natexu@100.119.8.14 \
  '/Users/natexu/.local/bin/claude-remote -p "..." --dangerously-skip-permissions \
   --allowedTools "Read,Edit,Write,Bash" --max-turns 30 < /dev/null'
```

`< /dev/null` 避免 "no stdin data received in 3s" warning。

**已验证：**

```bash
ssh hk 'ssh natexu@100.119.8.14 "/Users/natexu/.local/bin/claude-remote -p \"回复仅一个字符 X\" --dangerously-skip-permissions"'
# → X
```

**6/15 后注意：** 用户提示 `-p` print mode 6/15 后可能不再走 Max 套餐而走 API 计费。届时切到 stdin pipe (`echo prompt | claude-remote --dangerously-skip-permissions`) 或其他方案。改 wrapper 即可，SKILL.md 不用变。

---

### Task 1: Mac 项目骨架（WORKFLOW.md + 目录 + .gitignore）

**Files:**
- Create: `~/MyProjects/compass/math/WORKFLOW.md`
- Create: `~/MyProjects/compass/math/CHANGELOG.md`
- Create: `~/MyProjects/compass/math/README.md`
- Create: `~/MyProjects/compass/math/.gitignore`
- Create: `~/MyProjects/compass/math/{mistakes,inbox,kp,hermes-skills/learning/math-tutor,scripts}/.gitkeep`
- Create: `~/MyProjects/compass/math/kp/INDEX.md`

- [ ] **Step 1: 写 `WORKFLOW.md`**（活文档，Hermes skill 和 Mac Claude Code 启动时都会读）

```markdown
# WORKFLOW v1.0

最后更新: 2026-05-28
本文件是流程 single source of truth。任何流程改动都更新本文件并追加到 [[CHANGELOG.md]]。

---

## 角色分工

| 角色 | 干啥 | 不干啥 |
|------|------|--------|
| TG @nate_hermes_001_bot | 接口：用户拍题、回 push 通知 | 不识别题、不讲解、不归档 |
| HK Hermes (GLM-5.1) | router：收图存 inbox、ssh Mac 调 Claude、转发回复 | **不讲题**（智力降级红线）|
| Mac Claude Code | 大脑：识别 + 讲 + 归档 + 知识点串联 + 自我迭代 | 不做 SRS（v1.1）、不做 dashboard（v1.2）|

## 主循环：拍题 → 讲 → 归档

### Hermes 端（math-tutor skill 触发）

1. 用户在 TG 发图（可能附文字"哪步开始懵"）
2. Hermes 把图存到 `/opt/math-inbox/<YYYY-MM-DD-HHMMSS>.jpg`，文字（若有）存同名 `.txt`
3. Hermes ssh Mac：
   ```
   ssh natexu@100.119.8.14 \
     "cd ~/MyProjects/compass/math && \
      /Users/natexu/.local/bin/claude-remote -p '处理 inbox 新题，按 WORKFLOW.md 走主循环' \
      --dangerously-skip-permissions \
      --allowedTools 'Read,Edit,Write,Bash' --max-turns 30 < /dev/null"
   ```
4. 拿 stdout 转回 TG
5. Mac offline（ssh 超时 10s）→ TG 回："Claude 不在，已存第 N 题在 inbox。在线后会主动通知。"

### Mac Claude Code 端（被 ssh 触发）

1. `pull-inbox-from-hk.sh` 拉新文件到 `inbox/`
2. 对每张图：
   - Read 工具读图 → OCR 题面
   - 不清楚则输出 `ASK: <反问>`（Hermes 转给 TG，等下次循环）
   - 读对应 .txt 用户描述 → 定位错因（概念/思路/计算/表达）
   - 生成下一个序号 `MMDD-NNN`（当天起 001 递增）
   - 写 `mistakes/YYYY/MM/MMDD-NNN.md`（schema 见 DESIGN.md）
   - 把 inbox 图 mv 到 `mistakes/YYYY/MM/MMDD-NNN.jpg`
   - 创建/更新 `kp/<科目>/<章>/<节>.md`（不存在则按 INDEX.md 路径建）
   - 追加反向引用到知识点页"错题反向引用"段
   - grep frontmatter 找历史相似题（同 kp 或同 errors）→ 输出"相关错题 N 道：[link]"
3. 全部处理完 → `git add . && git commit -m "math: 归档 N 道 (MMDD-NNN..NNN)"`
4. `git push hk main`
5. ssh hk 删除已处理的 inbox 原件
6. 输出讲解汇总到 stdout（Hermes 拿这个转回 TG）

## 自我迭代循环（`#改` hashtag）

### Hermes 端

1. 用户 TG 发的消息以 `#改` 开头 → 不走主循环，走反馈流
2. ssh Mac：
   ```
   ssh natexu@100.119.8.14 \
     "cd ~/MyProjects/compass/math && \
      /Users/natexu/.local/bin/claude-remote -p '收到反馈：\"<原文>\"。按 WORKFLOW.md 自我迭代循环处理' \
      --dangerously-skip-permissions \
      --allowedTools 'Read,Edit,Write,Bash' --max-turns 20 < /dev/null"
   ```
3. 拿到 diff → 转回 TG

### Mac Claude Code 端

1. 解析反馈，定位要改 `WORKFLOW.md` 还是 `hermes-skills/learning/math-tutor/SKILL.md`
2. Edit 对应文件
3. 追加一条到 `CHANGELOG.md`：
   ```
   ## YYYY-MM-DD
   - **反馈**: <原文>
   - **改动**: <diff 摘要>
   - **影响**: WORKFLOW.md / SKILL.md
   ```
4. `git commit -m "workflow: <一句话改动>"`
5. `git push hk main`
6. 若改的是 `hermes-skills/`：跑 `scripts/sync-skills-to-hk.sh`（scp 到 HK + restart hermes-gateway-telegram_bot）
7. 输出 `git diff HEAD~1` 到 stdout

## 错因 4 分类

| 标签 | 触发条件 | 治法 |
|------|---------|------|
| 概念错 | 用户描述"不知道这是干啥"/"没学过" | 补讲概念，链接到知识点页 |
| 思路错 | 用户描述"看错了/想偏了/没想到要用 X 方法" | 拓展同类题、对比正反例 |
| 计算错 | 用户描述"知道思路但中间算错了" | 列出常错步骤，建议计算专项 |
| 表达错 | 用户描述"我做对了但答案规范不符" | 给标准答题模板 |

## 错题文件命名约定

- 当天序号：`MMDD-NNN`，NNN 从 001 起，按当天处理顺序
- 目录：按月分 `mistakes/YYYY/MM/`
- 原图与 md 同名：`0528-001.md` + `0528-001.jpg`

## 知识点 taxonomy 规则

- 按需创建，不预建空壳
- 路径来自 `kp/INDEX.md`，如 `高数/不定积分/换元积分法/三角代换`
- 每个知识点页有反向引用段，错题归档时自动 append
- 知识点 4 段：`核心要点 / 常见考点 / 易混点 / 错题反向引用`

## Mac 离线兜底

- Hermes ssh 超时 10s → 视为 Mac offline
- TG 回固定话术，inbox 文件保留
- Mac 下次执行任何 claude 命令时，shell 启动 hook 检查 inbox，发现 N>0 文件就提示"有 N 道待处理，要现在过一遍吗？"

## 不在 v1 范围（明确不做）

- SRS 提醒、进度看板、Hindsight 索引、定期复盘 subagent
- 整卷模式、讲解档位、学习计划
- 公开题库爬取
```

- [ ] **Step 2: 写 `CHANGELOG.md`**

```markdown
# Changelog

## 2026-05-28

- **v1.0** 系统首次落地。架构见 DESIGN.md，流程见 WORKFLOW.md。
```

- [ ] **Step 3: 写 `README.md`**（share.rusti.cc/math/ 着陆页）

```markdown
# 考研数学错题本

自动同步自 `~/MyProjects/compass/math/`。

## 浏览

- [错题本](mistakes/) — 按年月归档
- [知识点](kp/) — 数二考纲 + 反向引用
- [DESIGN.md](DESIGN.md) — 架构
- [WORKFLOW.md](WORKFLOW.md) — 当前流程
- [CHANGELOG.md](CHANGELOG.md) — 流程演化日志

## 不要在这里编辑

source of truth 在 Mac。这里是 git checkout 的只读快照。
```

- [ ] **Step 4: 写 `.gitignore`**

```
# 待处理原图：Mac 本地 working space，处理完就 mv 走，不入 git
inbox/*
!inbox/.gitkeep

# OS
.DS_Store
Thumbs.db

# Editor
*.swp
.vscode/
.idea/
```

- [ ] **Step 5: 建子目录 + .gitkeep**

```bash
cd ~/MyProjects/compass/math
mkdir -p mistakes inbox kp hermes-skills/learning/math-tutor scripts
touch mistakes/.gitkeep inbox/.gitkeep kp/.gitkeep \
      hermes-skills/learning/math-tutor/.gitkeep scripts/.gitkeep
```

- [ ] **Step 6: 写 `kp/INDEX.md`**（数二考纲骨架；不预建子文件，只列路径）

```markdown
# 数二知识点 INDEX

每个叶节点对应一个 `kp/<path>.md` 文件，按需创建（第一次有错题挂上来时建）。
未建的节点点击会 404，是正常的。

## 高等数学

### 函数极限连续
- 高数/函数与极限/数列极限.md
- 高数/函数与极限/函数极限.md
- 高数/函数与极限/无穷小与无穷大.md
- 高数/函数与极限/连续与间断.md

### 一元函数微分学
- 高数/导数与微分/导数定义.md
- 高数/导数与微分/求导法则.md
- 高数/导数与微分/高阶导数.md
- 高数/中值定理/罗尔/拉格朗日/柯西.md
- 高数/中值定理/泰勒公式.md
- 高数/导数应用/单调性极值.md
- 高数/导数应用/凹凸性拐点.md
- 高数/导数应用/曲率.md

### 一元函数积分学
- 高数/不定积分/换元积分法.md
- 高数/不定积分/分部积分法.md
- 高数/不定积分/有理函数积分.md
- 高数/定积分/定义与性质.md
- 高数/定积分/换元法.md
- 高数/定积分/分部积分.md
- 高数/定积分/反常积分.md
- 高数/定积分应用/几何应用.md
- 高数/定积分应用/物理应用.md

### 多元函数微分学
- 高数/多元微分/偏导数.md
- 高数/多元微分/全微分.md
- 高数/多元微分/极值与最值.md
- 高数/多元微分/拉格朗日乘数.md

### 二重积分
- 高数/二重积分/直角坐标.md
- 高数/二重积分/极坐标.md
- 高数/二重积分/对称性.md

### 微分方程
- 高数/微分方程/一阶可分离变量.md
- 高数/微分方程/一阶线性.md
- 高数/微分方程/伯努利方程.md
- 高数/微分方程/二阶常系数齐次.md
- 高数/微分方程/二阶常系数非齐次.md

## 线性代数

### 行列式
- 线代/行列式/性质.md
- 线代/行列式/计算技巧.md

### 矩阵
- 线代/矩阵/运算.md
- 线代/矩阵/逆矩阵.md
- 线代/矩阵/初等变换与初等矩阵.md
- 线代/矩阵/秩.md

### 向量
- 线代/向量/线性相关无关.md
- 线代/向量/极大无关组.md
- 线代/向量/向量组的秩.md

### 线性方程组
- 线代/方程组/解的判定.md
- 线代/方程组/解的结构.md

### 特征值与特征向量
- 线代/特征值/特征值特征向量.md
- 线代/特征值/相似对角化.md
- 线代/特征值/实对称矩阵对角化.md

### 二次型
- 线代/二次型/标准化.md
- 线代/二次型/正定性.md
```

- [ ] **Step 7: commit**

```bash
cd ~/MyProjects/compass/math
git add WORKFLOW.md CHANGELOG.md README.md .gitignore \
        mistakes/.gitkeep inbox/.gitkeep kp/.gitkeep \
        hermes-skills/learning/math-tutor/.gitkeep \
        scripts/.gitkeep kp/INDEX.md
git commit -m "scaffold: WORKFLOW + 目录骨架 + 数二考纲 INDEX

WORKFLOW.md 是流程指挥棒（Hermes skill 和 Mac Claude Code 共享）。
.gitignore 排除 inbox/* — 待处理图是 Mac 本地 working space。
kp/INDEX.md 只列考纲路径，叶节点按需创建。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>"
```

---

### Task 2: HK 端 — bare repo + post-receive hook

**Files:**
- HK Create: `/opt/git/math.git/` (bare repo)
- HK Create: `/opt/git/math.git/hooks/post-receive`
- HK Create: `/opt/share/math/` (working tree, 空目录)
- HK Create: `/opt/math-inbox/` (privacy-scoped, 0750)

- [ ] **Step 1: 在 HK 创建 bare repo + working tree + inbox**

```bash
ssh hk 'set -e
git init --bare /opt/git/math.git
mkdir -p /opt/share/math /opt/math-inbox
chmod 0755 /opt/share/math
chmod 0750 /opt/math-inbox
ls -la /opt/git/math.git/ /opt/share/math/ /opt/math-inbox/'
```
Expected: bare repo 目录结构（HEAD, hooks/, objects/...），share/math 和 math-inbox 是空目录。

- [ ] **Step 2: 写 post-receive hook**

```bash
ssh hk 'cat > /opt/git/math.git/hooks/post-receive <<'"'"'EOF'"'"'
#!/bin/bash
# 接到 push 后自动 checkout 到 /opt/share/math/（working tree）
set -e
TARGET=/opt/share/math
GIT_DIR=/opt/git/math.git

# 只在 push 到 main 时触发
while read oldrev newrev refname; do
  if [ "$refname" = "refs/heads/main" ]; then
    GIT_WORK_TREE=$TARGET git --git-dir=$GIT_DIR checkout -f main
    echo "[post-receive] checked out main to $TARGET"
  fi
done
EOF
chmod +x /opt/git/math.git/hooks/post-receive
ls -la /opt/git/math.git/hooks/post-receive'
```
Expected: hook 文件存在，权限 `-rwxr-xr-x`。

- [ ] **Step 3: Mac 加 git remote**

```bash
cd ~/MyProjects/compass/math
git remote add hk hk:/opt/git/math.git
git remote -v
```
Expected: 看到 `hk` 远端的 fetch/push 行。

- [ ] **Step 4: push + 验证 checkout**

```bash
cd ~/MyProjects/compass/math
git push -u hk main
ssh hk 'ls -la /opt/share/math/ && cat /opt/share/math/WORKFLOW.md | head -5'
```
Expected: push 成功；HK 上 `/opt/share/math/` 出现所有文件（DESIGN.md, PLAN.md, WORKFLOW.md, ...）；hook 输出 "checked out main"。

- [ ] **Step 5: 验证 share.rusti.cc 能浏览**

```bash
env -u HTTPS_PROXY -u HTTP_PROXY curl -sI https://share.rusti.cc/math/ | head -5
```
Expected: HTTP/2 200 或 301 redirect。在浏览器打开 https://share.rusti.cc/math/ 应见目录列表。

---

### Task 3: Hermes — mirror `claude-code` skill 到 telegram_bot profile

**Why:** telegram_bot profile 当前没有这个 skill；mirror 是 startup-cache，必须 restart 服务才能加载。

**Files:**
- HK Copy: `/root/.hermes/skills/autonomous-ai-agents/claude-code/` → `/root/.hermes/profiles/telegram_bot/skills/autonomous-ai-agents/claude-code/`

- [ ] **Step 1: 复制 skill 目录**

```bash
ssh hk 'set -e
mkdir -p /root/.hermes/profiles/telegram_bot/skills/autonomous-ai-agents
cp -r /root/.hermes/skills/autonomous-ai-agents/claude-code \
      /root/.hermes/profiles/telegram_bot/skills/autonomous-ai-agents/
ls /root/.hermes/profiles/telegram_bot/skills/autonomous-ai-agents/claude-code/'
```
Expected: 看到 `SKILL.md` + `references/` 目录。

- [ ] **Step 2: restart 服务并验证 skill 加载**

```bash
ssh hk 'systemctl --user restart hermes-gateway-telegram_bot && \
        sleep 3 && \
        systemctl --user status hermes-gateway-telegram_bot --no-pager | head -10'
```
Expected: service active (running)，无 error 日志。

- [ ] **Step 3: TG 端验证**

发一条 TG 消息给 `@nate_hermes_001_bot`：

```
@Hermes 你现在有 claude-code skill 吗？列出你能调的 skill 里包含 "claude" 的
```

Expected: bot 回复列表里含 `autonomous-ai-agents/claude-code`。

---

### Task 4: 写 `learning/math-tutor` skill（Mac → HK）

**Files:**
- Mac Create: `~/MyProjects/compass/math/hermes-skills/learning/math-tutor/SKILL.md`
- HK Create (via sync): `/root/.hermes/profiles/telegram_bot/skills/learning/math-tutor/SKILL.md`

- [ ] **Step 1: 在 Mac 写 SKILL.md**

```bash
mkdir -p ~/MyProjects/compass/math/hermes-skills/learning/math-tutor
```

文件内容（写到 `~/MyProjects/compass/math/hermes-skills/learning/math-tutor/SKILL.md`）：

```markdown
---
name: math-tutor
description: "考研数学辅导：TG 拍题 → 调 Mac Claude Code 识别讲解归档。也处理 #改 hashtag 反馈触发的自我迭代。仅在 telegram_bot profile 启用。"
version: 1.0.0
author: Hermes + Mac Claude Code
license: MIT
metadata:
  hermes:
    tags: [Learning, Math, Tutoring, Telegram, Claude-Code]
    related_skills: [autonomous-ai-agents/claude-code]
---

# Math Tutor — 考研数学辅导工作流

## 触发条件

只在 telegram_bot profile 用。两种触发：

1. **拍题流**：用户在 TG 发**图片**（jpg/png），可能附文字"哪步开始懵 / 我哪里不会"
2. **反馈流**：用户在 TG 发**以 `#改` 开头的纯文本**（如 `#改 以后讲完先标知识点再写讲解`）

## 不要做的事

- ❌ **绝对不要自己讲题** — GLM 智力降级会污染错题本，所有讲解走 Mac Claude Code
- ❌ 不要 OCR 题面 — Claude Code 自己读图
- ❌ 不要修改 `mistakes/` `kp/` 任何文件 — 只有 Mac Claude Code 改
- ❌ 不要触发其他流程（SRS、dashboard）— v1 不在范围

## 拍题流

### Step 1：收图存 HK inbox

```
terminal(command="
  TS=$(date +%Y-%m-%d-%H%M%S)
  cp $TELEGRAM_LAST_IMAGE /opt/math-inbox/${TS}.jpg
  [ -n \"$USER_TEXT\" ] && echo \"$USER_TEXT\" > /opt/math-inbox/${TS}.txt
  ls -la /opt/math-inbox/${TS}*
", timeout=10)
```

（`$TELEGRAM_LAST_IMAGE` 是 Hermes 把 TG 收到的图落到本地的临时路径，由 hermes 自身机制提供。如不存在该变量，用 `attachments` 工具获取图片路径。）

### Step 2: 检查 Mac 是否在线（10 秒超时）

```
terminal(command="
  timeout 10 ssh -o StrictHostKeyChecking=accept-new -o ConnectTimeout=5 \
    natexu@100.119.8.14 'echo ok' 2>&1
", timeout=15)
```

如果返回非 `ok` 或 timeout → 跳到 **Mac 离线兜底**。

### Step 3：调 Mac Claude Code 处理 inbox

调 `autonomous-ai-agents/claude-code` skill：

```
terminal(command="
  ssh natexu@100.119.8.14 '
    cd ~/MyProjects/compass/math &&
    /Users/natexu/.local/bin/claude-remote -p \"处理 inbox/${TS}.jpg 这道题。按 WORKFLOW.md 主循环走：OCR、定位错因、写 mistakes/、更新 kp/、找相似题、commit、push。最后输出讲解汇总（含相似题链接）。\" \
      --dangerously-skip-permissions \
      --allowedTools \"Read,Edit,Write,Bash\" \
      --max-turns 30 < /dev/null
  '
", timeout=600)
```

拿到 stdout 后**原文**转回 TG（不做二次包装、不做总结）。

如果 stdout 以 `ASK:` 开头（Claude 反问澄清），把 `ASK:` 后的内容作为提问发给 TG，等待用户下次消息后再调一次（提示中说明"上次反问的回答是：<用户答>"）。

### Mac 离线兜底

```
回复 TG：
"Claude 不在线（Mac 可能休眠或断网），已经把这道题存到 inbox 第 N 道。
Mac 在线后会主动通知你处理。"
```

（N = `ssh hk "ls /opt/math-inbox/*.jpg | wc -l"` 的结果；如果 ssh 也不通，省略 N。）

## 反馈流（`#改` hashtag）

### Step 1：识别

如果用户消息以 `#改` 开头（允许 `#改 `、`#改：`、`#改:` 三种），剩余文本就是反馈内容。

### Step 2：调 Mac Claude Code 处理反馈

```
terminal(command="
  ssh natexu@100.119.8.14 '
    cd ~/MyProjects/compass/math &&
    /Users/natexu/.local/bin/claude-remote -p \"收到反馈：「${FEEDBACK_TEXT}」。按 WORKFLOW.md 自我迭代循环处理：定位要改 WORKFLOW.md 还是 SKILL.md，edit、append CHANGELOG.md、commit、push、若改了 hermes-skills/ 则 sync。最后输出 git diff HEAD~1。\" \
      --dangerously-skip-permissions \
      --allowedTools \"Read,Edit,Write,Bash\" \
      --max-turns 20 < /dev/null
  '
", timeout=300)
```

拿到 stdout 后转回 TG，加一行前缀："已按反馈改动，diff：" + 原 stdout。

### Step 3（仅当 Mac 改了 hermes-skills/）：等 sync 完成

Mac 端 `sync-skills-to-hk.sh` 会在 push 后自动 scp 新版 SKILL.md 到 HK 并 `systemctl --user restart hermes-gateway-telegram_bot`。重启后本 skill 用新版重新加载。

不需要 Hermes 主动做什么 — Mac 全包。

## 错误处理

- ssh 失败（非超时）→ TG 回："ssh 报错：<stderr>"，让用户排查
- Claude Code 返回非零退出码 → TG 回："Claude 处理出错：<stderr 最后 20 行>"
- inbox 写入失败（HK 磁盘满等）→ TG 回："HK 存不下了，先别拍了，查磁盘"

## 不要兼容

按 WORKFLOW.md 原则 4："狠改不留兼容"。本 skill 任何改动**不必**考虑向后兼容；用户发反馈 `#改` 即刻演化。
```

- [ ] **Step 2: 写 `scripts/sync-skills-to-hk.sh`**

```bash
cat > ~/MyProjects/compass/math/scripts/sync-skills-to-hk.sh <<'EOF'
#!/bin/bash
# 只 sync learning/math-tutor 子目录，--delete 范围限于此子目录，
# 不会动 HK telegram_bot/skills/ 下其他 23 个 skill。
set -e

LOCAL=~/MyProjects/compass/math/hermes-skills/learning/math-tutor
REMOTE=/root/.hermes/profiles/telegram_bot/skills/learning/math-tutor

echo "[sync] ensure remote dir: $REMOTE"
ssh hk "mkdir -p $REMOTE"

echo "[sync] rsync $LOCAL/ → hk:$REMOTE/"
rsync -avz --delete "$LOCAL/" "hk:$REMOTE/"

echo "[sync] restart hermes-gateway-telegram_bot"
ssh hk 'systemctl --user restart hermes-gateway-telegram_bot && sleep 2 && systemctl --user is-active hermes-gateway-telegram_bot'

echo "[sync] done"
EOF
chmod +x ~/MyProjects/compass/math/scripts/sync-skills-to-hk.sh
```

- [ ] **Step 3: 写 `scripts/pull-inbox-from-hk.sh`**

```bash
cat > ~/MyProjects/compass/math/scripts/pull-inbox-from-hk.sh <<'EOF'
#!/bin/bash
# pull new inbox items from HK to Mac local inbox/
set -e

LOCAL=~/MyProjects/compass/math/inbox
REMOTE=hk:/opt/math-inbox

mkdir -p "$LOCAL"
echo "[pull] rsync $REMOTE/ → $LOCAL/"
rsync -avz --ignore-existing "$REMOTE/" "$LOCAL/"

# 列出新拉到的
COUNT=$(find "$LOCAL" -type f \( -name "*.jpg" -o -name "*.png" \) | wc -l | tr -d ' ')
echo "[pull] inbox now has $COUNT image(s)"
EOF
chmod +x ~/MyProjects/compass/math/scripts/pull-inbox-from-hk.sh
```

- [ ] **Step 4: 写 `scripts/process-inbox.sh`**（被 claude -p 内部调用的 batch 入口）

```bash
cat > ~/MyProjects/compass/math/scripts/process-inbox.sh <<'EOF'
#!/bin/bash
# 列出 inbox 待处理项，Mac 上 Claude Code 用 Read 工具按列表逐个读
set -e

INBOX=~/MyProjects/compass/math/inbox
cd "$INBOX"
find . -maxdepth 1 -type f \( -name "*.jpg" -o -name "*.png" \) | sort
EOF
chmod +x ~/MyProjects/compass/math/scripts/process-inbox.sh
```

- [ ] **Step 5: commit Mac 端**

```bash
cd ~/MyProjects/compass/math
git add hermes-skills/learning/math-tutor/SKILL.md scripts/
git commit -m "skill: 写 math-tutor + Mac sync 脚本

math-tutor SKILL.md 定义 Hermes 端两条流（拍题/反馈），关键约束：
GLM 不讲题、不改文件，所有动作走 Mac Claude Code via ssh。

scripts/ 三个胶水：
- sync-skills-to-hk.sh: 改完 hermes-skills/ 后推 HK + restart
- pull-inbox-from-hk.sh: Mac 处理前拉新图
- process-inbox.sh: 列 inbox 待处理（被 claude -p 内部调）

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>"
```

- [ ] **Step 6: push + 触发首次 sync**

```bash
cd ~/MyProjects/compass/math
git push hk main
bash scripts/sync-skills-to-hk.sh
```
Expected:
- push 成功，post-receive hook checkout 到 /opt/share/math/
- rsync 推 SKILL.md 到 HK profile skills 目录
- service restart 成功，`is-active` 返回 `active`

- [ ] **Step 7: 验证 HK 端 skill 加载**

```bash
ssh hk 'ls /root/.hermes/profiles/telegram_bot/skills/learning/math-tutor/'
ssh hk 'grep "^version:" /root/.hermes/profiles/telegram_bot/skills/learning/math-tutor/SKILL.md'
```
Expected: SKILL.md 存在；version 行显示 `version: 1.0.0`。

---

### Task 5: 端到端 smoke test — 拍题流

**Why:** 整套系统是分布式的，单元测试覆盖不了"TG → HK → Mac → 回 TG"这条链。必须端到端跑一次。

**Setup:** 准备一道**真实数二题**（最好是用户当前在做的某道）的照片，拍好放手机相册。

- [ ] **Step 1: TG 端发图**

打开 TG 找 `@nate_hermes_001_bot`，发一张题图，附文字（任选一种）：
- "这题第二步开始懵"
- "答案对了但我不知道为什么"
- "完全没思路"

- [ ] **Step 2: 观察 HK inbox 落盘**

```bash
ssh hk 'ls -la /opt/math-inbox/ | tail -5'
```
Expected: 看到刚才的 jpg + 同名 txt（描述）。

- [ ] **Step 3: 观察 Hermes 是否调起 Mac Claude Code**

```bash
ssh hk 'journalctl --user -u hermes-gateway-telegram_bot --since "2 minutes ago" | grep -iE "ssh|claude|math-tutor" | tail -30'
```
Expected: 看到 ssh natexu@100.119.8.14 的命令、claude -p 的调用日志。

- [ ] **Step 4: Mac 端验证文件生成**

```bash
cd ~/MyProjects/compass/math
git log --oneline -5
ls mistakes/$(date +%Y)/$(date +%m)/
```
Expected: 看到新 commit `math: 归档 1 道 (MMDD-001)`，mistakes 目录有 `MMDD-001.md` + `MMDD-001.jpg`。

- [ ] **Step 5: 检查错题文件 schema**

```bash
cat ~/MyProjects/compass/math/mistakes/$(date +%Y)/$(date +%m)/$(date +%m%d)-001.md
```
Expected: frontmatter 完整（id/date/kp/errors/source/related/image），body 有题面/做法/错在哪/正确思路/易混点 5 段。

- [ ] **Step 6: 检查知识点反向引用**

```bash
# 看 frontmatter 里的 kp 值，找对应 .md，看反向引用段
KP_PATH=$(grep -m1 "^kp:" ~/MyProjects/compass/math/mistakes/$(date +%Y)/$(date +%m)/$(date +%m%d)-001.md | sed 's/kp: \[//;s/\]//')
cat "~/MyProjects/compass/math/kp/${KP_PATH}.md" 2>/dev/null | tail -20
```
Expected: 知识点页底部"错题反向引用"段含 `MMDD-001` 链接。

- [ ] **Step 7: 验证 push 到 HK + share.rusti.cc 更新**

```bash
ssh hk 'ls /opt/share/math/mistakes/'
env -u HTTPS_PROXY -u HTTP_PROXY curl -s https://share.rusti.cc/math/mistakes/ | grep -o '20[0-9][0-9]' | head -3
```
Expected: HK 上 mistakes/ 有新年月目录；网页能浏览到。

- [ ] **Step 8: 验证 HK inbox 已清空**

```bash
ssh hk 'ls /opt/math-inbox/'
```
Expected: 空（Mac 处理完应该 ssh hk rm 了原件；如果没清，是 WORKFLOW.md 主循环 step 5 漏了，回去补）。

- [ ] **Step 9: 验证 TG 收到讲解**

回 TG 看 bot 是否回了讲解（含"相关错题 N 道"提示）。

如果任一步失败 → 不进入 Task 6，先 debug 修复（用 systematic-debugging skill），修完重跑这一步。

---

### Task 6: 端到端 smoke test — `#改` hashtag 反馈流

- [ ] **Step 1: TG 端发反馈**

发：
```
#改 测试反馈：错题文件 frontmatter 加一个 difficulty 字段（值 easy/mid/hard）
```

- [ ] **Step 2: 观察 Mac 是否被触发**

```bash
ssh hk 'journalctl --user -u hermes-gateway-telegram_bot --since "1 minute ago" | grep -iE "ssh|claude.*feedback|#改" | tail -10'
```
Expected: 看到 ssh natexu@100.119.8.14 调 claude，prompt 含"收到反馈"。

- [ ] **Step 3: 验证 WORKFLOW.md 改了 + CHANGELOG.md 追加**

```bash
cd ~/MyProjects/compass/math
git log --oneline -3
git diff HEAD~1 -- WORKFLOW.md CHANGELOG.md
```
Expected: 看到新 commit `workflow: 错题 frontmatter 加 difficulty 字段`，diff 中 WORKFLOW.md schema 段加了 difficulty 字段，CHANGELOG.md 追加一条 2026-05-28 反馈记录。

- [ ] **Step 4: 验证 TG 收到 diff 反馈**

回 TG 看 bot 是否回了 "已按反馈改动，diff：" + git diff 内容。

- [ ] **Step 5: 回滚测试反馈**（不要让 difficulty 真的进入 schema，因为我们没设计这个字段）

```
#改 撤销刚才那个 difficulty 字段，本来只是测试
```

验证：bot 回应、git 又有一个 commit 把 difficulty 字段移除。

如果撤销也成功 → 自我迭代闭环验证通过。

---

### Task 7: 写 Mac 离线启动检查（最小版）

**Why:** WORKFLOW.md 写了"Mac 下次启动 claude 时检查 inbox 提示"，但需要落到具体机制。最简单：写个 shell function 加到 Mac 的 zshrc，每次进 math 目录提示。

**Files:**
- Modify: `~/.zshrc`（用户验证后再加，需要用户同意）

- [ ] **Step 1: 写 hook 函数到 math 项目的 README 里供用户复制（不擅自改 .zshrc）**

把以下加到 `~/MyProjects/compass/math/README.md`：

```markdown
## Mac 端离线兜底（可选）

把以下加到 `~/.zshrc`，每次 cd 进 math 目录时自动提示 inbox 待处理：

```bash
chpwd_compass_math() {
  if [[ "$PWD" == "$HOME/MyProjects/compass/math"* ]]; then
    local count
    count=$(find "$HOME/MyProjects/compass/math/inbox" -maxdepth 1 -type f \( -name "*.jpg" -o -name "*.png" \) 2>/dev/null | wc -l | tr -d ' ')
    if (( count > 0 )); then
      echo "⚠️  inbox 有 $count 张待处理题图。运行 'claude' 让我帮你处理。"
    fi
    # 也检查 HK 上的 inbox（Mac 离线期间堆积的）
    local hk_count
    hk_count=$(ssh -o ConnectTimeout=3 hk 'find /opt/math-inbox -maxdepth 1 -type f \( -name "*.jpg" -o -name "*.png" \) 2>/dev/null | wc -l' 2>/dev/null | tr -d ' ')
    if [[ -n "$hk_count" ]] && (( hk_count > 0 )); then
      echo "⚠️  HK inbox 还有 $hk_count 张未拉回 Mac。运行 'bash scripts/pull-inbox-from-hk.sh' 拉回。"
    fi
  fi
}
autoload -U add-zsh-hook
add-zsh-hook chpwd chpwd_compass_math
```
```

- [ ] **Step 2: commit**

```bash
cd ~/MyProjects/compass/math
git add README.md
git commit -m "doc: 加 Mac 离线兜底的 zshrc 提示 hook（可选）

写在 README 给用户复制粘贴，避免擅自改用户 zshrc。
进 math 目录时检查本地 + HK inbox，提示待处理数量。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>"
git push hk main
```

---

## Self-Review

### Spec coverage (vs DESIGN.md v1 范围)

| DESIGN v1 要求 | 实现 task |
|----------------|----------|
| TG → inbox 入口 | Task 4 (math-tutor skill Step 1) |
| Mac Claude Code 主循环 | Task 1 (WORKFLOW.md) + Task 4 (skill 触发) + Task 5 (smoke test) |
| `mistakes/` + `kp/` 文件结构 | Task 1 (目录) + Task 5 (验证) |
| 错题 / 知识点 schema | Task 1 (WORKFLOW.md 内嵌) + Task 5 (smoke test 验证) |
| WORKFLOW.md 作为指挥棒 | Task 1 |
| git Mac ↔ HK 同步 | Task 2 (bare repo + hook) |
| share.rusti.cc/math/ 浏览 | Task 2 Step 5 |
| 自我迭代 `#改` hashtag | Task 4 (skill 反馈流) + Task 6 (smoke test) |
| Mac 离线兜底 | Task 4 (skill 离线分支) + Task 7 (zshrc hook) |
| HK mirror claude-code skill | Task 3 |
| HK 新写 math-tutor skill | Task 4 |

无遗漏。

### Placeholder scan

无 TBD / TODO / "implement later" / "add appropriate X" / "similar to Task N"。所有命令完整、所有文件内容完整。

### Type/path/naming consistency

- 错题命名：`MMDD-NNN.md` ✓ 在 DESIGN.md、WORKFLOW.md、Task 5 一致
- HK inbox：`/opt/math-inbox/` ✓ Task 2、Task 4、Task 7 一致（不是 `/opt/share/math/inbox/`）
- HK share：`/opt/share/math/` ✓ 各 task 一致
- bare repo：`/opt/git/math.git/` ✓
- Mac 用户名：`natexu` ✓（Task 0 验证后所有 ssh 命令都用这个）
- Tailscale IP：`100.119.8.14` ✓
- Hermes profile：`telegram_bot` ✓
- 服务名：`hermes-gateway-telegram_bot`（user scope） ✓
- skill 路径：`/root/.hermes/profiles/telegram_bot/skills/...` ✓ 一致

### Risk / Watch

- **Task 0 已完成**（走 OpenSSH 路径，因 Mac App Store 版 Tailscale 不支持 SSH server）。详见 Task 0 段。
- **`claude -p` 通过 ssh 启动拿不到 Max OAuth token**（已解决）→ 用 `~/.local/bin/claude-remote` wrapper 先 unlock keychain。**绝不要回退用 `ANTHROPIC_API_KEY`** — 用户明确不希望走 API 计费。
- **6/15 后 `-p` 可能不走 Max** → 用户提示。届时改 wrapper 加 stdin pipe 模式，SKILL.md 不用改。
- **post-receive hook 权限** → 如果 HK 上 `/opt/share/math/` 不是 `root` 拥有，hook 写不进去。Task 2 Step 1 已 `mkdir` 默认 root 拥有，无问题。
- **Hermes profile skill startup-cache** → 改 skill 必须 restart 服务（`sync-skills-to-hk.sh` 已包含），用户手动改 skill 后忘 restart 会困惑。reminder 写在 WORKFLOW.md 里。
