Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
f1e7387
chore(gitignore): ignore .viki/ (local SQLite knowledge db)
liboze2026 May 15, 2026
9ee650c
docs(verify): add acceptance SPEC + first sample + verification-tooli…
liboze2026 May 15, 2026
feb0a80
docs(plans): add 5 implementation plans + brainstorm + INDEX for rema…
liboze2026 May 15, 2026
52afd0b
feat(verify): add repro-types — ReproSpec/ReproResult/ResultMatcher t…
liboze2026 May 15, 2026
6312d5f
feat(verify): add repro-core — pure matcher eval + verdict + worktree…
liboze2026 May 15, 2026
0618e40
feat(verify): add worktree-shell — git worktree + node_modules juncti…
liboze2026 May 15, 2026
f60bce8
feat(verify): add repro-runner + refactor computeVerdict to ambiguous…
liboze2026 May 15, 2026
fb05f2b
feat(verify): add repro-cli — orchestrates baseline prep + run + (stu…
liboze2026 May 15, 2026
cd4e775
feat(verify): add hook-moment-block ReproSpec sample + README
liboze2026 May 15, 2026
7d9831f
chore(verify): add hook-moment-block end-to-end evidence — verdict=pass
liboze2026 May 15, 2026
301ec5b
feat(verify): add gif-core — ffmpeg arg construction for GIF recording
liboze2026 May 15, 2026
31d2f42
feat(verify): add win-record.ps1 — parameterized Win32 + gdigrab reco…
liboze2026 May 15, 2026
dfdd9d1
feat(verify): add record-gif.ts + wire repro-cli to real recordGif
liboze2026 May 15, 2026
a365a14
feat(verify): add report-core — ReportManifest to SPEC-compliant HTML
liboze2026 May 15, 2026
05e0317
feat(verify): add gen-report.ts + regenerate hook-moment-block sample…
liboze2026 May 15, 2026
ffb25f3
feat(review): add review-types — CriterionResult/ReviewVerdict/FixDir…
liboze2026 May 15, 2026
3b8d3a9
feat(review): add review-core — verdict aggregation + failure classif…
liboze2026 May 15, 2026
fbd0b0c
feat(review): add run-checks — execute the 2 WORKFLOW criteria
liboze2026 May 15, 2026
735653e
feat(review): add review-cli — run criteria once, write review-verdic…
liboze2026 May 15, 2026
b0ca971
feat(review): add loop-driver + local-review skill — review/fix-loop …
liboze2026 May 15, 2026
f60bb36
feat(ci): add setup-repo composite action — DRY checkout+pnpm+install
liboze2026 May 15, 2026
81869e0
feat(review): add post-pr-comment — render verdict + post/update PR c…
liboze2026 May 15, 2026
ed3d653
feat(ci): add pr-review.yml — remote CC verdict workflow
liboze2026 May 15, 2026
5e3683e
feat(automerge): add can-auto-merge — pure gate logic for auto-squash…
liboze2026 May 15, 2026
e1fe75b
feat(automerge): add auto-merge.yml workflow
liboze2026 May 15, 2026
e587da3
docs(adr): add 0017 — six-hour release cadence + early-exit guard
liboze2026 May 15, 2026
ecdc972
feat(release): add 6h schedule + workflow_dispatch + early-exit guard…
liboze2026 May 15, 2026
4bfd78f
docs(workflow): flip 实现状态 7 项为 ✅ —— Plans #1–#5 + Phase 1/2 全部落地
liboze2026 May 15, 2026
b1324ef
fix(ci): move actions/checkout out of setup-repo composite —— 鸡生蛋修复
liboze2026 May 15, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 59 additions & 0 deletions .claude/skills/local-review/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
---
name: local-review
description: 本地 review 自动循环 —— 跑 WORKFLOW.md 的 2 条标准,不通过自动派打回 subagent 修,循环至通过或达上限
---

# local-review skill

## 何时调用

执行 CC 完成一个 feature 后,调本 skill 触发本地 review 循环;
也支持人手动 `/local-review` 触发。

## 怎么跑

```bash
# 直接调 driver(skill 实际就是包了一层文档 + 派 subagent 的 prompt 模板)
npx tsx scripts/review/loop-driver.ts \
--repro docs/acceptance/<date>-<id>/repro-result.json \
--max-retries 3
```

## 2 条标准(WORKFLOW.md 第 4 步钉死)

1. **before/after 对比成立** —— 套件 1 跑出 `verdict: "pass"`(不是 `fail` 也不是 `ambiguous`)
2. **全量测试全过 + 合并无冲突** —— `pnpm test` 退出 0,工作树无未提交改动,与 `origin/main` 无冲突

## 行为

- 跑 review-cli → verdict=pass → 落 `review-verdict.json`、exit 0、完事
- 跑 review-cli → verdict=fail → 派一个 fresh subagent(用 Claude Code 的 `Agent` 工具),
把 `fixDirective.prompt` 喂给它;subagent 修完落 `$TEMP/fix-marker` 标记;
driver 检测到标记重跑 review-cli;循环。
- 重试次数达 `--max-retries`(默认 3) → 停止、exit 非 0;
累计 fix 历史写到 `review-verdict.json` 的 `attempts[]` 字段。

## 当前默认是 `--fix-mode manual`(重要)

「verdict=fail 自动派 subagent 去修」依赖一个**还没实现**的宿主 hook:
driver 写 `$TEMP/fix-prompt` + `$TEMP/fix-pending`,hook 监听到后用 Agent tool 派 subagent。
在 hook 落地之前,driver 默认走 `--fix-mode manual` —— 第一次 fail 就把 fix prompt 打印 + 退出非 0,
由人手动派 CC 去修。

`--fix-mode agent` 会正常运行,但若 hook 缺席,会卡在等 fix-marker 直到超时(30 分钟)。
跨子系统 follow-up 见 `docs/plans/2026-05-15-INDEX.md` 末段「跨子系统 follow-up」。

## 派打回 subagent 的 prompt 模板

见 `fix-directive-prompt.md`。

## 默认测试命令

worktree 里没装独立 `.bin/`,且 vitest config 不抓 `scripts/`,
所以 driver 默认 `--test-cmd "C:/bzli/Matrix/node_modules/.bin/tsx.cmd --test scripts/lock-core.test.ts"`。
项目级真实 review 请显式传:

```bash
--test-cmd "pnpm test" # 全量(在主仓库 root 跑)
--test-cmd "C:/bzli/Matrix/node_modules/.bin/vitest.cmd run packages/core" # 包级
```
29 changes: 29 additions & 0 deletions .claude/skills/local-review/fix-directive-prompt.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# 打回 subagent prompt 模板

> 由 `scripts/review/loop-driver.ts` 在 `--fix-mode agent` 下使用:
> 把本文件填入 `{{fixDirective.prompt}}` 处理后,作为派给 fresh subagent 的 prompt。

---

你是一个执行 subagent,被本地 review 循环派回来「修一个 review 失败」。

## 任务

{{fixDirective.prompt}}

## 约束(硬性)

- **只动与本失败类别直接相关的代码**。不要顺手 refactor、不要 reorganize 文件、不要改无关测试。
- 修完用 `Bash`(Unix)或 `PowerShell`(Windows)落标记文件,然后 stop:
- Unix:`echo done > /tmp/fix-marker`
- Windows:`echo done > %TEMP%\fix-marker`
loop-driver 看到标记会自动重跑 review;**你不要自己跑 review**。
- 如果你判断这次失败**不应在本 PR 修**(超出范围、应另开 issue),
把标记换成 `skip:<理由>`(把 done 改成 skip: 加理由)然后 stop。
driver 看到 `skip:` 会停循环、把理由报给上游。

## 边界

- 不开新 PR、不切分支、不 commit、不 push。改完留在工作树里,driver 会处理 commit 时机。
- 不要去改 review 标准本身(`scripts/review/review-core.ts` 等)—— 标准是 WORKFLOW.md 钉死的;
改它属于另一个子系统决策,不在本次「修 feature 让 review 过」的范围内。
2 changes: 2 additions & 0 deletions .githooks/post-merge
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,5 @@ if teamagent --help 2>&1 | grep -q "m5-sync"; then
fi
exit 0
# <<< teamagent post-merge block <<<


2 changes: 2 additions & 0 deletions .githooks/pre-commit
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,5 @@ else
fi
exit 0
# <<< teamagent post-merge block <<<


17 changes: 17 additions & 0 deletions .github/actions/setup-repo/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
name: Setup repo (pnpm + node + install)
description: 在已 checkout 的 repo 上装 pnpm/node 并 frozen install。注意:调用方必须先 actions/checkout —— composite action 自己 checkout 是鸡生蛋(GitHub 找不到 action.yml)。
inputs:
node-version:
description: Node 版本
required: false
default: '22'
runs:
using: composite
steps:
- uses: pnpm/action-setup@v5
- uses: actions/setup-node@v5
with:
node-version: ${{ inputs.node-version }}
cache: pnpm
- run: pnpm install --frozen-lockfile
shell: bash
116 changes: 116 additions & 0 deletions .github/workflows/auto-merge.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
name: Auto-merge (远程 review 通过 → 自动 squash 合主分支)

# workflow_run 链式触发:pr-review.yml 跑完 → 本 workflow 启动 →
# 调 scripts/automerge/can-auto-merge.ts 门控 → 通过则 gh pr merge --squash --delete-branch。
# 详细见 docs/plans/2026-05-15-auto-merge.md。

on:
workflow_run:
workflows: ["PR Review (远程 CC 自动评审)"]
types: [completed]

permissions:
contents: write # squash merge 落 commit 到 main
pull-requests: write # 关 PR、删 branch、写 comment

# 同 PR 重入兜底:同 head SHA 只跑一次
concurrency:
group: auto-merge-${{ github.event.workflow_run.head_sha }}
cancel-in-progress: false

jobs:
evaluate-and-merge:
# 仅在 pr-review 成功完成 + 是 pull_request 触发的情况下运行
if: github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.event == 'pull_request'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
with:
ref: ${{ github.event.workflow_run.head_sha }}
fetch-depth: 0
- uses: actions/setup-node@v5
with:
node-version: '22'

- name: Resolve PR number from workflow_run
id: pr
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
shell: bash
run: |
# workflow_run event 不直接给 PR number,要从 head_branch 反查
PR_NUMBER=$(gh pr list --head "${{ github.event.workflow_run.head_branch }}" \
--state open --json number --jq '.[0].number // empty')
if [[ -z "$PR_NUMBER" ]]; then
echo "::warning::找不到 head_branch=${{ github.event.workflow_run.head_branch }} 的 open PR;退出"
echo "skip=true" >> "$GITHUB_OUTPUT"
exit 0
fi
echo "pr=$PR_NUMBER" >> "$GITHUB_OUTPUT"
echo "找到 PR #$PR_NUMBER"

- name: Snapshot PR
if: steps.pr.outputs.skip != 'true'
id: snap
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
shell: bash
run: |
PR=${{ steps.pr.outputs.pr }}
gh pr view "$PR" --json number,baseRefName,isDraft,body,labels,state,mergeable,headRepository,headRepositoryOwner > /tmp/pr.json
# review/verdict commit status
STATUS=$(gh api "repos/${{ github.repository }}/commits/${{ github.event.workflow_run.head_sha }}/statuses" \
--jq '[.[] | select(.context=="review/verdict")][0].state // "missing"')
REPO_OWNER='${{ github.repository_owner }}'
jq --arg verdict "$STATUS" --arg owner "$REPO_OWNER" '. + {
reviewVerdictState: $verdict,
isFromInternalRepo: (.headRepositoryOwner.login == $owner),
labels: [.labels[].name]
}' /tmp/pr.json > /tmp/snapshot.json
cat /tmp/snapshot.json

