---
name: review-readiness-scan
description: 掃描 openspec/changes/ 各 change 的 manual-review 區塊，判斷哪些已 ready for 人工檢查、哪些被 Pre-Review Data Readiness pattern 命中（alert）尚未 ready，並把結果登記到 HANDOFF.md。Use when 使用者說「掃 review readiness」「review:ui 哪些 ready」「scan manual review alerts」「批次人工檢查前先看哪些 ready」「找出 review:ui 的 alert」。不適用於單一 change 內逐項 review（那走 `pnpm review` GUI，從 clade home 跑）。
license: MIT
metadata:
  author: clade
  version: "1.0"
---
<!-- 🔒 LOCKED — managed by clade · auto-generated by sync-to-agents; edit source in .claude/ then re-run sync -->

# review-readiness-scan

主動掃描 consumer 端所有 active change 的 `## 人工檢查` 區塊，把「已 ready / 尚未 ready」分組寫入 `HANDOFF.md`，讓使用者能在合適時機**批次**跑 `pnpm review`（從 clade home），而不是每條 change 個別開 GUI 才知道沒準備好。

**前置**：consumer 必須已從 clade 散播到 `scripts/review-gui.mts`（各 consumer 預設都有；若沒有，跑 `pnpm hub:check` 確認）。

## Step 1 — 跑 headless scan

```bash
cd ~/offline/clade
node vendor/scripts/review-gui.mts --scan
```

預設從 clade home 掃，輸出會聚合 `consumers.local` 內所有 consumer + worktree。若是 CI / debug 要只掃單一 consumer，才改用 `node vendor/scripts/review-gui.mts --repo <consumer-path> --scan`。

`reviewPort` 會用跟 GUI server 相同的 fallback 規則計算：若 5174 已被占用，scan 會輸出下一個可用 port（例如 5175），後續 handoff **MUST** 使用 entry 內的 `reviewUrl`，不要硬寫 5174。

輸出 JSON（schema: `review-readiness-scan/v2`）到 stdout，結構：

```jsonc
{
  "schema": "review-readiness-scan/v2",
  "generatedAt": "<ISO8601>",
  "repoRoot": "<abs>",
  "reviewHost": "127.0.0.1",
  "reviewPort": 5174,
  "counts": {
    "ready": N,
    "notReady": M,
    "buckets": { "ready": N, "readyForEvidence": N, "applyInProgress": N }
  },
  "ready":    [ { "name": "<change>", "consumerId": "perno",
                  "changeKey": "perno:<change>", "reviewUrl": "http://127.0.0.1:5174/review/perno:<change>",
                  "bucket": "ready", "pending": N, "issued": N, "total": N } ],
  "notReady": [ { "name": "<change>", "consumerId": "perno",
                  "changeKey": "perno:<change>", "reviewUrl": "http://127.0.0.1:5174/review/perno:<change>",
                  "bucket": "readyForEvidence", "pending": N, "issued": N, "total": N,
                  "readinessHits": N, "malformed": N,
                  "hitsByCode": { "UI_ITEM_NO_URL": 2, "REVIEW_UI_BACKEND_ROUNDTRIP": 1 },
                  // evidenceMissing：item 標了 [verify:e2e/api/ui] 但缺對應 (verified-*:) annotation。
                  // 落 notReady 群的另一條觸發路徑（與 readinessHits / malformed 並列），各 entry 一個 item。
                  "evidenceMissing": [ { "itemId": "#3", "description": "...",
                                          "kinds": ["e2e", "api", "ui"] } ] } ],
  "buckets": {
    "ready": [ /* 可直接開 reviewUrl 給 user 做 GUI review */ ],
    "readyForEvidence": [ /* apply 已接近完成，先跑 /spectra-apply Step 8a 補 evidence */ ],
    "applyInProgress": [ /* implementation 還沒完成，不該補 evidence */ ],
    "applyBlocked": [ /* impl 卡外部 blocker（@apply-blocked marker），交還 user，不要硬推 */ ],
    "healthCheckNeeded": [ /* manual-review pattern hits，先 ingest/fix tasks.md */ ],
    "awaitArchiveWalkthrough": [ /* 只剩 [discuss]，跑 /spectra-archive Step 2.5 */ ],
    "awaitingUserDecision": [ /* Claude 已標 (awaiting-user-decision:)，等 user 商業拍板，不要硬推 */ ],
    "feedbackGiven": [ /* user 已標 issue 或 verify pending，交回 Claude 處理 */ ]
  }
}
```

> Hono 沒裝 → script 會在 dynamic import 時報 missing dep。讓 user 跑 `pnpm add -D hono`，不要自動安裝。

## Step 2 — Patch HANDOFF.md 固定 section

HANDOFF.md 用 marker 包夾，每次重跑**覆蓋同一段**（不累積垃圾，不留時戳 entries）：

