首页
/ gstack /retro 完整指南:从 Git 数据生成团队感知型每周工程复盘

gstack /retro 完整指南:从 Git 数据生成团队感知型每周工程复盘

2026-09-06 17:24:31作者:董斯意

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: retroversion: 2.0.0preamble-tier: 2
  • allowed-tools: Bash、Read、Write、Glob、AskUserQuestion
  • 触发词(triggers):weekly retrowhat did we shipengineering 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 跨项目复盘 + 显式窗口

窗口解析规则有三个要点:

  1. 零默认与本地时区:无参数默认 7 天;所有时间一律用系统默认时区报告(明确禁止设置 TZ)。
  2. 午夜对齐(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"
  3. 参数校验:若参数不匹配"数字 + 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 diffgit loggit fetchgit merge 命令中的 <default> 都替换为它):

git remote get-url origin 2>/dev/null
  • URL 含 github.comGitHub
  • URL 含 gitlabGitLab
  • 其他情况用 CLI 探测:gh auth status 成功 → GitHub(覆盖 GitHub Enterprise);glab auth status 成功 → GitLab(覆盖自建实例);都失败 → unknown(仅用 git 原生命令)

基线分支的探测优先级:

  • GitHubgh pr view --json baseRefName -q .baseRefNamegh repo view --json defaultBranchRef -q .defaultBranchRef.name
  • GitLabglab mr view -F json 提取 target_branchglab repo view -F json 提取 default_branch
  • Git 原生回退git symbolic-ref refs/remotes/origin/HEAD 去掉前缀 → git rev-parse --verify origin/main 则用 maingit 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_PROJECTunset(首次运行):通过 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, run git fetch origin <default> manually and re-run /retro.",并停住直到用户解决。
  • 否则输出 "RETRO_GUARD: latest commit <DATE> within window — proceeding."

两条细节值得强调:

  1. "今天"的来源:文档明确指示模型从会话提醒中用户可见的 ## currentDate 标签取当前日期,绝不用 date 命令——容器化 harness 里系统时钟可能偏好几个小时。若模型无法可靠计算"今天",必须在此处停下并通过 AskUserQuestion 询问用户,而不是继续。
  2. 跳过路径也要披露skip-no-remoteskip-detachedwarn-fetch-failed 都继续进入 Step 1,但要求把原因写成一行 stderr,让复盘叙事携带披露(如"offline run, window not freshness-verified"),而不是静默地错误报告。

这套守卫不是"软约定",而是被静态回归测试钉死的构建不变量。test/regression-1624-retro-stale-base.test.tsretro/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 存在):按日期过滤窗口内条目,按类型计数 fixfpalready-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_openp0_p1p2completed_this_periodadded_this_period);test_health 仅当命令 10 返回 >0(存在测试文件)时包含(字段:total_test_filestests_added_this_periodregression_test_commitstest_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

然后是固定的章节骨架:

  1. Summary Table(Step 2 的指标表)
  2. Trends vs Last Retro(Step 12,首份复盘跳过)
  3. Time & Session Patterns(Steps 3–4):解读团队级时间模式——最有产出的时段及原因、会话在变长还是变短、团队日均有效编码时长、成员是同时段工作还是轮班
  4. Shipping Velocity(Steps 5–7):提交类型 mix 揭示什么、PR 规模分布揭示的发货节奏、fix-chain 检测(同一子系统上连续 fix commit 序列)、版本 bump 纪律
  5. Code Quality Signals:test LOC ratio 趋势、热点分析(同样的文件是否持续 churn)、Greptile 信号比及趋势
  6. Test Health:测试文件总数、本期新增、test(qa): / test(design): / test: coverage 回归 commit 列表、与上一份快照的 delta;test ratio 低于 20% 时标记为成长区——"100% test coverage is the goal. Tests make vibe coding safe."
  7. 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 总和)、最常被跳过的条目类别;无数据时静默跳过。

  1. Focus & Highlights(Step 8):focus score 解读 + Ship of the week 特写
  2. Your Week(个人深挖):个人 commit 数/LOC/test ratio、会话模式与峰值时段、聚焦领域、最大发货,加两块结论——"What you did well"(2–3 条,锚定 commit)与 "Where to level up"(1–2 条,具体可执行)
  3. Team Breakdown(每位队友,按 commit 降序):What they shipped / Praise / Opportunity for growth 三段式,附文档给的范例措辞;若大量 commit 带 AI 的 Co-Authored-By trailer,中性地报告 "N% of commits were AI-assisted" 团队指标,不带评判
  4. Top 3 Team Wins:窗口内全团队 3 个最高影响发货,各说明是什么、谁发的、为什么重要(产品/架构影响)
  5. 3 Things to Improve:具体、可执行、锚定 commit,混合个人与团队层面,句式为 "to get even better, the team could..."
  6. 3 Habits for Next Week:小而实际,每个 <5 分钟即可采纳,至少一条面向团队(如 "review each other's PRs same-day")
  7. 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/HEADmain/mastergit 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)的五步流程:

  1. 用午夜对齐起始日期计算当前窗口(默认 7d)的指标(例:今天 2026-03-18、窗口 7d → --since="2026-03-11T00:00:00");
  2. --since + --until 计算紧邻的前一个等长窗口(同样午夜对齐避免重叠,例:--since="2026-03-04T00:00:00" --until="2026-03-11T00:00:00");
  3. 输出带 delta 与箭头的并排对比表;
  4. 简短叙事突出最大改善与最大退步;
  5. 只保存当前窗口的快照.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"]}'
  • Typespattern(可复用做法)、pitfall(不该做什么)、preference(用户声明)、architecture(结构性决策)、tool(库/框架洞见)、operational(项目环境/CLI/流程知识)
  • Sourcesobserved(在代码里验证过)、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/*.jsontimeline.jsonllearnings.jsonl,配合 Step 12 的历史对比与 Prior Learnings 检索,让每周复盘不只是孤立报告,而是一条可累积、可对比、可验证的趋势线。

延伸阅读

登录后查看全文
热门项目推荐
相关项目推荐