Skip to content

Commit d268b5f

Browse files
committed
feat: ship run log persistence for /retro velocity tracking
Adds structured JSONL logging to /ship and consumption in /retro, enabling shipping velocity metrics across retrospectives. - bin/gstack-ship-log: read/write helper with --window filtering - ship Step 8.25: appends entry after PR creation (version, branch, PR URL, review findings, Greptile stats, todos completed, test coverage counts) - retro Step 2: aggregates ship velocity (runs, PRs, avg findings, Greptile catches, coverage delta) - retro Step 13: persists ship_velocity in JSON snapshot - retro Step 14: references ship log trends in narrative Also fixes pre-existing duplicate command numbering in retro Step 1 (two commands were both labeled #12).
1 parent 407b156 commit d268b5f

7 files changed

Lines changed: 260 additions & 12 deletions

File tree

.agents/skills/gstack-retro/SKILL.md

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -329,8 +329,11 @@ git log origin/<default> --since="<window>" --oneline --grep="test(qa):" --grep=
329329
# 12. gstack skill usage telemetry (if available)
330330
cat ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true
331331

332-
# 12. Test files changed in window
332+
# 13. Test files changed in window
333333
git log origin/<default> --since="<window>" --format="" --name-only | grep -E '\.(test|spec)\.' | sort -u | wc -l
334+
335+
# 14. Ship run log (if available)
336+
~/.codex/skills/gstack/bin/gstack-ship-log read --window <window> 2>/dev/null || true
334337
```
335338

336339
### Step 2: Compute Metrics
@@ -403,6 +406,21 @@ If moments exist, list them:
403406

404407
If the JSONL file doesn't exist or has no entries in the window, skip the Eureka Moments row.
405408

409+
**Ship Velocity (if ship log exists):** Read the ship log output from Step 1, command 14. If entries exist within the retro window, aggregate:
410+
- Total `/ship` runs
411+
- PRs created (count entries with non-empty `pr_url`)
412+
- Average review findings per ship run (`review_findings`)
413+
- Total Greptile catches vs false positives (`greptile_fixed` vs `greptile_fps`)
414+
- TODOs completed via `/ship` (sum of `todos_completed`)
415+
- Test coverage delta (average `coverage_after - coverage_before`)
416+
417+
Include in the metrics table:
418+
```
419+
| Ship Velocity | N /ship runs · M PRs · avg X review findings · Y Greptile catches |
420+
```
421+
422+
If the ship log doesn't exist or has no entries in the window (`NO_SHIP_LOG` returned), skip the Ship Velocity row.
423+
406424
### Step 3: Commit Time Distribution
407425

408426
Show hourly histogram in local time using bar chart:
@@ -598,7 +616,7 @@ Use the Write tool to save the JSON file with this schema:
598616
}
599617
```
600618

601-
**Note:** Only include the `greptile` field if `~/.gstack/greptile-history.md` exists and has entries within the time window. Only include the `backlog` field if `TODOS.md` exists. Only include the `test_health` field if test files were found (command 10 returns > 0). If any has no data, omit the field entirely.
619+
**Note:** Only include the `greptile` field if `~/.gstack/greptile-history.md` exists and has entries within the time window. Only include the `backlog` field if `TODOS.md` exists. Only include the `test_health` field if test files were found (command 10 returns > 0). Only include `ship_velocity` if the ship log has entries in the window. If any has no data, omit the field entirely.
602620

603621
Include test health data in the JSON when test files exist:
604622
```json
@@ -621,6 +639,19 @@ Include backlog data in the JSON when TODOS.md exists:
621639
}
622640
```
623641

642+
Include ship velocity data in the JSON when the ship log has entries in the window:
643+
```json
644+
"ship_velocity": {
645+
"ship_runs": 5,
646+
"prs_created": 5,
647+
"avg_review_findings": 2.4,
648+
"greptile_catches": 3,
649+
"greptile_fps": 1,
650+
"todos_completed": 8,
651+
"avg_coverage_delta": 3
652+
}
653+
```
654+
624655
### Step 14: Write the Narrative
625656

626657
Structure the output as:
@@ -650,13 +681,14 @@ Narrative interpreting what the team-wide patterns mean:
650681
- Notable patterns: do team members code at the same time or in shifts?
651682

652683
### Shipping Velocity
653-
(from Steps 5-7)
684+
(from Steps 5-7 + ship log)
654685

655686
Narrative covering:
656687
- Commit type mix and what it reveals
657688
- PR size distribution and what it reveals about shipping cadence
658689
- Fix-chain detection (sequences of fix commits on the same subsystem)
659690
- Version bump discipline
691+
- Ship log trends (if data exists): `/ship` runs per period, review finding rates, Greptile signal over time, test coverage growth from `/ship` auto-generation
660692

661693
### Code Quality Signals
662694
- Test LOC ratio trend

.agents/skills/gstack-ship/SKILL.md

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1068,7 +1068,34 @@ EOF
10681068
)"
10691069
```
10701070

1071-
**Output the PR URL** — then proceed to Step 8.5.
1071+
**Output the PR URL** — then proceed to Step 8.25.
1072+
1073+
---
1074+
1075+
## Step 8.25: Ship Log
1076+
1077+
Append a structured entry to the ship log for `/retro` velocity tracking. This step is fully automatic and must never fail the workflow.
1078+
1079+
Collect the data from earlier steps and write a single JSONL entry:
1080+
1081+
```bash
1082+
source <(~/.codex/skills/gstack/bin/gstack-slug 2>/dev/null)
1083+
~/.codex/skills/gstack/bin/gstack-ship-log '{"ts":"'"$(date -u +%Y-%m-%dT%H:%M:%SZ)"'","version":"VERSION","branch":"'"$BRANCH"'","repo":"'"$SLUG"'","pr_url":"PR_URL","review_findings":N,"review_auto_fixed":N,"greptile_comments":N,"greptile_fixed":N,"greptile_fps":N,"todos_completed":N,"tests_passed":true,"coverage_before":N,"coverage_after":N}'
1084+
```
1085+
1086+
Substitute the capitalized placeholders from data gathered in earlier steps:
1087+
- `VERSION`: the new version from Step 4
1088+
- `PR_URL`: the PR URL output from Step 8
1089+
- `review_findings`: total issues found in Step 3.5 (0 if none)
1090+
- `review_auto_fixed`: count of auto-fixed issues in Step 3.5
1091+
- `greptile_comments`: total Greptile comments in Step 3.75 (0 if skipped)
1092+
- `greptile_fixed`: Greptile comments fixed in Step 3.75
1093+
- `greptile_fps`: Greptile false positives in Step 3.75
1094+
- `todos_completed`: items marked complete in Step 5.5 (0 if none)
1095+
- `tests_passed`: `true` (always true here — Step 3 would have stopped if tests failed)
1096+
- `coverage_before`/`coverage_after`: test file counts from Step 3.4 (0 if skipped)
1097+
1098+
If the `gstack-ship-log` script is not found or the write fails, warn once and continue. Never stop the ship workflow for a log failure.
10721099

10731100
---
10741101

bin/gstack-ship-log

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
#!/usr/bin/env bash
2+
# gstack-ship-log — append a structured JSON entry to the ship log
3+
#
4+
# Usage:
5+
# gstack-ship-log '{"ts":"...","version":"...","branch":"...","pr_url":"..."}'
6+
# gstack-ship-log read — print ship log contents (or NO_SHIP_LOG)
7+
# gstack-ship-log read --window 7d — print entries from last N days
8+
#
9+
# The ship log lives at ~/.gstack/analytics/ship-log.jsonl.
10+
# /ship appends an entry after each PR creation.
11+
# /retro reads the log for shipping velocity metrics.
12+
#
13+
# Env overrides (for testing):
14+
# GSTACK_STATE_DIR — override ~/.gstack state directory
15+
set -uo pipefail
16+
17+
STATE_DIR="${GSTACK_STATE_DIR:-$HOME/.gstack}"
18+
LOG_FILE="$STATE_DIR/analytics/ship-log.jsonl"
19+
20+
case "${1:-}" in
21+
read)
22+
if [ ! -f "$LOG_FILE" ]; then
23+
echo "NO_SHIP_LOG"
24+
exit 0
25+
fi
26+
# Optional --window filter
27+
if [ "${2:-}" = "--window" ] && [ -n "${3:-}" ]; then
28+
WINDOW="${3}"
29+
# Extract numeric value and unit
30+
DAYS=0
31+
case "$WINDOW" in
32+
*d) DAYS="${WINDOW%d}" ;;
33+
*h) DAYS=0 ;; # sub-day: include everything (awk filters by timestamp)
34+
*) DAYS=7 ;;
35+
esac
36+
if [ "$DAYS" -gt 0 ] 2>/dev/null; then
37+
if date -v-1d +%Y-%m-%d >/dev/null 2>&1; then
38+
CUTOFF="$(date -v-${DAYS}d -u +%Y-%m-%dT%H:%M:%SZ)"
39+
else
40+
CUTOFF="$(date -u -d "$DAYS days ago" +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || echo "2000-01-01T00:00:00Z")"
41+
fi
42+
awk -F'"' -v cutoff="$CUTOFF" '
43+
/"ts":"/ {
44+
for (i=1; i<=NF; i++) {
45+
if ($i == "ts" && $(i+1) ~ /^:/) {
46+
if ($(i+2) >= cutoff) { print; break }
47+
}
48+
}
49+
}
50+
' "$LOG_FILE"
51+
else
52+
cat "$LOG_FILE"
53+
fi
54+
else
55+
cat "$LOG_FILE"
56+
fi
57+
;;
58+
-h|--help)
59+
sed -n '2,/^[^#]/{ /^#/s/^# \{0,1\}//p; }' "$0"
60+
exit 0
61+
;;
62+
"")
63+
echo "Usage: gstack-ship-log '{json}' or gstack-ship-log read" >&2
64+
exit 1
65+
;;
66+
*)
67+
# Append mode: $1 is a JSON string
68+
mkdir -p "$STATE_DIR/analytics"
69+
echo "$1" >> "$LOG_FILE"
70+
;;
71+
esac

retro/SKILL.md

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -336,8 +336,11 @@ git log origin/<default> --since="<window>" --oneline --grep="test(qa):" --grep=
336336
# 12. gstack skill usage telemetry (if available)
337337
cat ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true
338338

339-
# 12. Test files changed in window
339+
# 13. Test files changed in window
340340
git log origin/<default> --since="<window>" --format="" --name-only | grep -E '\.(test|spec)\.' | sort -u | wc -l
341+
342+
# 14. Ship run log (if available)
343+
~/.claude/skills/gstack/bin/gstack-ship-log read --window <window> 2>/dev/null || true
341344
```
342345