```markdown
<!-- BEGIN: review-readiness-scan -->
## Manual Review Readiness（auto-scan）

> 最後掃描：<generatedAt>　|　ready: N　not-ready: M　|　review: http://127.0.0.1:<port>

### ✅ 可以開始檢查（N changes）

可批次跑 `pnpm review`（從 clade home）處理；每行直接列 `reviewUrl`，不要重新手組 URL：

- `<changeKey>` — pending N/total — `<reviewUrl>`
- ...

### ⚠ 尚未準備好，需先補強（M changes）

下列 change 落這群的原因有兩種，依實際 entry 欄位分開列：

**(A) Pre-Review Data Readiness alert** — `readinessHits > 0`，**先補資料再 review**（patterns 詳見 `vendor/snippets/manual-review-enforcement/patterns.json`）：

- `<changeKey>` — pending N · ⚠ N hits: UI_ITEM_NO_URL ×2, REVIEW_UI_BACKEND_ROUNDTRIP ×1 — `<reviewUrl>`
- ...

**(B) Verify-channel evidence missing** — `evidenceMissing.length > 0`，**跑 `/spectra-apply` Step 8a 補 evidence**：

- `<changeKey>` — pending N · ⚠ N item 缺 evidence (e2e ×2, api ×1, ui ×1) — `<reviewUrl>`
- ...

**(C) Apply 尚未完成 / feedback / archive walkthrough** — 依 `bucket` 分組列在同一 section 下，不要把這些 change 放進「可以開始檢查」：

- `applyInProgress` → 繼續 `/spectra-apply <change>`，不要補 Step 8a evidence
- `applyBlocked` → impl 卡外部 blocker（`@apply-blocked` marker），ball in user，**不要**硬推；解 blocker 後移除 marker 回 applyInProgress
- `feedbackGiven` → user 已在 GUI 留 issue 或 verify pending，交回 Claude 針對 issue 處理
- `awaitingUserDecision` → Claude 已標 `(awaiting-user-decision:)`，等 user 商業拍板，ball in user，**不要**硬推
- `awaitArchiveWalkthrough` → 跑 `/spectra-archive <change>` 觸發 Step 2.5 discuss walkthrough
- `crossWtDirty` / `malformed` → 先修 worktree routing 或 tasks.md 格式
<!-- END: review-readiness-scan -->
```

### 寫入規則

1. **HANDOFF.md 不存在**：建立 HANDOFF.md 並把 section 放在檔尾
2. **HANDOFF.md 存在、有舊 marker**：用 BEGIN/END 之間整段覆寫，**保留** marker 外的所有內容
3. **HANDOFF.md 存在、無 marker**：append 到檔尾（前面空一行）
4. **ready 與 notReady 都為 0**：仍寫入 section，但內容改成 `> 目前無含人工檢查區塊的 active change。`，讓 user 看到 skill 跑過、不是漏跑

### 不該做

- ❌ 不要刪 HANDOFF.md 其他段落（即使看起來過時）
- ❌ 不要在 ready 段落 append 額外備註、推測 user 接下來該做什麼 — section 是純資料，主線判讀
- ❌ 不要因為 hitsByCode 命中某個 code 就**自動修 tasks.md**（修法走 `/spectra-ingest`，由 user 拍板）

## Step 3 — 主線報告

寫完 HANDOFF.md 後，給 user 一段精簡 summary：

```
Scanned at <generatedAt>:
  ✅ Ready  (N): consumer:change-a, consumer:change-b
  ⚠  Need fix (M): consumer:change-c (3 hits), consumer:change-d (1 evidence missing)

HANDOFF.md updated（section: Manual Review Readiness）。
Ready deep-links 已寫入 HANDOFF.md；需要 fix 的先看 bucket / hitsByCode 處理後再 rescan。
```

**不要**主動跑 `/spectra-ingest`、不要主動修 tasks.md、不要推薦 schedule。User 拍板下一步。

## 何時 NOT 觸發

- 使用者只想跑單一 change 的人工檢查 → 直接 `cd ~/offline/clade && pnpm review`，不需要 scan
- 使用者問「現在有哪些 active change」這類純列表 → 用 `spectra list`，scan 是 readiness 評估不是 change 列表
- consumer 沒有 `openspec/changes/` 目錄（非 spectra 專案）→ scan 會輸出空，回 user 「此專案沒有 openspec/changes/，跳過」

## 邊界與已知限制

- Scan 只看 `openspec/changes/<name>/tasks.md` 的 `## 人工檢查` section，**不**讀 parked changes（spectra parked 那群會被排除）— 因為 parked 通常是暫存不在動的，readiness 評估無意義
- hitsByCode 用的 pattern 規格存在 `vendor/snippets/manual-review-enforcement/patterns.json`，與 review-gui banner、`post-propose-manual-review-check.sh` 共用同一份 source-of-truth
- 截圖資料夾數（screenshotTopicCount）**不**影響 readiness 判斷 — 截圖缺失屬於 GUI 內 banner（red verify-channel evidence-missing），不在 Pre-Review Data Readiness 範疇
