fix: clarify retro workflow and evaluate compare instructions

Include compare mode in the frontier judge excerpt, define metric sources and snapshot ordering, and preserve the existing prompt-size budget.

Co-authored-by: OpenAI Codex <noreply@openai.com>
This commit is contained in:
Garry Tan
2026-09-09 05:08:30 +00:00
co-authored by OpenAI Codex
parent eb4fd65ed3
commit 2e624adf20
9 changed files with 182 additions and 176 deletions
+1
View File
@@ -11,6 +11,7 @@
- Frontier judges have enough output budget for thinking and JSON. Workflow evals read generated sections in execution order, without duplicated content.
- Ship and plan reviews keep approval gates with the parent agent, preserve readiness checks, and use project-native test commands. QA scoring and monitoring rules are explicit; Aside drive options wait for a successful readiness probe.
- Benchmark timing uses the navigation entry's actual fields. Deploy setup collects missing configuration, and failed vendored upgrades restore their backup instead of deleting it.
- Retro reports distinguish verified merges from PR references, use the session date for snapshots, and define report ordering. The quality eval includes compare-mode instructions instead of cutting them off.
## [1.81.0.0] - 2026-09-06
+82 -96
View File
@@ -459,7 +459,7 @@ branch name wherever the instructions say "the base branch" or `<default>`.
# /retro — Weekly Engineering Retrospective
Generates a comprehensive engineering retrospective analyzing commit history, work patterns, and code quality metrics. Team-aware: identifies the user running the command, then analyzes every contributor with per-person praise and growth opportunities. Designed for a senior IC/CTO-level builder using Claude Code as a force multiplier.
Analyze commit history, work patterns, and code quality for the current user and every contributor, with evidence-backed praise and growth opportunities.
## User-invocable
When the user types `/retro`, run this skill.
@@ -504,7 +504,9 @@ Usage: /retro [window | compare | global]
/retro global 14d — cross-project retro with explicit window
```
**If the first argument is `global`:** Skip the normal repo-scoped retro (Steps 1-14). Instead, follow the **Global Retrospective** flow at the end of this document. The optional second argument is the time window (default 7d). This mode does NOT require being inside a git repo.
**Routing:** `global` skips all repo-scoped steps, including Prior Learnings, Step 0.5, and post-report capture; follow **Global Retrospective Mode** (no git repo required). `compare` follows **Compare Mode**. Both accept an optional window (default 7d). Otherwise run the repo-scoped flow below.
`<default>` is the base branch from the preceding **Step 0: Detect platform and base branch**. `<today>` is the session-reminder date; reuse it in all snapshot filenames, never re-read the clock.
## Prior Learnings
@@ -557,20 +559,20 @@ Remember whether the fetch succeeded — the stale-base guard in Step 1 only BLO
### Step 1: Gather Metrics (one command)
All raw data gathering and metric computation runs through `gstack-retro-metrics` — one command instead of a dozen git pipelines. Substitute the base branch detected in Step 0 and the midnight-aligned start computed above:
Run `gstack-retro-metrics` with the detected base branch and computed start:
```bash
_RM="$HOME/.claude/skills/gstack/bin/gstack-retro-metrics"
[ -x "$_RM" ] || _RM=".claude/skills/gstack/bin/gstack-retro-metrics"
"$_RM" --base "<default>" --since "<since>" \
|| echo "RETRO_METRICS: unavailable — stale install (compute metrics manually from the steps below)"
|| echo "RETRO_METRICS: unavailable — stale install (read the helper source for manual computation)"
```
Read the labeled `METRIC_NAME: value` lines — they feed every step below. **Degraded mode:** if `RETRO_METRICS_PROTO: 1` is missing from the output, the install is stale; compute each metric manually with git commands, using the metric definitions in Steps 2-11 as the spec.
Read the `METRIC_NAME: value` lines. **Degraded mode:** without `RETRO_METRICS_PROTO: 1`, reproduce computations from the installed `bin/gstack-retro-metrics` source, not the presentation steps below. If source or metrics are unavailable, say so; never invent values. Suggest `/gstack-upgrade` to restore the helper.
**Identity:** `USER_NAME` is **"you"** — the person reading this retro. All other authors are teammates. Orient the narrative around this: "your" commits vs teammate contributions.
**Stale-base + bad-today-anchor guard.** The script echoes `GUARD_LATEST_COMMIT: <DATE>` (newest commit on the analyzed ref). If "today" drifts (model session-context error) or the local `origin/<default>` is materially behind the remote, the window returns zero or near-zero commits and the retro would fabricate a coherent-looking narrative from nothing. Evaluate in this order:
**Stale-base + bad-today-anchor guard.** `GUARD_LATEST_COMMIT: <DATE>` is the newest commit on the analyzed ref. A wrong "today" or stale ref can produce an empty window. Evaluate in order:
1. If `GUARD_REMOTE: none` or `GUARD_HEAD: detached` or the Step 0.5 fetch failed: proceed, but carry the disclosure into the narrative ("offline run, window not freshness-verified") rather than silently misreporting.
2. If the Step 0.5 fetch succeeded AND the `GUARD_LATEST_COMMIT` date is **older than (today window-days)**: BLOCK with: "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." Stop the skill until the user resolves.
@@ -617,12 +619,17 @@ Also check `RETRO_REF`: if it is not `origin/<default>` (local-only repo, missin
### Step 2: Compute Metrics
Present these metrics in a summary table, straight from the metric lines:
Most rows come directly from the metric lines. Gather the two shipping outcomes separately before building the table:
- **Merged PRs:** On GitHub, run `gh pr list --state merged --base "<default>" --search "merged:>=<start-date>" --limit 1000 --json number,title,mergedAt`. Filter `mergedAt` to the exact requested window, including its upper bound in compare mode. If the result hits the limit, paginate via the hosting API or label the count partial. On GitLab use the equivalent merged-MR listing. If hosting data is unavailable, show **PRs referenced** = `PRS_REFERENCED` instead; these are not verified merges. Save `prs_merged: null` in that case.
- **Features shipped:** Read CHANGELOG changes on `RETRO_REF` in the same window (`git log <ref> --since "<since>" -p -- CHANGELOG.md`, adding `--until` for the prior window). Combine newly added user-visible capabilities with verified merged PR titles. Deduplicate entries referring to the same capability, excluding fixes, chores, and reverted work. Keep a short list of feature names with their source commit/PR beside the count. If neither source is available, show unavailable, not zero. This is an evidence-backed classification, not a metric-script line.
Use the analyzed ref in the commit-count label (not always `main`). Test health counts **files changed**, not tests added or test cases; use `TEST_FILES_TOTAL`, `TEST_FILES_CHANGED`, and `REGRESSION_TEST_COMMITS` respectively.
| Metric | Value |
|--------|-------|
| **Features shipped** (from CHANGELOG + merged PR titles) | N |
| Commits to main | N |
| Commits to analyzed ref | N |
| Weighted commits (`WEIGHTED_COMMITS`) | N |
| Contributors | N |
| PRs merged | N |
@@ -637,13 +644,10 @@ Present these metrics in a summary table, straight from the metric lines:
| 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 |
| Test Health | N test files · M changed this period · K regression test commits |
**Metric order rationale (V1):** features shipped leads — what users got. Commits
and weighted commits reflect intent-to-ship. Logical SLOC added reflects real
new functionality. Raw LOC is demoted to context because AI inflates it; ten
lines of a good fix is not less shipping than ten thousand lines of scaffold.
See docs/designs/PLAN_TUNING_V1.md §Workstream C.
Lead with user-visible features, then commit and logical-SLOC metrics; raw LOC
is only context, not impact (PLAN_TUNING_V1.md, Workstream C).
Then show a **per-author leaderboard** immediately below, from the `AUTHOR:` lines:
@@ -734,44 +738,17 @@ Report `COMMIT_SIZE_BUCKETS`:
For each contributor (including the current user), the `AUTHOR:` line carries commits, insertions, deletions, test ratio, top areas, commit type mix, and peak hour; `AUTHOR_BIGGEST:` carries their single highest-impact commit. Use the `COMMIT:` lines to anchor everything in actual work.
**For the current user ("You"):** This section gets the deepest treatment. Include all the detail from the solo retro — session analysis, time patterns, focus score. Frame it in first person: "Your peak hours...", "Your biggest ship..."
**For the current user ("You"):** Include session analysis, time patterns, and focus score: "Your peak hours...", "Your biggest ship..."
**For each teammate:** Write 2-3 sentences covering what they worked on and their pattern. Then:
- **Praise** (1-2 specific things): Anchor in actual commits. Not "great work" — say exactly what was good. Examples: "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 specific thing): Frame as a leveling-up suggestion, not criticism. Anchor in actual data. Examples: "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."
- **Praise** (1-2 specifics): cite commits and what was good, not generic praise.
- **Opportunity for growth** (1 specific): tie an actionable suggestion to data, not criticism. Step 14 supplies examples.
**If only one contributor (solo repo):** Skip the team breakdown and proceed as before — the retro is personal.
**Co-author credit:** `COAUTHOR:` lines carry human `Co-Authored-By:` trailers — credit those authors for the commit alongside the primary author. AI co-authors (e.g., `noreply@anthropic.com`) are counted in `AI_ASSISTED_COMMITS` instead — track "AI-assisted commits" as a separate metric, never as a team member.
## Capture Learnings
If you discovered a non-obvious pattern, pitfall, or architectural insight during
this session, log it for future sessions:
```bash
~/.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` (reusable approach), `pitfall` (what NOT to do), `preference`
(user stated), `architecture` (structural decision), `tool` (library/framework insight),
`operational` (project environment/CLI/workflow knowledge).
**Sources:** `observed` (you found this in the code), `user-stated` (user told you),
`inferred` (AI deduction), `cross-model` (both Claude and Codex agree).
**Confidence:** 1-10. Be honest. An observed pattern you verified in the code is 8-9.
An inference you're not sure about is 4-5. A user preference they explicitly stated is 10.
**files:** Include the specific file paths this learning references. This enables
staleness detection: if those files are later deleted, the learning can be flagged.
**Only log genuine discoveries.** Don't log obvious things. Don't log things the user
already knows. A good test: would this insight save time in a future session? If yes, log it.
### Step 10: Week-over-Week Trends (if window >= 14d)
If the time window is 14 days or more, use the `WEEK:` lines (w0 = the week containing the newest commit) to show trends:
@@ -800,12 +777,8 @@ grep -rn "gstack-shortcut(" . \
| grep -vE "gstack-shortcut\(dec-(<|\*)" || true
```
(The exclusions keep docs that merely document the convention — generated
SKILL.md, templates, skill installs — out of the ledger, and the trailing
filter drops placeholder forms like `dec-<id>` / `dec-*` that documentation
uses. Judgment call on what survives: discard any hit that quotes or tests
the convention itself — a sample marker in a checklist, resolver source, or
convention test — rather than marking a real cut corner in this repo's code.)
Discard remaining hits that only document or test the convention (checklists,
resolver examples, tests). Count only real shortcuts in this repo's code.
For each hit, one ledger row: `<file>:<line>, <what was simplified>. ceiling: <X>. upgrade: <Y>.`
- Markers carry a decision id (`dec-<id>`): join against `gstack-decision-search`
@@ -826,7 +799,7 @@ setopt +o nomatch 2>/dev/null || true # zsh compat
ls -t .context/retros/*.json 2>/dev/null
```
**If prior retros exist:** Load the most recent one using the Read tool. Calculate deltas for key metrics and include a **Trends vs Last Retro** section:
**If prior retros exist:** Load the most recent one with the same `window` using the Read tool; if none matches, disclose that and skip historical deltas. Calculate deltas for available key metrics and include a **Trends vs Last Retro** section (in `compare` mode use the freshly computed prior period instead):
```
Last Now Delta
Test ratio: 22% → 41% ↑19pp
@@ -841,19 +814,18 @@ Deep sessions: 3 → 5 ↑2
### Step 13: Save Retro History
After computing all metrics (including streak) and loading any prior history for comparison, save a JSON snapshot:
After computing all metrics (including streak) and loading any prior history for comparison, draft the tweetable summary using the format in Step 14, then save a JSON snapshot. The Step 14 narrative must reuse this exact summary. `streak_days` is the live **team** streak from Step 11 (0 when broken); put the personal streak in `user_streak_days`.
```bash
mkdir -p .context/retros
```
Determine the next sequence number for today (substitute the actual date for `$(date +%Y-%m-%d)`):
Determine the next unused sequence number for today (substitute the session-reminder date for `<today>`):
```bash
setopt +o nomatch 2>/dev/null || true # zsh compat
# Count existing retros for today to get next sequence number
today=$(date +%Y-%m-%d)
existing=$(ls .context/retros/${today}-*.json 2>/dev/null | wc -l | tr -d ' ')
next=$((existing + 1))
today="<today>"
next=1
while [ -e ".context/retros/${today}-${next}.json" ]; do next=$((next + 1)); done
# Save as .context/retros/${today}-${next}.json
```
@@ -887,6 +859,7 @@ Use the Write tool to save the JSON file with this schema:
},
"version_range": ["1.16.0.0", "1.16.1.0"],
"streak_days": 47,
"user_streak_days": 32,
"tweetable": "Week of Mar 1: 47 commits (3 contributors), 3.2k LOC, 38% tests, 12 PRs, peak: 10pm",
"greptile": {
"fixes": 3,
@@ -903,7 +876,6 @@ Include test health data in the JSON when test files exist:
```json
"test_health": {
"total_test_files": 47,
"tests_added_this_period": 5,
"regression_test_commits": 3,
"test_files_changed": 8
}
@@ -925,11 +897,40 @@ Include backlog data in the JSON when TODOS.md exists:
> **STOP.** Before writing the retrospective narrative (Step 14, after all metrics are computed and compared), Read `~/.claude/skills/gstack/retro/sections/report-format.md` and execute it
> in full. Do not work from memory — that section is the source of truth for this step.
After delivering the repo-scoped report, run the following learning capture and result-save steps, then stop. Do not fall through into Global Retrospective Mode.
## Capture Learnings
If you discovered a non-obvious pattern, pitfall, or architectural insight during
this session, log it for future sessions:
```bash
~/.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` (reusable approach), `pitfall` (what NOT to do), `preference`
(user stated), `architecture` (structural decision), `tool` (library/framework insight),
`operational` (project environment/CLI/workflow knowledge).
**Sources:** `observed` (you found this in the code), `user-stated` (user told you),
`inferred` (AI deduction), `cross-model` (both Claude and Codex agree).
**Confidence:** 1-10. Be honest. An observed pattern you verified in the code is 8-9.
An inference you're not sure about is 4-5. A user preference they explicitly stated is 10.
**files:** Include the specific file paths this learning references. This enables
staleness detection: if those files are later deleted, the learning can be flagged.
**Only log genuine discoveries.** Don't log obvious things. Don't log things the user
already knows. A good test: would this insight save time in a future session? If yes, log it.
---
## Global Retrospective Mode
When the user runs `/retro global` (or `/retro global 14d`), follow this flow instead of the repo-scoped Steps 1-14. This mode works from any directory — it does NOT require being inside a git repo.
`/retro global [window]` follows only this flow and works outside a git repo.
### Global Step 1: Compute time window
@@ -1013,11 +1014,11 @@ From the discovery JSON, analyze tool usage patterns:
- Session count per tool
- Behavioral patterns (e.g., "Codex used exclusively for myapp, Claude Code for everything else")
### Global Step 7: Aggregate and generate narrative
### Global Step 7: Aggregate and draft narrative
Structure the output with the **shareable personal card first**, then the full
team/project breakdown below. The personal card is designed to be screenshot-friendly
— everything someone would want to share on X/Twitter in one clean block.
Draft the report below without publishing it yet. Load history in Global Step 8, insert its trends table after **All Projects Overview**, then save the completed snapshot in Global Step 9 and deliver the report. Reuse the drafted tweetable summary in the snapshot.
Output the screenshot-friendly **personal card first**, then the team/project breakdown.
---
@@ -1028,15 +1029,9 @@ Week of Mar 14: 5 projects, 138 commits, 250k LOC across 5 repos | 48 AI session
## 🚀 Your Week: [user name] — [date range]
This section is the **shareable personal card**. It contains ONLY the current user's
stats — no team data, no project breakdowns. Designed to screenshot and post.
Use the user identity from `git config user.name` to filter all per-repo git data.
Aggregate across all repos to compute personal totals.
Render as a single visually clean block. Left border only — no right border (LLMs
can't align right borders reliably). Pad repo names to the longest name so columns
align cleanly. Never truncate project names.
Filter per-repo data by `git config user.name` and aggregate personal totals.
The card contains only this user's stats, not team totals. Use a left border only;
pad names to the longest name and never truncate them.
```
╔═══════════════════════════════════════════════════════════════
@@ -1069,18 +1064,12 @@ align cleanly. Never truncate project names.
**Rules for the personal card:**
- Only show repos where the user has commits. Skip repos with 0 commits.
- Sort repos by user's commit count descending.
- **Never truncate repo names.** Use the full repo name (e.g., `analyze_transcripts`
not `analyze_trans`). Pad the name column to the longest repo name so all columns
align. If names are long, widen the box — the box width adapts to content.
- Widen the card to fit full repo names; align columns.
- For LOC, use "k" formatting for thousands (e.g., "+64.0k" not "+64010").
- Role: "solo" if user is the only contributor, "team" if others contributed.
- Ship of the Week: the user's single highest-LOC PR across ALL repos.
- Top Work: 3 bullet points summarizing the user's major themes, inferred from
commit messages. Not individual commits — synthesize into themes.
E.g., "Built /retro global — cross-project retrospective with AI session discovery"
not "feat: gstack-global-discover" + "feat: /retro global template".
- The card must be self-contained. Someone seeing ONLY this block should understand
the user's week without any surrounding context.
- Top Work: 3 themes synthesized from commit messages, not a list of commits.
- The card must explain the user's week without surrounding context.
- Do NOT include team members, project totals, or context switching data here.
**Personal streak:** Use the user's own commits across all repos (filtered by
@@ -1090,8 +1079,7 @@ align cleanly. Never truncate project names.
## Global Engineering Retro: [date range]
Everything below is the full analysis — team data, project breakdowns, patterns.
This is the "deep dive" that follows the shareable card.
Full team/project analysis follows the personal card.
### All Projects Overview
| Metric | Value |
@@ -1112,9 +1100,7 @@ For each repo (sorted by commits descending):
- AI sessions by tool
**Your Contributions** (sub-section within each project):
For each project, add a "Your contributions" block showing the current user's
personal stats within that repo. Use the user identity from `git config user.name`
to filter. Include:
For each project, filter by `git config user.name` and include:
- Your commits / total commits (with %)
- Your LOC (+insertions / -deletions)
- Your key work (inferred from YOUR commit messages only)
@@ -1175,12 +1161,12 @@ If no prior global retros exist, append: "First global retro recorded — run ag
mkdir -p ~/.gstack/retros
```
Determine the next sequence number for today:
Determine the next unused sequence number for today, using the same session-reminder date as Global Step 1:
```bash
setopt +o nomatch 2>/dev/null || true # zsh compat
today=$(date +%Y-%m-%d)
existing=$(ls ~/.gstack/retros/global-${today}-*.json 2>/dev/null | wc -l | tr -d ' ')
next=$((existing + 1))
today="<today>"
next=1
while [ -e "$HOME/.gstack/retros/global-${today}-${next}.json" ]; do next=$((next + 1)); done
```
Use the Write tool to save JSON to `~/.gstack/retros/global-${today}-${next}.json`:
@@ -1221,10 +1207,10 @@ Use the Write tool to save JSON to `~/.gstack/retros/global-${today}-${next}.jso
When the user runs `/retro compare` (or `/retro compare 14d`):
1. Run Steps 0.5-1 for the current window (default 7d) using the midnight-aligned start date (same logic as the main retro — e.g., if today is 2026-03-18 and window is 7d, `--since "2026-03-11T00:00:00"`)
2. Run `gstack-retro-metrics` a second time for the immediately prior same-length window, using both `--since` and `--until` with midnight-aligned dates to avoid overlap (e.g., for a 7d window starting 2026-03-11: `--since "2026-03-04T00:00:00" --until "2026-03-11T00:00:00"`)
3. Show a side-by-side comparison table with deltas and arrows
4. Write a brief narrative highlighting the biggest improvements and regressions
5. Save only the current-window snapshot to `.context/retros/` (same as a normal retro run); do **not** persist the prior-window metrics.
2. Run `gstack-retro-metrics` a second time for the immediately prior same-length window, using both `--since` and `--until` (e.g., for a 7d window starting 2026-03-11: `--since "2026-03-04T00:00:00" --until "2026-03-10T23:59:59"`)
3. Compute the windowed metrics in Steps 2-10 for each dataset, keeping current and prior values separate. Run Steps 11-11.5 only for the current report: streaks use full history and the shortcut ledger scans the current tree, so neither is a prior-window metric. Apply the freshness guard only to the current window; an inactive prior window is valid comparison data. For hour windows, capture one explicit end timestamp, then subtract the requested hours twice for the two starts. Git includes `--until`, so use one second before the current start for the prior end to avoid counting the boundary commit twice.
4. In place of Step 12's saved-history comparison, show a **Current vs Prior Period** table for commits, logical SLOC, test ratio, sessions, and fix ratio. Show absolute deltas and percentage changes (ratio changes in percentage points); if the prior value is zero, report absolute change and percentage change as N/A. Highlight the biggest improvements and regressions in the Step 14 narrative.
5. Run Steps 13-14 and the post-report capture for the current window only; do **not** persist the prior-window metrics. This comparison works on the first run and does not require saved history.
## Tone
@@ -1247,6 +1233,6 @@ When the user runs `/retro compare` (or `/retro compare 14d`):
- If `COMMITS: 0`, say so and suggest a different window
- Round LOC/hour to nearest 50 (the script pre-rounds `LOC_PER_SESSION_HOUR`)
- Treat merge commits as PR boundaries
- Do not read CLAUDE.md or other docs — this skill is self-contained
- On first run (no prior retros), skip comparison sections gracefully
- Do not read CLAUDE.md or unrelated docs — this skill is self-contained; the CHANGELOG and optional inputs explicitly named above are exceptions
- On first run (no prior retros), skip saved-history comparisons gracefully; explicit `compare` mode still computes its prior window
- **Global mode:** Does NOT require being inside a git repo. Saves snapshots to `~/.gstack/retros/` (not `.context/retros/`). Gracefully skip AI tools that aren't installed. Only compare against prior global retros with the same window value. If streak hits 365d cap, display as "365+ days".
+59 -73
View File
@@ -48,7 +48,7 @@ gbrain:
# /retro — Weekly Engineering Retrospective
Generates a comprehensive engineering retrospective analyzing commit history, work patterns, and code quality metrics. Team-aware: identifies the user running the command, then analyzes every contributor with per-person praise and growth opportunities. Designed for a senior IC/CTO-level builder using Claude Code as a force multiplier.
Analyze commit history, work patterns, and code quality for the current user and every contributor, with evidence-backed praise and growth opportunities.
## User-invocable
When the user types `/retro`, run this skill.
@@ -86,7 +86,9 @@ Usage: /retro [window | compare | global]
/retro global 14d — cross-project retro with explicit window
```
**If the first argument is `global`:** Skip the normal repo-scoped retro (Steps 1-14). Instead, follow the **Global Retrospective** flow at the end of this document. The optional second argument is the time window (default 7d). This mode does NOT require being inside a git repo.
**Routing:** `global` skips all repo-scoped steps, including Prior Learnings, Step 0.5, and post-report capture; follow **Global Retrospective Mode** (no git repo required). `compare` follows **Compare Mode**. Both accept an optional window (default 7d). Otherwise run the repo-scoped flow below.
`<default>` is the base branch from the preceding **Step 0: Detect platform and base branch**. `<today>` is the session-reminder date; reuse it in all snapshot filenames, never re-read the clock.
{{LEARNINGS_SEARCH}}
@@ -103,20 +105,20 @@ Remember whether the fetch succeeded — the stale-base guard in Step 1 only BLO
### Step 1: Gather Metrics (one command)
All raw data gathering and metric computation runs through `gstack-retro-metrics` — one command instead of a dozen git pipelines. Substitute the base branch detected in Step 0 and the midnight-aligned start computed above:
Run `gstack-retro-metrics` with the detected base branch and computed start:
```bash
_RM="$HOME/.claude/skills/gstack/bin/gstack-retro-metrics"
[ -x "$_RM" ] || _RM=".claude/skills/gstack/bin/gstack-retro-metrics"
"$_RM" --base "<default>" --since "<since>" \
|| echo "RETRO_METRICS: unavailable — stale install (compute metrics manually from the steps below)"
|| echo "RETRO_METRICS: unavailable — stale install (read the helper source for manual computation)"
```
Read the labeled `METRIC_NAME: value` lines — they feed every step below. **Degraded mode:** if `RETRO_METRICS_PROTO: 1` is missing from the output, the install is stale; compute each metric manually with git commands, using the metric definitions in Steps 2-11 as the spec.
Read the `METRIC_NAME: value` lines. **Degraded mode:** without `RETRO_METRICS_PROTO: 1`, reproduce computations from the installed `bin/gstack-retro-metrics` source, not the presentation steps below. If source or metrics are unavailable, say so; never invent values. Suggest `/gstack-upgrade` to restore the helper.
**Identity:** `USER_NAME` is **"you"** — the person reading this retro. All other authors are teammates. Orient the narrative around this: "your" commits vs teammate contributions.
**Stale-base + bad-today-anchor guard.** The script echoes `GUARD_LATEST_COMMIT: <DATE>` (newest commit on the analyzed ref). If "today" drifts (model session-context error) or the local `origin/<default>` is materially behind the remote, the window returns zero or near-zero commits and the retro would fabricate a coherent-looking narrative from nothing. Evaluate in this order:
**Stale-base + bad-today-anchor guard.** `GUARD_LATEST_COMMIT: <DATE>` is the newest commit on the analyzed ref. A wrong "today" or stale ref can produce an empty window. Evaluate in order:
1. If `GUARD_REMOTE: none` or `GUARD_HEAD: detached` or the Step 0.5 fetch failed: proceed, but carry the disclosure into the narrative ("offline run, window not freshness-verified") rather than silently misreporting.
2. If the Step 0.5 fetch succeeded AND the `GUARD_LATEST_COMMIT` date is **older than (today window-days)**: BLOCK with: "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." Stop the skill until the user resolves.
@@ -163,12 +165,17 @@ Also check `RETRO_REF`: if it is not `origin/<default>` (local-only repo, missin
### Step 2: Compute Metrics
Present these metrics in a summary table, straight from the metric lines:
Most rows come directly from the metric lines. Gather the two shipping outcomes separately before building the table:
- **Merged PRs:** On GitHub, run `gh pr list --state merged --base "<default>" --search "merged:>=<start-date>" --limit 1000 --json number,title,mergedAt`. Filter `mergedAt` to the exact requested window, including its upper bound in compare mode. If the result hits the limit, paginate via the hosting API or label the count partial. On GitLab use the equivalent merged-MR listing. If hosting data is unavailable, show **PRs referenced** = `PRS_REFERENCED` instead; these are not verified merges. Save `prs_merged: null` in that case.
- **Features shipped:** Read CHANGELOG changes on `RETRO_REF` in the same window (`git log <ref> --since "<since>" -p -- CHANGELOG.md`, adding `--until` for the prior window). Combine newly added user-visible capabilities with verified merged PR titles. Deduplicate entries referring to the same capability, excluding fixes, chores, and reverted work. Keep a short list of feature names with their source commit/PR beside the count. If neither source is available, show unavailable, not zero. This is an evidence-backed classification, not a metric-script line.
Use the analyzed ref in the commit-count label (not always `main`). Test health counts **files changed**, not tests added or test cases; use `TEST_FILES_TOTAL`, `TEST_FILES_CHANGED`, and `REGRESSION_TEST_COMMITS` respectively.
| Metric | Value |
|--------|-------|
| **Features shipped** (from CHANGELOG + merged PR titles) | N |
| Commits to main | N |
| Commits to analyzed ref | N |
| Weighted commits (`WEIGHTED_COMMITS`) | N |
| Contributors | N |
| PRs merged | N |
@@ -183,13 +190,10 @@ Present these metrics in a summary table, straight from the metric lines:
| 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 |
| Test Health | N test files · M changed this period · K regression test commits |
**Metric order rationale (V1):** features shipped leads — what users got. Commits
and weighted commits reflect intent-to-ship. Logical SLOC added reflects real
new functionality. Raw LOC is demoted to context because AI inflates it; ten
lines of a good fix is not less shipping than ten thousand lines of scaffold.
See docs/designs/PLAN_TUNING_V1.md §Workstream C.
Lead with user-visible features, then commit and logical-SLOC metrics; raw LOC
is only context, not impact (PLAN_TUNING_V1.md, Workstream C).
Then show a **per-author leaderboard** immediately below, from the `AUTHOR:` lines:
@@ -280,21 +284,17 @@ Report `COMMIT_SIZE_BUCKETS`:
For each contributor (including the current user), the `AUTHOR:` line carries commits, insertions, deletions, test ratio, top areas, commit type mix, and peak hour; `AUTHOR_BIGGEST:` carries their single highest-impact commit. Use the `COMMIT:` lines to anchor everything in actual work.
**For the current user ("You"):** This section gets the deepest treatment. Include all the detail from the solo retro — session analysis, time patterns, focus score. Frame it in first person: "Your peak hours...", "Your biggest ship..."
**For the current user ("You"):** Include session analysis, time patterns, and focus score: "Your peak hours...", "Your biggest ship..."
**For each teammate:** Write 2-3 sentences covering what they worked on and their pattern. Then:
- **Praise** (1-2 specific things): Anchor in actual commits. Not "great work" — say exactly what was good. Examples: "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 specific thing): Frame as a leveling-up suggestion, not criticism. Anchor in actual data. Examples: "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."
- **Praise** (1-2 specifics): cite commits and what was good, not generic praise.
- **Opportunity for growth** (1 specific): tie an actionable suggestion to data, not criticism. Step 14 supplies examples.
**If only one contributor (solo repo):** Skip the team breakdown and proceed as before — the retro is personal.
**Co-author credit:** `COAUTHOR:` lines carry human `Co-Authored-By:` trailers — credit those authors for the commit alongside the primary author. AI co-authors (e.g., `noreply@anthropic.com`) are counted in `AI_ASSISTED_COMMITS` instead — track "AI-assisted commits" as a separate metric, never as a team member.
{{LEARNINGS_LOG}}
{{GBRAIN_SAVE_RESULTS}}
### Step 10: Week-over-Week Trends (if window >= 14d)
If the time window is 14 days or more, use the `WEEK:` lines (w0 = the week containing the newest commit) to show trends:
@@ -323,12 +323,8 @@ grep -rn "gstack-shortcut(" . \
| grep -vE "gstack-shortcut\(dec-(<|\*)" || true
```
(The exclusions keep docs that merely document the convention — generated
SKILL.md, templates, skill installs — out of the ledger, and the trailing
filter drops placeholder forms like `dec-<id>` / `dec-*` that documentation
uses. Judgment call on what survives: discard any hit that quotes or tests
the convention itself — a sample marker in a checklist, resolver source, or
convention test — rather than marking a real cut corner in this repo's code.)
Discard remaining hits that only document or test the convention (checklists,
resolver examples, tests). Count only real shortcuts in this repo's code.
For each hit, one ledger row: `<file>:<line>, <what was simplified>. ceiling: <X>. upgrade: <Y>.`
- Markers carry a decision id (`dec-<id>`): join against `gstack-decision-search`
@@ -349,7 +345,7 @@ setopt +o nomatch 2>/dev/null || true # zsh compat
ls -t .context/retros/*.json 2>/dev/null
```
**If prior retros exist:** Load the most recent one using the Read tool. Calculate deltas for key metrics and include a **Trends vs Last Retro** section:
**If prior retros exist:** Load the most recent one with the same `window` using the Read tool; if none matches, disclose that and skip historical deltas. Calculate deltas for available key metrics and include a **Trends vs Last Retro** section (in `compare` mode use the freshly computed prior period instead):
```
Last Now Delta
Test ratio: 22% → 41% ↑19pp
@@ -364,19 +360,18 @@ Deep sessions: 3 → 5 ↑2
### Step 13: Save Retro History
After computing all metrics (including streak) and loading any prior history for comparison, save a JSON snapshot:
After computing all metrics (including streak) and loading any prior history for comparison, draft the tweetable summary using the format in Step 14, then save a JSON snapshot. The Step 14 narrative must reuse this exact summary. `streak_days` is the live **team** streak from Step 11 (0 when broken); put the personal streak in `user_streak_days`.
```bash
mkdir -p .context/retros
```
Determine the next sequence number for today (substitute the actual date for `$(date +%Y-%m-%d)`):
Determine the next unused sequence number for today (substitute the session-reminder date for `<today>`):
```bash
setopt +o nomatch 2>/dev/null || true # zsh compat
# Count existing retros for today to get next sequence number
today=$(date +%Y-%m-%d)
existing=$(ls .context/retros/${today}-*.json 2>/dev/null | wc -l | tr -d ' ')
next=$((existing + 1))
today="<today>"
next=1
while [ -e ".context/retros/${today}-${next}.json" ]; do next=$((next + 1)); done
# Save as .context/retros/${today}-${next}.json
```
@@ -410,6 +405,7 @@ Use the Write tool to save the JSON file with this schema:
},
"version_range": ["1.16.0.0", "1.16.1.0"],
"streak_days": 47,
"user_streak_days": 32,
"tweetable": "Week of Mar 1: 47 commits (3 contributors), 3.2k LOC, 38% tests, 12 PRs, peak: 10pm",
"greptile": {
"fixes": 3,
@@ -426,7 +422,6 @@ Include test health data in the JSON when test files exist:
```json
"test_health": {
"total_test_files": 47,
"tests_added_this_period": 5,
"regression_test_commits": 3,
"test_files_changed": 8
}
@@ -447,11 +442,17 @@ Include backlog data in the JSON when TODOS.md exists:
{{SECTION:report-format}}
After delivering the repo-scoped report, run the following learning capture and result-save steps, then stop. Do not fall through into Global Retrospective Mode.
{{LEARNINGS_LOG}}
{{GBRAIN_SAVE_RESULTS}}
---
## Global Retrospective Mode
When the user runs `/retro global` (or `/retro global 14d`), follow this flow instead of the repo-scoped Steps 1-14. This mode works from any directory — it does NOT require being inside a git repo.
`/retro global [window]` follows only this flow and works outside a git repo.
### Global Step 1: Compute time window
@@ -535,11 +536,11 @@ From the discovery JSON, analyze tool usage patterns:
- Session count per tool
- Behavioral patterns (e.g., "Codex used exclusively for myapp, Claude Code for everything else")
### Global Step 7: Aggregate and generate narrative
### Global Step 7: Aggregate and draft narrative
Structure the output with the **shareable personal card first**, then the full
team/project breakdown below. The personal card is designed to be screenshot-friendly
— everything someone would want to share on X/Twitter in one clean block.
Draft the report below without publishing it yet. Load history in Global Step 8, insert its trends table after **All Projects Overview**, then save the completed snapshot in Global Step 9 and deliver the report. Reuse the drafted tweetable summary in the snapshot.
Output the screenshot-friendly **personal card first**, then the team/project breakdown.
---
@@ -550,15 +551,9 @@ Week of Mar 14: 5 projects, 138 commits, 250k LOC across 5 repos | 48 AI session
## 🚀 Your Week: [user name] — [date range]
This section is the **shareable personal card**. It contains ONLY the current user's
stats — no team data, no project breakdowns. Designed to screenshot and post.
Use the user identity from `git config user.name` to filter all per-repo git data.
Aggregate across all repos to compute personal totals.
Render as a single visually clean block. Left border only — no right border (LLMs
can't align right borders reliably). Pad repo names to the longest name so columns
align cleanly. Never truncate project names.
Filter per-repo data by `git config user.name` and aggregate personal totals.
The card contains only this user's stats, not team totals. Use a left border only;
pad names to the longest name and never truncate them.
```
╔═══════════════════════════════════════════════════════════════
@@ -591,18 +586,12 @@ align cleanly. Never truncate project names.
**Rules for the personal card:**
- Only show repos where the user has commits. Skip repos with 0 commits.
- Sort repos by user's commit count descending.
- **Never truncate repo names.** Use the full repo name (e.g., `analyze_transcripts`
not `analyze_trans`). Pad the name column to the longest repo name so all columns
align. If names are long, widen the box — the box width adapts to content.
- Widen the card to fit full repo names; align columns.
- For LOC, use "k" formatting for thousands (e.g., "+64.0k" not "+64010").
- Role: "solo" if user is the only contributor, "team" if others contributed.
- Ship of the Week: the user's single highest-LOC PR across ALL repos.
- Top Work: 3 bullet points summarizing the user's major themes, inferred from
commit messages. Not individual commits — synthesize into themes.
E.g., "Built /retro global — cross-project retrospective with AI session discovery"
not "feat: gstack-global-discover" + "feat: /retro global template".
- The card must be self-contained. Someone seeing ONLY this block should understand
the user's week without any surrounding context.
- Top Work: 3 themes synthesized from commit messages, not a list of commits.
- The card must explain the user's week without surrounding context.
- Do NOT include team members, project totals, or context switching data here.
**Personal streak:** Use the user's own commits across all repos (filtered by
@@ -612,8 +601,7 @@ align cleanly. Never truncate project names.
## Global Engineering Retro: [date range]
Everything below is the full analysis — team data, project breakdowns, patterns.
This is the "deep dive" that follows the shareable card.
Full team/project analysis follows the personal card.
### All Projects Overview
| Metric | Value |
@@ -634,9 +622,7 @@ For each repo (sorted by commits descending):
- AI sessions by tool
**Your Contributions** (sub-section within each project):
For each project, add a "Your contributions" block showing the current user's
personal stats within that repo. Use the user identity from `git config user.name`
to filter. Include:
For each project, filter by `git config user.name` and include:
- Your commits / total commits (with %)
- Your LOC (+insertions / -deletions)
- Your key work (inferred from YOUR commit messages only)
@@ -697,12 +683,12 @@ If no prior global retros exist, append: "First global retro recorded — run ag
mkdir -p ~/.gstack/retros
```
Determine the next sequence number for today:
Determine the next unused sequence number for today, using the same session-reminder date as Global Step 1:
```bash
setopt +o nomatch 2>/dev/null || true # zsh compat
today=$(date +%Y-%m-%d)
existing=$(ls ~/.gstack/retros/global-${today}-*.json 2>/dev/null | wc -l | tr -d ' ')
next=$((existing + 1))
today="<today>"
next=1
while [ -e "$HOME/.gstack/retros/global-${today}-${next}.json" ]; do next=$((next + 1)); done
```
Use the Write tool to save JSON to `~/.gstack/retros/global-${today}-${next}.json`:
@@ -743,10 +729,10 @@ Use the Write tool to save JSON to `~/.gstack/retros/global-${today}-${next}.jso
When the user runs `/retro compare` (or `/retro compare 14d`):
1. Run Steps 0.5-1 for the current window (default 7d) using the midnight-aligned start date (same logic as the main retro — e.g., if today is 2026-03-18 and window is 7d, `--since "2026-03-11T00:00:00"`)
2. Run `gstack-retro-metrics` a second time for the immediately prior same-length window, using both `--since` and `--until` with midnight-aligned dates to avoid overlap (e.g., for a 7d window starting 2026-03-11: `--since "2026-03-04T00:00:00" --until "2026-03-11T00:00:00"`)
3. Show a side-by-side comparison table with deltas and arrows
4. Write a brief narrative highlighting the biggest improvements and regressions
5. Save only the current-window snapshot to `.context/retros/` (same as a normal retro run); do **not** persist the prior-window metrics.
2. Run `gstack-retro-metrics` a second time for the immediately prior same-length window, using both `--since` and `--until` (e.g., for a 7d window starting 2026-03-11: `--since "2026-03-04T00:00:00" --until "2026-03-10T23:59:59"`)
3. Compute the windowed metrics in Steps 2-10 for each dataset, keeping current and prior values separate. Run Steps 11-11.5 only for the current report: streaks use full history and the shortcut ledger scans the current tree, so neither is a prior-window metric. Apply the freshness guard only to the current window; an inactive prior window is valid comparison data. For hour windows, capture one explicit end timestamp, then subtract the requested hours twice for the two starts. Git includes `--until`, so use one second before the current start for the prior end to avoid counting the boundary commit twice.
4. In place of Step 12's saved-history comparison, show a **Current vs Prior Period** table for commits, logical SLOC, test ratio, sessions, and fix ratio. Show absolute deltas and percentage changes (ratio changes in percentage points); if the prior value is zero, report absolute change and percentage change as N/A. Highlight the biggest improvements and regressions in the Step 14 narrative.
5. Run Steps 13-14 and the post-report capture for the current window only; do **not** persist the prior-window metrics. This comparison works on the first run and does not require saved history.
## Tone
@@ -769,6 +755,6 @@ When the user runs `/retro compare` (or `/retro compare 14d`):
- If `COMMITS: 0`, say so and suggest a different window
- Round LOC/hour to nearest 50 (the script pre-rounds `LOC_PER_SESSION_HOUR`)
- Treat merge commits as PR boundaries
- Do not read CLAUDE.md or other docs — this skill is self-contained
- On first run (no prior retros), skip comparison sections gracefully
- Do not read CLAUDE.md or unrelated docs — this skill is self-contained; the CHANGELOG and optional inputs explicitly named above are exceptions
- On first run (no prior retros), skip saved-history comparisons gracefully; explicit `compare` mode still computes its prior window
- **Global mode:** Does NOT require being inside a git repo. Saves snapshots to `~/.gstack/retros/` (not `.context/retros/`). Gracefully skip AI tools that aren't installed. Only compare against prior global retros with the same window value. If streak hits 365d cap, display as "365+ days".
+8 -2
View File
@@ -15,7 +15,7 @@ Week of Mar 1: 47 commits (3 contributors), 3.2k LOC, 38% tests, 12 PRs, peak: 1
(from Step 2)
### Trends vs Last Retro
(from Step 12, loaded before save — skip if first retro)
(from Step 12, loaded before save — skip if no matching history; in `compare` mode use **Current vs Prior Period** from the computed prior window even on the first run)
### Time & Session Patterns
(from Steps 3-4)
@@ -42,7 +42,7 @@ Narrative covering:
### Test Health
- Total test files: N (`TEST_FILES_TOTAL`)
- Tests added this period: M (`TEST_FILES_CHANGED` — test files changed in the window)
- Test files changed this period: M (`TEST_FILES_CHANGED`; not newly added test cases)
- Regression test commits: list the `REGRESSION_COMMIT` lines (`test(qa):`, `test(design):`, and `test: coverage` commits)
- If prior retro exists and has `test_health`: show delta "Test count: {last} → {now} (+{delta})"
- If test ratio < 20%: flag as growth area — "100% test coverage is the goal. Tests make vibe coding safe."
@@ -75,6 +75,12 @@ If no plan data exists, skip this section silently.
- Focus score with interpretation
- Ship of the week callout
### Shipping Streaks
(from Step 11: team and personal streaks, including broken-streak disclosure)
### Shortcut Debt
(from Step 11.5: marker ledger and count, or the clean-ledger statement)
### Your Week (personal deep-dive)
(from Step 9, for the current user only)
+8 -2
View File
@@ -13,7 +13,7 @@ Week of Mar 1: 47 commits (3 contributors), 3.2k LOC, 38% tests, 12 PRs, peak: 1
(from Step 2)
### Trends vs Last Retro
(from Step 12, loaded before save — skip if first retro)
(from Step 12, loaded before save — skip if no matching history; in `compare` mode use **Current vs Prior Period** from the computed prior window even on the first run)
### Time & Session Patterns
(from Steps 3-4)
@@ -40,7 +40,7 @@ Narrative covering:
### Test Health
- Total test files: N (`TEST_FILES_TOTAL`)
- Tests added this period: M (`TEST_FILES_CHANGED` — test files changed in the window)
- Test files changed this period: M (`TEST_FILES_CHANGED`; not newly added test cases)
- Regression test commits: list the `REGRESSION_COMMIT` lines (`test(qa):`, `test(design):`, and `test: coverage` commits)
- If prior retro exists and has `test_health`: show delta "Test count: {last} → {now} (+{delta})"
- If test ratio < 20%: flag as growth area — "100% test coverage is the goal. Tests make vibe coding safe."
@@ -73,6 +73,12 @@ If no plan data exists, skip this section silently.
- Focus score with interpretation
- Ship of the week callout
### Shipping Streaks
(from Step 11: team and personal streaks, including broken-streak disclosure)
### Shortcut Debt
(from Step 11.5: marker ledger and count, or the clean-ledger statement)
### Your Week (personal deep-dive)
(from Step 9, for the current user only)
+1 -1
View File
@@ -177,7 +177,7 @@ describe('gstack-retro-metrics contract', () => {
expect(tmpl).toContain('".claude/skills/gstack/bin/gstack-retro-metrics"');
expect(tmpl).toContain('--base "<default>" --since "<since>"');
expect(tmpl).toContain(
'RETRO_METRICS: unavailable — stale install (compute metrics manually from the steps below)',
'RETRO_METRICS: unavailable — stale install (read the helper source for manual computation)',
);
// Degraded-mode prose keys off the proto handshake.
expect(tmpl).toContain('RETRO_METRICS_PROTO: 1');
+1 -1
View File
@@ -785,7 +785,7 @@ describeIfSelected('Other skill evals', [
suite: 'Other skill evals',
skillPath: 'retro/SKILL.md',
startMarker: '## Instructions',
endMarker: '## Compare Mode',
endMarker: '## Tone',
judgeContext: 'an engineering retrospective data gathering and analysis workflow',
judgeGoal: 'how to gather git metrics (commit history, test counts, work patterns), analyze them, produce a structured retro report with praise, growth areas, and trend tracking',
});
+3 -1
View File
@@ -1404,7 +1404,9 @@ describe('Retro test health tracking', () => {
test('retro/SKILL.md has Test Health metrics row', () => {
const content = readSkillUnion('retro');
expect(content).toContain('Test Health');
expect(content).toContain('regression tests');
expect(content).toContain('N test files');
expect(content).toContain('M changed this period');
expect(content).toContain('K regression test commits');
});
test('retro/SKILL.md has Test Health narrative section', () => {
+19
View File
@@ -83,6 +83,25 @@ describe('workflow judge excerpts', () => {
expect(() => readWorkflowExcerpt('ship/SKILL.md', '# Ship:', '# missing')).toThrow('End marker not found');
});
test('retro judge includes compare semantics and unambiguous report inputs', () => {
const text = readWorkflowExcerpt('retro/SKILL.md', '## Instructions', '## Tone');
expect(text).toContain('## Compare Mode');
expect(text).toContain('does not require saved history');
expect(text).toContain('one second before the current start');
expect(text).toContain('PRs referenced');
expect(text).toContain('prs_merged: null');
expect(text).toContain('not newly added test cases');
expect(text).toContain('`streak_days` is the live **team** streak');
expect(text).toContain('draft the tweetable summary using the format in Step 14, then save');
expect(text).toContain('### Shipping Streaks');
expect(text).toContain('### Shortcut Debt');
expect(text.indexOf('## Capture Learnings')).toBeGreaterThan(text.indexOf('### Step 14:'));
expect(text).not.toContain('$(date');
expect(text.match(/today="<today>"/g)).toHaveLength(2);
const judge = readFileSync(join(import.meta.dir, 'skill-llm-eval.test.ts'), 'utf8');
expect(judge).toMatch(/skillPath: 'retro\/SKILL.md',[\s\S]*?endMarker: '## Tone'/);
});
test('deploy gates and navigation timing formulas are executable as documented', () => {
const land = readFileSync(join(import.meta.dir, '../land-and-deploy/SKILL.md.tmpl'), 'utf8');
expect(land).not.toContain('Skip Step 3, go to Step 4');