343346
### Step 2: Compute Metrics
@@ -410,6 +413,21 @@ If moments exist, list them:
410413

411414
If the JSONL file doesn't exist or has no entries in the window, skip the Eureka Moments row.
412415

416+
**Ship Velocity (if ship log exists):** Read the ship log output from Step 1, command 14. If entries exist within the retro window, aggregate:
417+
- Total `/ship` runs
418+
- PRs created (count entries with non-empty `pr_url`)
419+
- Average review findings per ship run (`review_findings`)
420+
- Total Greptile catches vs false positives (`greptile_fixed` vs `greptile_fps`)
421+
- TODOs completed via `/ship` (sum of `todos_completed`)
422+
- Test coverage delta (average `coverage_after - coverage_before`)
423+
424+
Include in the metrics table:
425+
```
426+
| Ship Velocity | N /ship runs · M PRs · avg X review findings · Y Greptile catches |
427+
```
428+
429+
If the ship log doesn't exist or has no entries in the window (`NO_SHIP_LOG` returned), skip the Ship Velocity row.
430+
413431
### Step 3: Commit Time Distribution
414432

415433
Show hourly histogram in local time using bar chart:
@@ -605,7 +623,7 @@ Use the Write tool to save the JSON file with this schema:
605623
}
606624
```
607625

608-
**Note:** Only include the `greptile` field if `~/.gstack/greptile-history.md` exists and has entries within the time window. Only include the `backlog` field if `TODOS.md` exists. Only include the `test_health` field if test files were found (command 10 returns > 0). If any has no data, omit the field entirely.
626+
**Note:** Only include the `greptile` field if `~/.gstack/greptile-history.md` exists and has entries within the time window. Only include the `backlog` field if `TODOS.md` exists. Only include the `test_health` field if test files were found (command 10 returns > 0). Only include `ship_velocity` if the ship log has entries in the window. If any has no data, omit the field entirely.
609627

610628
Include test health data in the JSON when test files exist:
611629
```json
@@ -628,6 +646,19 @@ Include backlog data in the JSON when TODOS.md exists:
628646
}
629647
```
630648

649+
Include ship velocity data in the JSON when the ship log has entries in the window:
650+
```json
651+
"ship_velocity": {
652+
"ship_runs": 5,
653+
"prs_created": 5,
654+
"avg_review_findings": 2.4,
655+
"greptile_catches": 3,
656+
"greptile_fps": 1,
657+
"todos_completed": 8,
658+
"avg_coverage_delta": 3
659+
}
660+
```
661+
631662
### Step 14: Write the Narrative
632663

633664
Structure the output as:
@@ -657,13 +688,14 @@ Narrative interpreting what the team-wide patterns mean:
657688
- Notable patterns: do team members code at the same time or in shifts?
658689

659690
### Shipping Velocity
660-
(from Steps 5-7)
691+
(from Steps 5-7 + ship log)
661692

662693
Narrative covering:
663694
- Commit type mix and what it reveals
664695
- PR size distribution and what it reveals about shipping cadence
665696
- Fix-chain detection (sequences of fix commits on the same subsystem)
666697
- Version bump discipline
698+
- Ship log trends (if data exists): `/ship` runs per period, review finding rates, Greptile signal over time, test coverage growth from `/ship` auto-generation
667699

668700
### Code Quality Signals
669701
- Test LOC ratio trend

retro/SKILL.md.tmpl

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -112,8 +112,11 @@ git log origin/<default> --since="<window>" --oneline --grep="test(qa):" --grep=
112112
# 12. gstack skill usage telemetry (if available)
113113
cat ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true
114114
115-
# 12. Test files changed in window
115+
# 13. Test files changed in window
116116
git log origin/<default> --since="<window>" --format="" --name-only | grep -E '\.(test|spec)\.' | sort -u | wc -l
117+
118+
# 14. Ship run log (if available)
119+
~/.claude/skills/gstack/bin/gstack-ship-log read --window <window> 2>/dev/null || true
117120
```
118121