- name: Decide
if: steps.pr.outputs.skip != 'true'
id: decide
shell: bash
run: |
set +e
npx tsx -e "
import {canAutoMerge} from './scripts/automerge/can-auto-merge.ts';
import {readFileSync, writeFileSync} from 'node:fs';
const snap = JSON.parse(readFileSync('/tmp/snapshot.json', 'utf-8'));
const r = canAutoMerge(snap);
writeFileSync('/tmp/decision.json', JSON.stringify(r, null, 2));
console.log(JSON.stringify(r, null, 2));
process.exit(r.merge ? 0 : 78);
"
ec=$?
set -e
if [[ $ec -eq 0 ]]; then
echo "merge=true" >> "$GITHUB_OUTPUT"
elif [[ $ec -eq 78 ]]; then
echo "merge=false" >> "$GITHUB_OUTPUT"
else
echo "::error::canAutoMerge 评估异常 (exit=$ec)"
exit $ec
fi

- name: Merge
if: steps.decide.outputs.merge == 'true'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
PR=${{ steps.pr.outputs.pr }}
gh pr merge "$PR" --squash --delete-branch
gh pr comment "$PR" --body "🤖 远程 review 通过 → 已自动 squash 合并到 main(\`auto-merge.yml\`)。"

- name: Skip note
if: steps.decide.outputs.merge == 'false' && steps.pr.outputs.skip != 'true'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
shell: bash
run: |
PR=${{ steps.pr.outputs.pr }}
REASON=$(jq -r .reason /tmp/decision.json)
gh pr comment "$PR" --body "🤖 auto-merge 跳过 —— 原因: $REASON"
echo "skipped: $REASON"
Loading
Loading