gstack /retro 完整指南:从 Git 数据生成团队感知型每周工程复盘
gstack 的 /retro 技能把"周会前翻 git log 找感觉"变成了一条完全数据驱动的工程复盘流水线:它并行拉取 12 类 git 原始数据,计算指标、检测工作会话、追踪连续提交天数,并对每位贡献者给出有具体 commit 支撑的表扬与成长建议。读完本文,你可以掌握 /retro 全部三种运行模式(单仓窗口 / 对比 / 跨项目 global)的参数、14 步分析流程、JSON 快照 schema,以及防"陈旧基线"误报的 Step 0.5 预检守卫的实现原理。
一、/retro 的定位与触发方式
在 docs/skills.md 中,/retro 被归入 gstack 的 Eng Manager 角色:"Team-aware weekly retro. Per-person breakdowns, shipping streaks, test health trends, growth opportunities." 官方文档给出的定位一句话概括是:"At the end of the week I want to know what actually happened. Not vibes — data."
技能定义位于 retro/SKILL.md(由 retro/SKILL.md.tmpl 通过 bun run gen:skill-docs 自动生成,禁止手改生成文件)。frontmatter 声明了以下关键元数据:
name: retro,version: 2.0.0,preamble-tier: 2allowed-tools: Bash、Read、Write、Glob、AskUserQuestion- 触发词(triggers):
weekly retro、what did we ship、engineering retrospective - 用户直接输入
/retro即可调用;文档同时建议在工作周或 sprint 结束时主动提议运行
frontmatter 还包含一段 gbrain: context_queries 声明,定义了三条文件系统的上下文查询,用于在技能启动时自动加载历史:
gbrain:
schema: 1
context_queries:
- id: prior-retros
kind: filesystem
glob: ".context/retros/*.json"
sort: mtime_desc
limit: 5
render_as: "## Prior retros for this project"
- id: recent-timeline
kind: filesystem
glob: "~/.gstack/projects/{repo_slug}/timeline.jsonl"
tail: 30
render_as: "## Recent timeline events"
- id: recent-learnings
kind: filesystem
glob: "~/.gstack/projects/{repo_slug}/learnings.jsonl"
tail: 10
render_as: "## Recent learnings"
其中 prior-retros 的注释(见 retro/SKILL.md.tmpl 第 26–29 行)值得注意:/retro 会把快照写到仓库本地的 .context/retros/*.json;旧版本 glob 指向 ~/.gstack/.../retros/*.md,匹配的是一个从不被写入的目录和扩展名,属于"死查询",已在 #2552 中修正为当前路径。
二、参数、时间窗口与参数校验
/retro 支持以下全部调用形式(见 retro/SKILL.md "Arguments" 一节):
| 命令 | 行为 |
|---|---|
/retro |
默认最近 7 天 |
/retro 24h |
最近 24 小时 |
/retro 14d |
最近 14 天 |
/retro 30d |
最近 30 天 |
/retro compare |
当前窗口 vs 前一个等长窗口对比 |
/retro compare 14d |
显式指定窗口的对比 |
/retro global |
跨所有 AI 编码工具的跨项目复盘(默认 7d) |
/retro global 14d |
跨项目复盘 + 显式窗口 |
窗口解析规则有三个要点:
- 零默认与本地时区:无参数默认 7 天;所有时间一律用系统默认时区报告(明确禁止设置
TZ)。 - 午夜对齐(midnight-aligned):
d(天)与w(周)单位必须换算成本地午夜 0 点的绝对起始日期,而不是相对字符串。例如今天是 2026-03-18、窗口 7 天,则起始日是 2026-03-11,git 查询用--since="2026-03-11T00:00:00"。文档特别解释了原因:省略T00:00:00后缀时,git 会把--since="2026-03-11"解释为当天 11pm 而非午夜(取决于查询执行时刻)。周单位按 7 倍换算(2w= 14 天);小时(h)单位不適用午夜对齐,直接用--since="N hours ago"。 - 参数校验:若参数不匹配"数字 + d/h/w"、
compare(可跟窗口)或global(可跟窗口),打印用法并停止:
Usage: /retro [window | compare | global]
/retro — last 7 days (default)
/retro 24h — last 24 hours
/retro 14d — last 14 days
/retro 30d — last 30 days
/retro compare — compare this period vs prior period
/retro compare 14d — compare with explicit window
/retro global — cross-project retro across all AI tools (7d default)
/retro global 14d — cross-project retro with explicit window
若第一个参数是 global:跳过单仓流程(Steps 1–14),改走本文第六节的 Global Retrospective 流程。第二个参数是窗口(默认 7d)。global 模式不要求在 git 仓库内运行。
三、Step 0:平台与基线分支检测
单仓流程第一步是检测 git 托管平台与"基线分支"(后续所有 git diff、git log、git fetch、git merge 命令中的 <default> 都替换为它):
git remote get-url origin 2>/dev/null
- URL 含
github.com→ GitHub - URL 含
gitlab→ GitLab - 其他情况用 CLI 探测:
gh auth status成功 → GitHub(覆盖 GitHub Enterprise);glab auth status成功 → GitLab(覆盖自建实例);都失败 → unknown(仅用 git 原生命令)
基线分支的探测优先级:
- GitHub:
gh pr view --json baseRefName -q .baseRefName→gh repo view --json defaultBranchRef -q .defaultBranchRef.name - GitLab:
glab mr view -F json提取target_branch→glab repo view -F json提取default_branch - Git 原生回退:
git symbolic-ref refs/remotes/origin/HEAD去掉前缀 →git rev-parse --verify origin/main则用main→git rev-parse --verify origin/master则用master→ 全部失败回退main
探测结果要打印出来,并在所有后续命令中一致替换。
四、Prior Learnings 与非 git 上下文
在拉取数据前,/retro 先搜索历史会话沉淀的 learnings:
_CROSS_PROJ=$(~/.claude/skills/gstack/bin/gstack-config get cross_project_learnings 2>/dev/null || echo "unset")
echo "CROSS_PROJECT: $_CROSS_PROJ"
if [ "$_CROSS_PROJ" = "true" ]; then
~/.claude/skills/gstack/bin/gstack-learnings-search --limit 10 --cross-project 2>/dev/null || true
else
~/.claude/skills/gstack/bin/gstack-learnings-search --limit 10 2>/dev/null || true
fi
- 若
CROSS_PROJECT为unset(首次运行):通过 AskUserQuestion 询问是否开启跨项目 learnings 搜索(本机多项目模式下可发现可复用的模式;纯本地、数据不出机器;建议 solo 开发者开启,服务多个客户代码库时关闭以防交叉污染)。选择后写入gstack-config set cross_project_learnings true|false,再用对应 flag 重跑搜索。 - 若发现 learnings 且与本次某条发现匹配,展示 "Prior learning applied: [key] (confidence N/10, from [date])",让"gstack 在你的代码库上越来越聪明"这件事变得可见。
随后检查非 git 上下文:
[ -f ~/.gstack/retro-context.md ] && echo "RETRO_CONTEXT_FOUND" || echo "NO_RETRO_CONTEXT"
若存在,读取用户手工维护的 ~/.gstack/retro-context.md(会议纪要、日历事件、决策等 git 历史里看不到的信息),并在相关处织入复盘叙事。
五、Step 0.5:陈旧基线 + 错误"今天"预检守卫
这是 /retro 最有工程含量的一个环节,由 #1624 回归缺陷催生。问题场景:/retro 用"今天"计算窗口、再查询 git log --since=<window> origin/<default>。如果模型会话上下文里的"今天"漂移了,或本地 worktree 的 origin/<default> 远远落后于真实远端,窗口内可能返回 0 或接近 0 个 commit——此时没有守卫的话,复盘会从虚无中编造出一段看起来自洽的叙事(silently confidently-wrong)。Step 0.5 的目的就是拦截这类误报。
预检按固定顺序执行,第一个命中的分支获胜(短路由 _RETRO_GUARD_VERDICT 变量门控):
# Pre-check A: no remote configured?
_RETRO_HAS_REMOTE=$(git remote 2>/dev/null | grep -c '^origin$' || echo 0)
if [ "$_RETRO_HAS_REMOTE" = "0" ]; then
echo "RETRO_GUARD: no 'origin' remote, base freshness not verified — proceeding"
_RETRO_GUARD_VERDICT="skip-no-remote"
fi
# Pre-check B: detached HEAD or no current base?
if [ -z "$_RETRO_GUARD_VERDICT" ]; then
_RETRO_HEAD_REF=$(git symbolic-ref --quiet HEAD 2>/dev/null || echo "")
if [ -z "$_RETRO_HEAD_REF" ]; then
echo "RETRO_GUARD: detached HEAD, base freshness not verified — proceeding"
_RETRO_GUARD_VERDICT="skip-detached"
fi
fi
# Pre-check C: fetch origin <default>; if it fails, warn but proceed.
if [ -z "$_RETRO_GUARD_VERDICT" ]; then
if ! git fetch origin <default> --quiet 2>/dev/null; then
echo "RETRO_GUARD: 'git fetch origin <default>' failed (offline?) — proceeding against last-known origin/<default>"
_RETRO_GUARD_VERDICT="warn-fetch-failed"
fi
fi
# Pre-check D: BLOCK only when fetch succeeded AND the latest origin/<default>
# commit predates the retro window.
if [ -z "$_RETRO_GUARD_VERDICT" ]; then
_RETRO_LATEST_ISO=$(git log -1 --format=%ci origin/<default> 2>/dev/null | awk '{print $1}')
if [ -n "$_RETRO_LATEST_ISO" ]; then
echo "RETRO_GUARD: latest origin/<default> commit on $_RETRO_LATEST_ISO"
_RETRO_GUARD_VERDICT="check-gap"
fi
fi
bash 执行完之后由模型评估 RETRO_GUARD: latest origin/<default> commit on <DATE> 相对"今天"和窗口的关系:
- 若最新 commit 日期早于(今天 − 窗口天数):BLOCK,输出 "Retro window is stale. Latest commit on
origin/<default>was<DATE>, but the window covers<since>to<today>. This usually means either (a) today's date is wrong in this session or (b)origin/<default>is materially behind the remote. Confirm today's date via the session reminder; if today is correct, rungit fetch origin <default>manually and re-run /retro.",并停住直到用户解决。 - 否则输出 "RETRO_GUARD: latest commit
<DATE>within window — proceeding."
两条细节值得强调:
- "今天"的来源:文档明确指示模型从会话提醒中用户可见的
## currentDate标签取当前日期,绝不用date命令——容器化 harness 里系统时钟可能偏好几个小时。若模型无法可靠计算"今天",必须在此处停下并通过 AskUserQuestion 询问用户,而不是继续。 - 跳过路径也要披露:
skip-no-remote、skip-detached、warn-fetch-failed都继续进入 Step 1,但要求把原因写成一行 stderr,让复盘叙事携带披露(如"offline run, window not freshness-verified"),而不是静默地错误报告。
这套守卫不是"软约定",而是被静态回归测试钉死的构建不变量。test/regression-1624-retro-stale-base.test.ts 对 retro/SKILL.md.tmpl 的正文做模式匹配:验证 Step 0.5 标题存在且位于 Step 1 之前、四个预检分支各自的 shell 形状(grep -c '^origin$'、git symbolic-ref --quiet HEAD、fetch 失败分支、git log -1 --format=%ci origin/<default>)、每个后续分支都被 if [ -z "$_RETRO_GUARD_VERDICT" ] 门控(保证短路顺序),以及 BLOCK 文案必须同时给出最新 commit 日期、git fetch 修复指令和日期确认提示。测试文件头注释说得很直白:"these tests are static invariants against the template body — they fail the build if the guard is removed, weakened, or its ordering broken."
六、Step 1:并行拉取 12 类原始数据
先 git fetch origin <default> --quiet,并用 git config user.name / user.email 确定"你"(读这份复盘的人)。其他作者都是队友,用这个身份锚定叙事("你的" commit vs 队友贡献)。
然后并行执行以下独立命令(文档明确说 Run ALL in parallel):
# 1. 窗口内全部 commit:hash、作者名/邮箱、时间、标题、短统计
git log origin/<default> --since="<window>" --format="%H|%aN|%ae|%ai|%s" --shortstat
# 2. 每 commit 的测试 vs 生产 LOC 分解(numstat 行跟在 COMMIT:<hash>|<author> 头后)
# 测试文件按 test/|spec/|__tests__/ 区分
git log origin/<default> --since="<window>" --format="COMMIT:%H|%aN" --numstat
# 3. commit 时间戳(会话检测 + 小时分布),按 unix 时间排序
git log origin/<default> --since="<window>" --format="%at|%aN|%ai|%s" | sort -n
# 4. 最常被改动的文件(热点分析)
git log origin/<default> --since="<window>" --format="" --name-only | grep -v '^$' | sort | uniq -c | sort -rn
# 5. 从 commit message 提取 PR/MR 编号(GitHub #NNN / GitLab !NNN)
git log origin/<default> --since="<window>" --format="%s" | grep -oE '[#!][0-9]+' | sort -t'#' -k1 | uniq
# 6. 按作者的文件热点(谁在改什么)
git log origin/<default> --since="<window>" --format="AUTHOR:%aN" --name-only
# 7. 按作者 commit 计数(快速汇总)
git shortlog origin/<default> --since="<window>" -sn --no-merges
# 8. Greptile 分诊历史(若存在)
cat ~/.gstack/greptile-history.md 2>/dev/null || true
# 9. TODOS.md 待办清单(若存在)
cat TODOS.md 2>/dev/null || true
# 10. 测试文件总数
git ls-files 2>/dev/null | grep -E '(\.test\.|\.spec\.|_test\.|_spec\.)' | wc -l
# 11. 窗口内回归测试 commit
git log origin/<default> --since="<window>" --oneline --grep="test(qa):" --grep="test(design):" --grep="test: coverage"
# 12. gstack 技能使用遥测(若存在)
cat ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true
# 12'. 窗口内被改动的测试文件数
git log origin/<default> --since="<window>" --format="" --name-only | grep -E '\.(test|spec)\.' | sort -u | wc -l
七、Step 2:指标汇总与贡献者排行榜
指标以表格呈现(顺序即文档规定的展示顺序):
| Metric | Value |
|---|---|
| Features shipped(来自 CHANGELOG + 已合并 PR 标题) | N |
| Commits to main | N |
| Weighted commits(commits × 平均触及文件数,每 commit 上限 20) | N |
| Contributors | N |
| PRs merged | N |
| Logical SLOC added(非空行、非注释,主代码量指标) | N |
| Raw LOC: insertions / deletions / net | N |
| Test LOC (insertions) / Test LOC ratio | N / N% |
| Version range | vX.Y.Z.W → vX.Y.Z.W |
| Active days | N |
| Detected sessions | N |
| Avg raw LOC/session-hour | N |
| Greptile signal | N% (Y catches, Z FPs) |
| Test Health | N total tests · M added this period · K regression tests |
文档给出了这个指标顺序的 V1 设计理由:features shipped 打头(用户真正拿到的是什么);commits 与 weighted commits 反映"发货意图";Logical SLOC added 反映真实新增功能;raw LOC 被降级为上下文信息,因为"AI 会膨胀它——一次好修复的十行代码不比一万行脚手架少发货"。原始文档指向 docs/designs/PLAN_TUNING_V1.md §Workstream C 作进一步说明。
紧跟表格的是按作者排行榜,按 commit 数降序,当前用户(git config user.name)固定第一行并标注 "You (name)":
Contributor Commits +/- Top area
You (garry) 32 +2400/-300 browse/
alice 12 +800/-150 app/services/
bob 3 +120/-40 tests/
四个条件性扩展指标:
- Greptile signal(仅当
~/.gstack/greptile-history.md存在):按日期过滤窗口内条目,按类型计数fix、fp、already-fixed,信号比 =(fix + already-fixed) / (fix + already-fixed + fp)。无条目或文件不存在则跳过整行;无法解析的行静默跳过。这条与 docs/skills.md 中 Greptile 技能的描述呼应:每次确认的误报都会存进该历史文件,/retro负责追踪信号率随时间的变化。 - Backlog Health(仅当
TODOS.md存在):统计未关闭 TODO 总数(排除## Completed区)、P0/P1 数、P2 数、本周期完成项(Completed 区内日期落在窗口内的)、本周期新增项(交叉引用窗口内改过 TODOS.md 的 commit),呈现为| Backlog Health | N open (X P0/P1, Y P2) · Z completed this period |。 - Skill Usage(仅当
~/.gstack/analytics/skill-usage.jsonl存在):按ts过滤窗口内条目,区分技能激活(无event字段)与 hook 触发(event: "hook_fire"),按技能名聚合,呈现为| Skill Usage | /ship(12) /qa(8) /review(5) · 3 safety hook fires |。 - Eureka Moments(仅当
~/.gstack/analytics/eureka.jsonl存在):按ts过滤,列出每个顿悟时刻的技能、分支和一句话洞察,例如:
EUREKA /office-hours (branch: garrytan/auth-rethink): "Session tokens don't need server storage — browser crypto API makes client-side JWT validation viable"
以上数据源缺失时一律静默跳过对应行,不报错。
八、Steps 3–8:时间分布、会话、提交类型、热点、PR 规模与专注度
Step 3 提交小时分布:本地时间直方图(柱状条),并点名:峰值时段、死区、模式是双峰(早晚)还是连续、深夜编码簇(22 点后)。
Step 4 工作会话检测:用相邻 commit 间 45 分钟间隔阈值切分会话。每个会话报告起止时间、commit 数、持续分钟数,并分级:
- Deep sessions(50+ 分钟)
- Medium sessions(20–50 分钟)
- Micro sessions(<20 分钟,通常是单 commit 的 fire-and-forget)
汇总计算:总有效编码时长、平均会话长度、每小时 LOC。
Step 5 提交类型分解:按 conventional commit 前缀(feat/fix/refactor/test/chore/docs)分类,以百分比条呈现。fix 占比超过 50% 要标记——它意味着 "ship fast, fix fast" 模式,可能暴露 review 缺口。
Step 6 热点分析:展示改动最多的前 10 个文件,标记三类:改动 5+ 次的 churn 热点、热点列表里测试文件 vs 生产文件的构成、VERSION/CHANGELOG 的变更频率(版本纪律指标)。
Step 7 PR 规模分布:从 commit diff 估算 PR 尺寸并分桶——Small(<100 LOC)、Medium(100–500)、Large(500–1500)、XL(1500+)。
Step 8 专注度 + 本周最佳发货:
- Focus score:触及单一最频繁一级目录(如
app/services/)的 commit 百分比。分数高 = 深度聚焦;分数低 = 上下文切换散乱。报告格式如 "Focus score: 62% (app/services/)"。 - Ship of the week:自动识别窗口内 LOC 最高的单个 PR,给出 PR 号、标题、LOC,并从 commit message 与触及文件推断其重要性。
九、Step 9:团队成员分析(团队感知核心)
对每位贡献者(含当前用户)计算六项:commit 与 LOC(总数/插入/删除/净)、聚焦领域(改动最多的前 3 个目录/文件)、个人提交类型 mix、会话模式(峰值时段与会话数)、测试纪律(个人 test LOC ratio)、最大发货(窗口内单条最高影响的 commit 或 PR)。
- 对当前用户给最深层处理:完整继承单人复盘的所有细节(会话分析、时间模式、focus score),用第一人称表述("Your peak hours..."、"Your biggest ship...")。
- 对每位队友写 2–3 句叙述加两块结论:
- Praise(1–2 条,锚定具体 commit):不要 "great work",要说清哪里好。文档给的范例:"Shipped the entire auth middleware rewrite in 3 focused sessions with 45% test coverage"、"Every PR under 200 LOC — disciplined decomposition."
- Opportunity for growth(1 条,锚定数据,定位为升级建议而非批评):如 "Test ratio was 12% this week — adding test coverage to the payment module before it gets more complex would pay off"、"5 fix commits on the same file suggest the original PR could have used a review pass."
- 单人仓库:跳过团队拆解,复盘退化为个人复盘。
- Co-Authored-By trailers:解析 commit message 里的
Co-Authored-By:行,把这些作者与主作者共同记功;但 AI 共同作者(如noreply@anthropic.com)不计入团队成员,而是单独追踪 "AI-assisted commits" 指标。
十、Steps 10–13:周趋势、连续天数、历史对比与 JSON 快照
Step 10 周对周趋势(仅窗口 ≥14d):按周分桶展示——每周 commit 数(总量与按作者)、每周 LOC、每周测试比、每周 fix 比、每周会话数。
Step 11 连续天数追踪:从今天往前数连续"至少 1 个 commit 到 origin/<default>"的天数,同时统计团队与个人两条 streak:
# 团队 streak:所有唯一 commit 日期(本地时间),无硬性截断
git log origin/<default> --format="%ad" --date=format:"%Y-%m-%d" | sort -u
# 个人 streak:仅当前用户的 commit
git log origin/<default> --author="<user_name>" --format="%ad" --date=format:"%Y-%m-%d" | sort -u
查询的是完整历史,所以任意长度的 streak 都能准确报告。输出示例:"Team shipping streak: 47 consecutive days" / "Your shipping streak: 32 consecutive days"。
Step 12 加载历史并对比:
setopt +o nomatch 2>/dev/null || true # zsh compat
ls -t .context/retros/*.json 2>/dev/null
- 若存在历史 retro:用 Read 工具加载最近一份,计算关键指标增量,输出 Trends vs Last Retro 表:
Last Now Delta
Test ratio: 22% → 41% ↑19pp
Sessions: 10 → 14 ↑4
LOC/hour: 200 → 350 ↑75%
Fix ratio: 54% → 30% ↓24pp (improving)
Commits: 32 → 47 ↑47%
Deep sessions: 3 → 5 ↑2
- 若无历史:跳过对比,并附 "First retro recorded — run again next week to see trends."
Step 13 保存复盘历史:
mkdir -p .context/retros
# 统计今日已有快照数,得到下一个序号
today=$(date +%Y-%m-%d)
existing=$(ls .context/retros/${today}-*.json 2>/dev/null | wc -l | tr -d ' ')
next=$((existing + 1))
# 保存为 .context/retros/${today}-${next}.json
快照 JSON schema(文档给出的完整示例):
{
"date": "2026-03-08",
"window": "7d",
"metrics": {
"commits": 47,
"contributors": 3,
"prs_merged": 12,
"insertions": 3200,
"deletions": 800,
"net_loc": 2400,
"test_loc": 1300,
"test_ratio": 0.41,
"active_days": 6,
"sessions": 14,
"deep_sessions": 5,
"avg_session_minutes": 42,
"loc_per_session_hour": 350,
"feat_pct": 0.40,
"fix_pct": 0.30,
"peak_hour": 22,
"ai_assisted_commits": 32
},
"authors": {
"Garry Tan": { "commits": 32, "insertions": 2400, "deletions": 300, "test_ratio": 0.41, "top_area": "browse/" },
"Alice": { "commits": 12, "insertions": 800, "deletions": 150, "test_ratio": 0.35, "top_area": "app/services/" }
},
"version_range": ["1.16.0.0", "1.16.1.0"],
"streak_days": 47,
"tweetable": "Week of Mar 1: 47 commits (3 contributors), 3.2k LOC, 38% tests, 12 PRs, peak: 10pm",
"greptile": {
"fixes": 3,
"fps": 1,
"already_fixed": 2,
"signal_pct": 83
}
}
三个条件字段:greptile 仅当 ~/.gstack/greptile-history.md 存在且窗口内有数据时包含;backlog 仅当 TODOS.md 存在时包含(字段:total_open、p0_p1、p2、completed_this_period、added_this_period);test_health 仅当命令 10 返回 >0(存在测试文件)时包含(字段:total_test_files、tests_added_this_period、regression_test_commits、test_files_changed)。无数据时整体省略字段,不写空值。
十一、Step 14:叙事输出结构
复盘的最终输出直接写进对话(唯一写盘的文件就是 .context/retros/ 快照)。文档规定输出以一条 Tweetable summary 开头:
Week of Mar 1: 47 commits (3 contributors), 3.2k LOC, 38% tests, 12 PRs, peak: 10pm | Streak: 47d
然后是固定的章节骨架:
- Summary Table(Step 2 的指标表)
- Trends vs Last Retro(Step 12,首份复盘跳过)
- Time & Session Patterns(Steps 3–4):解读团队级时间模式——最有产出的时段及原因、会话在变长还是变短、团队日均有效编码时长、成员是同时段工作还是轮班
- Shipping Velocity(Steps 5–7):提交类型 mix 揭示什么、PR 规模分布揭示的发货节奏、fix-chain 检测(同一子系统上连续 fix commit 序列)、版本 bump 纪律
- Code Quality Signals:test LOC ratio 趋势、热点分析(同样的文件是否持续 churn)、Greptile 信号比及趋势
- Test Health:测试文件总数、本期新增、
test(qa):/test(design):/test: coverage回归 commit 列表、与上一份快照的 delta;test ratio 低于 20% 时标记为成长区——"100% test coverage is the goal. Tests make vibe coding safe." - Plan Completion:从
~/.gstack/projects/$SLUG/*-reviews.jsonl中提取本期/ship的计划完成数据:
eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)"
cat ~/.gstack/projects/$SLUG/*-reviews.jsonl 2>/dev/null | grep '"skill":"ship"' | grep '"plan_items_total"' || echo "NO_PLAN_DATA"
统计有计划发货的分支数、平均完成率(plan_items_done 总和 / plan_items_total 总和)、最常被跳过的条目类别;无数据时静默跳过。
- Focus & Highlights(Step 8):focus score 解读 + Ship of the week 特写
- Your Week(个人深挖):个人 commit 数/LOC/test ratio、会话模式与峰值时段、聚焦领域、最大发货,加两块结论——"What you did well"(2–3 条,锚定 commit)与 "Where to level up"(1–2 条,具体可执行)
- Team Breakdown(每位队友,按 commit 降序):What they shipped / Praise / Opportunity for growth 三段式,附文档给的范例措辞;若大量 commit 带 AI 的
Co-Authored-Bytrailer,中性地报告 "N% of commits were AI-assisted" 团队指标,不带评判 - Top 3 Team Wins:窗口内全团队 3 个最高影响发货,各说明是什么、谁发的、为什么重要(产品/架构影响)
- 3 Things to Improve:具体、可执行、锚定 commit,混合个人与团队层面,句式为 "to get even better, the team could..."
- 3 Habits for Next Week:小而实际,每个 <5 分钟即可采纳,至少一条面向团队(如 "review each other's PRs same-day")
- Week-over-Week Trends(Step 10,适用时)
十二、Global Retrospective 模式(/retro global)
/retro global 走完全不同的流程,不要求在 git 仓库内,跨所有 AI 编码工具做跨项目复盘。
Global Step 1 时间窗口:与单仓相同的午夜对齐逻辑,默认 7d,global 后的第二个参数是窗口。
Global Step 2 运行发现脚本,按固定回退链定位二进制:
DISCOVER_BIN=""
[ -x ~/.claude/skills/gstack/bin/gstack-global-discover ] && DISCOVER_BIN=~/.claude/skills/gstack/bin/gstack-global-discover
[ -z "$DISCOVER_BIN" ] && [ -x .claude/skills/gstack/bin/gstack-global-discover ] && DISCOVER_BIN=.claude/skills/gstack/bin/gstack-global-discover
[ -z "$DISCOVER_BIN" ] && which gstack-global-discover >/dev/null 2>&1 && DISCOVER_BIN=$(which gstack-global-discover)
[ -z "$DISCOVER_BIN" ] && [ -f bin/gstack-global-discover.ts ] && DISCOVER_BIN="bun run bin/gstack-global-discover.ts"
echo "DISCOVER_BIN: $DISCOVER_BIN"
找不到就提示 "Discovery script not found. Run bun run build in the gstack directory to compile it." 并停止。仓库侧的佐证:scripts/build.sh 中确有 bun build --compile bin/gstack-global-discover.ts --outfile bin/gstack-global-discover 的编译步骤。发现脚本的源码是 bin/gstack-global-discover.ts——从源码结构看,它发现 Claude Code、Codex CLI、Gemini CLI 三家的 AI 编码会话,把每个会话的工作目录解析到 git 仓库,按归一化 remote URL 去重,并向 stdout 输出结构化 JSON。其类型定义(DiscoveryResult)与技能文档的解析预期严格对应:
interface DiscoveryResult {
window: string;
start_date: string;
repos: Repo[]; // { name, remote, paths: string[], sessions: { claude_code, codex, gemini } }
tools: {
claude_code: { total_sessions: number; repos: number };
codex: { total_sessions: number; repos: number };
gemini: { total_sessions: number; repos: number };
};
total_sessions: number;
total_repos: number;
}
它的 CLI 接受 --since <window>(必填,格式 \d+(d|h|w),与 /retro 参数同构)和 --format json|summary;其中天/周窗口在源码里同样是午夜对齐(setHours(0, 0, 0, 0)),与文档规则一致。
运行方式:$DISCOVER_BIN --since "<window>" --format json 2>/tmp/gstack-discover-stderr,stderr 文件读诊断信息,stdout 解析 JSON。若 total_sessions 为 0:输出 "No AI coding sessions found in the last . Try a longer window: /retro global 30d" 并停止。
Global Step 3 逐仓 git log:对发现结果 repos 数组中的每个仓库,取 paths[] 中第一个有效路径(目录存在且有 .git/),无有效路径则跳过并注明。local-only 仓库(remote 以 local: 开头)跳过 fetch,用 git log HEAD 代替 git log origin/$DEFAULT;有 remote 的仓库先 git -C <path> fetch origin --quiet,再按 git symbolic-ref refs/remotes/origin/HEAD → main/master → git rev-parse --abbrev-ref HEAD 的顺序探测默认分支,然后执行与单仓 Step 1 同构的四条命令(commits with stats、时间戳排序、shortlog、PR 号提取)。失败仓库(路径被删、网络错误)跳过并注明 "N repos could not be reached."
Global Step 4 全局发货 streak:每个仓库取近 365 天的 commit 日期集合,跨仓库取并集后从今天往前数"任意仓库有 commit"的连续天数;streak 到 365 天时显示 "365+ days"。
Global Step 5 上下文切换指标:按日期分组,统计每天有几个不同仓库有 commit,报告日均仓库数、峰值、哪些天是聚焦日(1 仓库)vs 碎片日(3+ 仓库)。
Global Step 6 分工具生产力模式:从发现 JSON 分析哪个 AI 工具用于哪些仓库(专属 vs 共享)、每工具会话数、行为模式(如 "Codex used exclusively for myapp, Claude Code for everything else")。
Global Step 7 聚合叙事:先输出可分享的个人卡片,再输出完整深挖。个人卡片只含当前用户自己的数据(用 git config user.name 过滤所有 per-repo git 数据后跨仓库聚合),设计目标是截图友好——只用左边界(文档解释 LLM 无法可靠对齐右边界)、仓库名按最长名补齐对齐、永不截断仓库名、LOC 用 "k" 格式:
╔═══════════════════════════════════════════════════════════════
║ [USER NAME] — Week of [date]
╠═══════════════════════════════════════════════════════════════
║ [N] commits across [M] projects
║ +[X]k LOC added · [Y]k LOC deleted · [Z]k net
║ [N] AI coding sessions (CC: X, Codex: Y, Gemini: Z)
║ [N]-day shipping streak 🔥
║ PROJECTS
║ [repo_name_full] [N] commits +[X]k LOC [solo/team]
║ SHIP OF THE WEEK
║ [PR title] — [LOC] lines across [N] files
║ TOP WORK
║ • [1-line description of biggest theme]
║ Powered by gstack
╚═══════════════════════════════════════════════════════════════
卡片规则:只展示用户有 commit 的仓库(0 commit 跳过)、按用户 commit 数降序、Ship of the Week 取跨所有仓库的最高 LOC PR、Top Work 是从 commit message 综合出的主题而非罗列单条 commit(文档示例:写 "Built /retro global — cross-project retrospective with AI session discovery",而不是 "feat: gstack-global-discover" + "feat: /retro global template" 两条)、卡片必须自包含(只看这个 block 就能理解用户的一周)、个人 streak 用 --author 过滤后单独计算。
卡片之后是 Global Engineering Retro 深挖部分:All Projects Overview 总表(活跃项目数、总 commit/LOC、AI 会话数及分工具、活跃天数、全局 streak、日均上下文切换)、Per-Project Breakdown(按 commit 降序,每仓含占比、LOC、PR 数、top contributor、AI 会话分布,以及 "Your contributions" 子块——个人 commit 占比/LOC/关键工作/类型 mix/本仓最大发货;solo 项目显示 "Solo project — all commits are yours.",用户 0 commit 则显示 "No commits this period — [N] AI sessions only.")、Cross-Project Patterns(用你的 commit 算时间分配)、Tool Usage Analysis、Ship of the Week (Global)、3 Cross-Project Insights(单仓复盘看不到的东西)、3 Habits for Next Week。
Global Step 8 加载历史:ls -t ~/.gstack/retros/global-*.json | head -5。只与 window 值相同的上一份对比(7d vs 7d);窗口不同则跳过并注明。有匹配则输出 Trends vs Last Global Retro 增量表(总 commit、LOC、会话、streak、日均上下文切换);没有则附 "First global retro recorded — run again next week to see trends."
Global Step 9 保存快照:写到 ~/.gstack/retros/global-${today}-${next}.json(注意与单仓的 .context/retros/ 不同),schema:
{
"type": "global",
"date": "2026-03-21",
"window": "7d",
"projects": [
{
"name": "gstack",
"remote": "<detected from git remote get-url origin, normalized to HTTPS>",
"commits": 47,
"insertions": 3200,
"deletions": 800,
"sessions": { "claude_code": 15, "codex": 3, "gemini": 0 }
}
],
"totals": {
"commits": 182,
"insertions": 15300,
"deletions": 4200,
"projects": 5,
"active_days": 6,
"sessions": { "claude_code": 48, "codex": 8, "gemini": 3 },
"global_streak_days": 52,
"avg_context_switches_per_day": 2.1
},
"tweetable": "Week of Mar 14: 5 projects, 182 commits, 15.3k LOC | CC: 48, Codex: 8, Gemini: 3 | Focus: gstack (58%) | Streak: 52d"
}
十三、Compare 模式
/retro compare(或 /retro compare 14d)的五步流程:
- 用午夜对齐起始日期计算当前窗口(默认 7d)的指标(例:今天 2026-03-18、窗口 7d →
--since="2026-03-11T00:00:00"); - 用
--since+--until计算紧邻的前一个等长窗口(同样午夜对齐避免重叠,例:--since="2026-03-04T00:00:00" --until="2026-03-11T00:00:00"); - 输出带 delta 与箭头的并排对比表;
- 简短叙事突出最大改善与最大退步;
- 只保存当前窗口的快照到
.context/retros/(与普通运行相同),不持久化前窗指标。
十四、Tone 与硬性规则
文档对复盘的语气有明确规范:encouraging but candid(鼓励但坦率,不哄)、一切结论锚定真实 commit/代码、跳过泛泛表扬、改进建议表述为 "leveling up" 而非批评、表扬要像 1:1 里真实会说的话、成长建议要像投资建议("this is worth your time because...",不是 "you failed at...")、绝不在队友之间做负面比较(每人的小节独立成立)、总输出约 3000–4500 词(略长以容纳团队小节)、数据用 markdown 表格和代码块、叙事用 prose、直接输出到对话(唯一例外是 .context/retros/ 快照)。
"Important Rules" 一节的硬约束:
- 所有叙事直接输出给用户;唯一写盘文件是
.context/retros/JSON 快照; - 所有 git 查询用
origin/<default>(本地 main 可能陈旧); - 时间戳一律本地时区,不覆盖
TZ; - 窗口内 0 个 commit 时明说并建议换窗口;
- LOC/hour 四舍五入到最近 50;
- merge commit 视为 PR 边界;
- 不读 CLAUDE.md 或其他文档——该技能自包含;
- 首次运行(无历史)时优雅跳过对比章节;
- Global 模式:不要求在 git 仓库内;快照存
~/.gstack/retros/;未安装的 AI 工具优雅跳过;只与同窗口值的历史对比;streak 到 365d 上限显示 "365+ days"。
十五、收尾:learnings 回写与数据落点
复盘过程中发现的非显然模式、坑或架构洞见,通过 learnings 机制回写,供后续会话复用:
~/.claude/skills/gstack/bin/gstack-learnings-log '{"skill":"retro","type":"TYPE","key":"SHORT_KEY","insight":"DESCRIPTION","confidence":N,"source":"SOURCE","files":["path/to/relevant/file"]}'
- Types:
pattern(可复用做法)、pitfall(不该做什么)、preference(用户声明)、architecture(结构性决策)、tool(库/框架洞见)、operational(项目环境/CLI/流程知识) - Sources:
observed(在代码里验证过)、user-stated(用户告知)、inferred(AI 推断)、cross-model(Claude 与 Codex 均确认) - Confidence 1–10:代码里验证过的 observed 模式给 8–9,不确定的推断给 4–5,用户明确声明的偏好给 10
- files 字段关联具体文件路径,用于 staleness 检测(文件被删后可标记该 learning 过期)
- 只记录真正的发现:判断标准是"这条洞见能否在未来的会话里省时间?"
至此形成完整闭环:/retro 的 frontmatter context_queries 在下次启动时自动拉回 .context/retros/*.json、timeline.jsonl、learnings.jsonl,配合 Step 12 的历史对比与 Prior Learnings 检索,让每周复盘不只是孤立报告,而是一条可累积、可对比、可验证的趋势线。
延伸阅读
- 技能完整定义(生成产物):retro/SKILL.md
- 技能模板(唯一可编辑源,含
{{PREAMBLE}}、{{LEARNINGS_SEARCH}}等占位符):retro/SKILL.md.tmpl - Step 0.5 守卫的回归测试(#1624):test/regression-1624-retro-stale-base.test.ts
- global 模式发现脚本源码:bin/gstack-global-discover.ts、编译入口:scripts/build.sh、行为测试:test/global-discover.test.ts
- 技能总览与
/retro章节:docs/skills.md
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0624
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00