119122
### Step 2: Compute Metrics
@@ -186,6 +189,21 @@ If moments exist, list them:
186189

187190
If the JSONL file doesn't exist or has no entries in the window, skip the Eureka Moments row.
188191

192+
**Ship Velocity (if ship log exists):** Read the ship log output from Step 1, command 14. If entries exist within the retro window, aggregate:
193+
- Total `/ship` runs
194+
- PRs created (count entries with non-empty `pr_url`)
195+
- Average review findings per ship run (`review_findings`)
196+
- Total Greptile catches vs false positives (`greptile_fixed` vs `greptile_fps`)
197+
- TODOs completed via `/ship` (sum of `todos_completed`)
198+
- Test coverage delta (average `coverage_after - coverage_before`)
199+
200+
Include in the metrics table:
201+
```
202+
| Ship Velocity | N /ship runs · M PRs · avg X review findings · Y Greptile catches |
203+
```
204+
205+
If the ship log doesn't exist or has no entries in the window (`NO_SHIP_LOG` returned), skip the Ship Velocity row.
206+
189207
### Step 3: Commit Time Distribution
190208

191209
Show hourly histogram in local time using bar chart:
@@ -381,7 +399,7 @@ Use the Write tool to save the JSON file with this schema:
381399
}
382400
```
383401

384-
**Note:** Only include the `greptile` field if `~/.gstack/greptile-history.md` exists and has entries within the time window. Only include the `backlog` field if `TODOS.md` exists. Only include the `test_health` field if test files were found (command 10 returns > 0). If any has no data, omit the field entirely.
402+
**Note:** Only include the `greptile` field if `~/.gstack/greptile-history.md` exists and has entries within the time window. Only include the `backlog` field if `TODOS.md` exists. Only include the `test_health` field if test files were found (command 10 returns > 0). Only include `ship_velocity` if the ship log has entries in the window. If any has no data, omit the field entirely.
385403

386404
Include test health data in the JSON when test files exist:
387405
```json
@@ -404,6 +422,19 @@ Include backlog data in the JSON when TODOS.md exists:
404422
}
405423
```
406424

425+
Include ship velocity data in the JSON when the ship log has entries in the window:
426+
```json
427+
"ship_velocity": {
428+
"ship_runs": 5,
429+
"prs_created": 5,
430+
"avg_review_findings": 2.4,
431+
"greptile_catches": 3,
432+
"greptile_fps": 1,
433+
"todos_completed": 8,
434+
"avg_coverage_delta": 3
435+
}
436+
```
437+
407438
### Step 14: Write the Narrative
408439

409440
Structure the output as:
@@ -433,13 +464,14 @@ Narrative interpreting what the team-wide patterns mean:
433464
- Notable patterns: do team members code at the same time or in shifts?
434465

435466
### Shipping Velocity
436-
(from Steps 5-7)
467+
(from Steps 5-7 + ship log)
437468

438469
Narrative covering:
439470
- Commit type mix and what it reveals
440471
- PR size distribution and what it reveals about shipping cadence
441472
- Fix-chain detection (sequences of fix commits on the same subsystem)
442473
- Version bump discipline
474+
- Ship log trends (if data exists): `/ship` runs per period, review finding rates, Greptile signal over time, test coverage growth from `/ship` auto-generation
443475

444476
### Code Quality Signals
445477
- Test LOC ratio trend

ship/SKILL.md

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1213,7 +1213,34 @@ EOF
12131213
)"
12141214
```
12151215

