| name | retro |
| description | Engineering retrospective from git metrics. Reports deployment frequency, release cycle span, churn hotspots, backlog health. Outputs narrative report with 3+ actionable items. Flags: --since, --path, explicit range argument.
|
| codesift_tools | {"always":["analyze_project","index_status","index_folder","index_file","plan_turn","analyze_hotspots","changed_symbols","diff_outline","get_file_tree","search_text"],"by_stack":{}} |
zuvo:retro
Generate an engineering retrospective from git history, backlog state, and skill usage trends. Produces a narrative report with actionable items.
Scope: Post-release or periodic retrospective covering shipping velocity, code churn, backlog health, and quality trends.
Out of scope: Actually fixing issues (use the suggested zuvo commands in the Actionable Items section).
Argument Parsing
| Input | Action |
|---|
| (empty) | Auto-detect window from memory/last-ship.json or last two git tags |
<range> | Explicit git range, e.g., v1.0.0..v1.2.0 |
--since <tag> | Start of retrospective window (open-ended, through HEAD) |
--path <dir> | Scope git analysis to a subdirectory (required for monorepos) |
Environment Compatibility
Read ../../shared/includes/env-compat.md for agent dispatch patterns, path resolution, and progress tracking across Claude Code, Codex, and Cursor.
Mandatory File Loading
CORE FILES LOADED:
1. ../../shared/includes/env-compat.md -- READ
2. ../../shared/includes/run-logger.md -- READ
Both files must be read before proceeding. If either is missing, note "DEGRADED -- [file] unavailable" in the report and continue with reduced capability.
Phase 0: Window Detection
Determine the retrospective window in this priority order:
- Explicit argument — if
<range> was provided (e.g., v1.0.0..v1.2.0), use it directly.
--since <tag> — if provided, window is <tag>..HEAD. No --until flag is defined.
memory/last-ship.json — read the file if it exists. Use the range field (SHA-based, e.g., "abc1234..def5678"). If the artifact uses a legacy version-based range, fall back to it.
- Last two git tags — run
git describe --tags --abbrev=0 twice (with --exclude on first result) to derive <prev-tag>..<latest-tag>.
- Fallback — if fewer than two tags exist, use the last 30 commits: derive range as
HEAD~30..HEAD.
Count commits in window:
git log --oneline <range> | wc -l
Edge Case E13 — Insufficient History
If commit count is < 10: output "Insufficient history for statistical metrics" and produce a qualitative-only report. Skip Phases 1 and 3 (no git metrics, no skill trends to aggregate). Use Phase 2 (backlog health) as the sole data source. Still produce Phase 4 actionable items and Phase 5 report.
Edge Case E14 — Monorepo Detection
Check for monorepo signals in the project root:
turbo.json
nx.json
pnpm-workspace.yaml
If any of these files exist AND no --path argument was provided:
- First, try auto-detection: run
git diff --name-only <range> and extract unique top-level directories matching workspace package paths.
- If only ONE package was touched: auto-scope to that package with
[AUTO-DECISION]: scoped to <package>.
- If multiple packages were touched:
- Interactive: list the affected packages and ask the user to pick one or confirm full-repo analysis.
- Non-interactive:
[AUTO-DECISION]: monorepo detected, analyzing all affected packages. Results may be noisy — re-run with --path for focused analysis. Proceed with full-repo analysis.
If --path was provided: scope all git log commands with -- <path> and note the scope in the report.
Phase 1: Git Metrics
Run these commands against the resolved <range> (append -- <path> if --path was provided).
Deployment Frequency
Count release tags within the window, not arbitrary tags.
Counting method:
git tag --sort=creatordate --merged HEAD | grep -E '^v[0-9]' | while read tag; do
TAG_DATE=$(git log -1 --format="%ai" "$tag")
done
Priority for identifying release tags:
- Tags matching a semver-like pattern (
v*.*.*, v[0-9]*)
- Tags referenced by
memory/last-ship.json (newTag, previousTag) — cross-reference, not sole source
- If no release tag pattern can be identified, report:
Deployment frequency unavailable — no reliable release tag pattern detected
Ignore unrelated tags (experimental, package-local, scratch tags). memory/last-ship.json covers only the most recent release; for multi-release windows, the git tag scan is the primary data source.
Release Cycle Span
Estimate the time span covered by the release window:
git log --format="%ai %H" <range>
Use the earliest commit date in the window as the start and the latest tag date as the end. Report as: N days (earliest commit → latest tag). Note: this is the window span, not per-PR lead time — exact branch-to-merge lead time requires per-PR analysis with gh pr list.
Churn Hotspots
Find the top 5 most-changed files in the window:
git log --name-only --pretty=format: <range> | sort | uniq -c | sort -rn | head -5
Filter out empty lines. Report as a ranked list: filename (N changes).
Phase 2: Backlog Health
Read memory/backlog.md if it exists — at the MAIN checkout root, resolved per ../../shared/includes/backlog-protocol.md "Where the Backlog Lives" (in a linked worktree a CWD-relative read silently sees no backlog).
If the file does not exist: note "No backlog tracked. Run zuvo:review or zuvo:code-audit to populate." Skip all backlog metrics.
If the file exists, count:
- Open items — rows where Status =
OPEN
- Critical items — OPEN rows where Severity =
CRITICAL
- Resolved in window — rows where Status =
RESOLVED and the Resolved/Updated date (not Added date) falls within the window. If backlog.md has no Resolved date column, skip this metric and note: "Resolved-in-window metric unavailable — backlog lacks Resolved date column."
- Added in window — rows where Added date falls within the retrospective window
Also identify the oldest unresolved item (earliest Added date among OPEN rows) and report its ID and age in days.
Phase 3: Skill Usage Trends
Read the skill usage log using the environment-aware path from run-logger.md (primary: ~/.zuvo/runs.log, Codex App fallback: memory/zuvo-runs.log).
If the file does not exist at either path: note "No skill usage history found." Skip this section.
If the file exists, parse each line by splitting on tab characters (\t):
DATE parsing: handle both T...Z (with Z suffix) and T... (without Z) formats as UTC.
Filter to entries where:
PROJECT matches the current project directory basename
DATE falls within the retrospective window
From the filtered entries, aggregate:
- Most-used skills — rank by frequency
- Average CQ score — parse
CQ_SCORE field, skip - entries, average the numerator/denominator separately
- Average Q score — same approach as CQ
- Pass/fail ratio — count PASS vs FAIL vs WARN verdicts
- Branch distribution: [branch: N runs, ...] (e.g.,
main: 8, feature/x: 2) — only display when at least one 11-field entry exists in the filtered window
Phase 4: Actionable Items
Generate at least 3 specific, actionable items. Each item must:
- Reference a specific file, directory, or backlog item (not generic advice)
- Include a specific zuvo command
- State the reason derived from the data
Derivation rules (priority order):
- High-churn files — for each file in the churn hotspot list with N ≥ 5 changes: check whether a corresponding test file exists. If no test file found:
zuvo:write-tests <file> with reason "high-churn, N changes, no test coverage found".
- Churn instability — for files with N ≥ 5 changes that already have tests:
zuvo:refactor <file> with reason "N changes suggest instability or unclear responsibilities".
- Critical backlog items — for each OPEN CRITICAL item:
zuvo:backlog fix <ID> with reason "critical debt item open N days".
- Old open items — if oldest open item is > 14 days:
zuvo:backlog prioritize with reason "oldest item open N days, prioritization overdue".
- Low CQ scores — if average CQ score < 23/29:
zuvo:code-audit <top-churn-directory> with reason "CQ average N/29 below threshold".
- Low Q scores — if average Q score < 14/19:
zuvo:write-tests <top-churn-directory> with reason "Q average N/19 suggests test quality gaps".
If fewer than 3 items can be derived from the data, supplement with: "Run zuvo:code-audit . to establish a quality baseline" or "Run zuvo:test-audit . to identify test gaps."
Phase 5: Report and Output
Write Report File
Create the report using a readable suffix:
- Prefer tag-based names when
previousTag / newTag are known:
zuvo/reports/retro-YYYY-MM-DD-<previousTag>_<newTag>.md
- Otherwise fall back to a shortened SHA-based suffix (first 7 chars of
baseSha and releaseCommitSha from last-ship.json):
zuvo/reports/retro-YYYY-MM-DD-<baseSha7>_<releaseCommitSha7>.md
This prevents collisions and keeps filenames readable. If zuvo/reports/ does not exist, create it.
Use this exact structure:
# Engineering Retrospective — YYYY-MM-DD
## Summary
[Tweetable one-liner: period, commits, releases, key metric]
## Shipping Velocity
- **Window:** <range> (<N> days, <N> commits)
- **Deployment frequency:** N releases in period (frequency: 1 per X days)
- **Release cycle span:** N days (earliest commit → latest tag)
- **Commits:** N total, N/day average
## Churn Hotspots
[Top 5 most-changed files with change count. Note if test file was found for each.]
| Rank | File | Changes | Has Tests |
|------|------|---------|-----------|
| 1 | src/orders/service.ts | 12 | No |
| 2 | src/auth/guard.ts | 8 | Yes |
## Backlog Health
- **Open items:** N (N critical, N high)
- **Added this period:** N
- **Resolved this period:** N
- **Oldest unresolved:** [item ID] — open N days
## Quality Trends
- **CQ scores:** avg N/29 ([N runs in period], or "no runs.log data")
- **Q scores:** avg N/19 ([N runs in period], or "no runs.log data")
- **Most-used skills:** [skill1 (N runs), skill2 (N runs)]
- **Pass/fail ratio:** N PASS, N WARN, N FAIL
## Actionable Items
1. `zuvo:write-tests src/orders/` — high-churn (12 changes), no test file found
2. `zuvo:refactor src/auth/guard.ts` — 8 changes suggest instability
3. `zuvo:backlog fix BD-007` — critical debt item open 21 days
## Comparison vs Prior Retro
[Delta table or "First retro — run again next period for trends"]
Prior Retro Comparison
Check for existing zuvo/reports/retro-*.md files. If one or more exist, compare with the most recent:
| Metric | Prior | Current | Delta |
|---|
| Deployment frequency | N/period | N/period | +/-N |
| Release cycle span | N days | N days | +/-N days |
| Open backlog items | N | N | +/-N |
| Avg CQ score | N/29 | N/29 | +/-N |
| Avg Q score | N/19 | N/19 | +/-N |
If no prior retro exists: note "First retro — run again next period for trends."
Terminal Output Block
Print the RETRO COMPLETE block after writing the report:
RETRO COMPLETE
Window: v1.1.0..v1.2.0 (14 days, 47 commits)
Releases: 2 in period (frequency: 1 per week)
Cycle span: 14 days (earliest commit → latest tag)
Hotspots: src/orders/service.ts (12 changes), src/auth/guard.ts (8 changes)
Backlog: +5 added, -3 resolved, 12 open (2 critical)
Report: zuvo/reports/retro-2026-03-28-v1.1.0_v1.2.0.md
Actions:
1. zuvo:write-tests src/orders/ — high-churn, low coverage
2. zuvo:refactor src/auth/guard.ts — 8 changes suggest instability
3. zuvo:backlog fix BD-007 — critical debt item open 21 days
Run: <ISO-8601-Z> retro <project> - - <VERDICT> - 6-phase <NOTES> <BRANCH> <SHA7>
After printing this block, append the `Run:` line value (without the `Run: ` prefix) to the log file path resolved per `run-logger.md`.
If E13 (insufficient history) was triggered, show:
RETRO COMPLETE [QUALITATIVE ONLY — <10 commits in window]
Window: HEAD~30..HEAD (fallback — fewer than 10 commits found)
Backlog: +N added, -N resolved, N open (N critical)
Report: zuvo/reports/retro-YYYY-MM-DD-<suffix>.md
Actions:
1. [derived from backlog only]
Run: <ISO-8601-Z> retro <project> - - <VERDICT> - qualitative <NOTES> <BRANCH> <SHA7>
After printing this block, append the `Run:` line value (without the `Run: ` prefix) to the log file path resolved per `run-logger.md`.