1216-
**Output the PR URL** — then proceed to Step 8.5.
1216+
**Output the PR URL** — then proceed to Step 8.25.
1217+
1218+
---
1219+
1220+
## Step 8.25: Ship Log
1221+
1222+
Append a structured entry to the ship log for `/retro` velocity tracking. This step is fully automatic and must never fail the workflow.
1223+
1224+
Collect the data from earlier steps and write a single JSONL entry:
1225+
1226+
```bash
1227+
source <(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)
1228+
~/.claude/skills/gstack/bin/gstack-ship-log '{"ts":"'"$(date -u +%Y-%m-%dT%H:%M:%SZ)"'","version":"VERSION","branch":"'"$BRANCH"'","repo":"'"$SLUG"'","pr_url":"PR_URL","review_findings":N,"review_auto_fixed":N,"greptile_comments":N,"greptile_fixed":N,"greptile_fps":N,"todos_completed":N,"tests_passed":true,"coverage_before":N,"coverage_after":N}'
1229+
```
1230+
1231+
Substitute the capitalized placeholders from data gathered in earlier steps:
1232+
- `VERSION`: the new version from Step 4
1233+
- `PR_URL`: the PR URL output from Step 8
1234+
- `review_findings`: total issues found in Step 3.5 (0 if none)
1235+
- `review_auto_fixed`: count of auto-fixed issues in Step 3.5
1236+
- `greptile_comments`: total Greptile comments in Step 3.75 (0 if skipped)
1237+
- `greptile_fixed`: Greptile comments fixed in Step 3.75
1238+
- `greptile_fps`: Greptile false positives in Step 3.75
1239+
- `todos_completed`: items marked complete in Step 5.5 (0 if none)
1240+
- `tests_passed`: `true` (always true here — Step 3 would have stopped if tests failed)
1241+
- `coverage_before`/`coverage_after`: test file counts from Step 3.4 (0 if skipped)
1242+
1243+
If the `gstack-ship-log` script is not found or the write fails, warn once and continue. Never stop the ship workflow for a log failure.
12171244

12181245
---
12191246

0 commit comments

Comments
 (0)