Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 72 additions & 0 deletions bin/gstack-ship-log
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
#!/usr/bin/env bash
# gstack-ship-log — append a structured JSON entry to the ship log
#
# Usage:
# gstack-ship-log '{"ts":"...","version":"...","branch":"...","pr_url":"..."}'
# gstack-ship-log read — print ship log contents (or NO_SHIP_LOG)
# gstack-ship-log read --window 7d — print entries from last N days
#
# The ship log lives at ~/.gstack/analytics/ship-log.jsonl.
# /ship appends an entry after each PR creation.
# /retro reads the log for shipping velocity metrics.
#
# Env overrides (for testing):
# GSTACK_STATE_DIR — override ~/.gstack state directory
set -uo pipefail

STATE_DIR="${GSTACK_STATE_DIR:-$HOME/.gstack}"
JSONL_FILE="$STATE_DIR/analytics/ship-log.jsonl"

case "${1:-}" in
read)
if [ ! -f "$JSONL_FILE" ]; then
echo "NO_SHIP_LOG"
exit 0
fi
# Optional --window filter
if [ "${2:-}" = "--window" ] && [ -n "${3:-}" ]; then
_WINDOW="${3}"
_DAYS=0
case "$_WINDOW" in
*d) _DAYS="${_WINDOW%d}" ;;
*h) _DAYS=0 ;; # sub-day: include everything (awk filters by timestamp)
*) _DAYS=7 ;;
esac
if [ "$_DAYS" -gt 0 ] 2>/dev/null; then
if date -v-1d +%Y-%m-%d >/dev/null 2>&1; then
_CUTOFF="$(date -v-${_DAYS}d -u +%Y-%m-%dT%H:%M:%SZ)"
else
_CUTOFF="$(date -u -d "$_DAYS days ago" +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || echo "2000-01-01T00:00:00Z")"
fi
awk -F'"' -v cutoff="$_CUTOFF" '
/"ts":"/ {
for (i=1; i<=NF; i++) {
if ($i == "ts" && $(i+1) ~ /^:/) {
if ($(i+2) >= cutoff) { print; break }
}
}
}
' "$JSONL_FILE"
else
cat "$JSONL_FILE"
fi
else
cat "$JSONL_FILE"
fi
;;
-h|--help)
sed -n '2,/^[^#]/{ /^#/s/^# \{0,1\}//p; }' "$0"
exit 0
;;
"")
echo "Usage: gstack-ship-log '{json}' or gstack-ship-log read" >&2
exit 1
;;
*)
# Append mode: $1 is a JSON string
mkdir -p "$STATE_DIR/analytics" 2>/dev/null || true
echo "$1" >> "$JSONL_FILE" 2>/dev/null || true
;;
esac

exit 0
40 changes: 36 additions & 4 deletions retro/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -660,8 +660,11 @@ git log origin/<default> --since="<window>" --oneline --grep="test(qa):" --grep=
# 12. gstack skill usage telemetry (if available)
cat ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true

# 12. Test files changed in window
# 13. Test files changed in window
git log origin/<default> --since="<window>" --format="" --name-only | grep -E '\.(test|spec)\.' | sort -u | wc -l

# 14. Ship run log (if available)
~/.claude/skills/gstack/bin/gstack-ship-log read --window <window> 2>/dev/null || true
```

### Step 2: Compute Metrics
Expand Down Expand Up @@ -734,6 +737,21 @@ If moments exist, list them:

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

**Ship Velocity (if ship log exists):** Read the ship log output from Step 1, command 14. If entries exist within the retro window, aggregate:
- Total `/ship` runs
- PRs created (count entries with non-empty `pr_url`)
- Average review findings per ship run (`review_findings`)
- Total Greptile catches vs false positives (`greptile_fixed` vs `greptile_fps`)
- TODOs completed via `/ship` (sum of `todos_completed`)
- Test coverage delta (average `coverage_after - coverage_before`)

Include in the metrics table:
```
| Ship Velocity | N /ship runs · M PRs · avg X review findings · Y Greptile catches |
```

If the ship log doesn't exist or has no entries in the window (`NO_SHIP_LOG` returned), skip the Ship Velocity row.

### Step 3: Commit Time Distribution

Show hourly histogram in local time using bar chart:
Expand Down Expand Up @@ -956,7 +974,7 @@ Use the Write tool to save the JSON file with this schema:
}
```

**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.
**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.

Include test health data in the JSON when test files exist:
```json
Expand All @@ -979,6 +997,19 @@ Include backlog data in the JSON when TODOS.md exists:
}
```

Include ship velocity data in the JSON when the ship log has entries in the window:
```json
"ship_velocity": {
"ship_runs": 5,
"prs_created": 5,
"avg_review_findings": 2.4,
"greptile_catches": 3,
"greptile_fps": 1,
"todos_completed": 8,
"avg_coverage_delta": 3
}
```

### Step 14: Write the Narrative

Structure the output as:
Expand Down Expand Up @@ -1008,13 +1039,14 @@ Narrative interpreting what the team-wide patterns mean:
- Notable patterns: do team members code at the same time or in shifts?

### Shipping Velocity
(from Steps 5-7)
(from Steps 5-7 + ship log)

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

### Code Quality Signals
- Test LOC ratio trend
Expand All @@ -1023,7 +1055,7 @@ Narrative covering:

### Test Health
- Total test files: N (from command 10)
- Tests added this period: M (from command 12 — test files changed)
- Tests added this period: M (from command 13 — test files changed)
- Regression test commits: list `test(qa):` and `test(design):` and `test: coverage` commits from command 11
- 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."
Expand Down
40 changes: 36 additions & 4 deletions retro/SKILL.md.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -113,8 +113,11 @@ git log origin/<default> --since="<window>" --oneline --grep="test(qa):" --grep=
# 12. gstack skill usage telemetry (if available)
cat ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true

# 12. Test files changed in window
# 13. Test files changed in window
git log origin/<default> --since="<window>" --format="" --name-only | grep -E '\.(test|spec)\.' | sort -u | wc -l

# 14. Ship run log (if available)
~/.claude/skills/gstack/bin/gstack-ship-log read --window <window> 2>/dev/null || true
```

### Step 2: Compute Metrics
Expand Down Expand Up @@ -187,6 +190,21 @@ If moments exist, list them:

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

**Ship Velocity (if ship log exists):** Read the ship log output from Step 1, command 14. If entries exist within the retro window, aggregate:
- Total `/ship` runs
- PRs created (count entries with non-empty `pr_url`)
- Average review findings per ship run (`review_findings`)
- Total Greptile catches vs false positives (`greptile_fixed` vs `greptile_fps`)
- TODOs completed via `/ship` (sum of `todos_completed`)
- Test coverage delta (average `coverage_after - coverage_before`)

Include in the metrics table:
```
| Ship Velocity | N /ship runs · M PRs · avg X review findings · Y Greptile catches |
```

If the ship log doesn't exist or has no entries in the window (`NO_SHIP_LOG` returned), skip the Ship Velocity row.

### Step 3: Commit Time Distribution

Show hourly histogram in local time using bar chart:
Expand Down Expand Up @@ -386,7 +404,7 @@ Use the Write tool to save the JSON file with this schema:
}
```

**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.
**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.

Include test health data in the JSON when test files exist:
```json
Expand All @@ -409,6 +427,19 @@ Include backlog data in the JSON when TODOS.md exists:
}
```

Include ship velocity data in the JSON when the ship log has entries in the window:
```json
"ship_velocity": {
"ship_runs": 5,
"prs_created": 5,
"avg_review_findings": 2.4,
"greptile_catches": 3,
"greptile_fps": 1,
"todos_completed": 8,
"avg_coverage_delta": 3
}
```

### Step 14: Write the Narrative

Structure the output as:
Expand Down Expand Up @@ -438,13 +469,14 @@ Narrative interpreting what the team-wide patterns mean:
- Notable patterns: do team members code at the same time or in shifts?

### Shipping Velocity
(from Steps 5-7)
(from Steps 5-7 + ship log)

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

### Code Quality Signals
- Test LOC ratio trend
Expand All @@ -453,7 +485,7 @@ Narrative covering:

### Test Health
- Total test files: N (from command 10)
- Tests added this period: M (from command 12 — test files changed)
- Tests added this period: M (from command 13 — test files changed)
- Regression test commits: list `test(qa):` and `test(design):` and `test: coverage` commits from command 11
- 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."
Expand Down
24 changes: 23 additions & 1 deletion ship/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -2434,7 +2434,29 @@ EOF
**If neither CLI is available:**
Print the branch name, remote URL, and instruct the user to create the PR/MR manually via the web UI. Do not stop — the code is pushed and ready.

**Output the PR/MR URL** — then proceed to Step 8.5.
**Output the PR/MR URL** — then proceed to Step 8.25.

---

## Step 8.25: Ship Log

Log shipping data so `/retro` can track velocity trends:

```bash
eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)"
~/.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}'
```

Substitute from earlier steps:
- **VERSION**: from Step 4
- **PR_URL**: the PR/MR URL from Step 8
- **review_findings** / **review_auto_fixed**: from Step 3.5 (0 if none)
- **greptile_comments** / **greptile_fixed** / **greptile_fps**: from Step 3.75 (0 if skipped)
- **todos_completed**: from Step 5.5 (0 if none)
- **tests_passed**: always `true` (Step 3 stops on failure)
- **coverage_before** / **coverage_after**: test file counts from Step 3.4 (0 if skipped)

This step is automatic — never skip it, never ask for confirmation. If `gstack-ship-log` is not found, warn once and continue.

---

Expand Down
24 changes: 23 additions & 1 deletion ship/SKILL.md.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -611,7 +611,29 @@ EOF
**If neither CLI is available:**
Print the branch name, remote URL, and instruct the user to create the PR/MR manually via the web UI. Do not stop — the code is pushed and ready.

**Output the PR/MR URL** — then proceed to Step 8.5.
**Output the PR/MR URL** — then proceed to Step 8.25.

---

## Step 8.25: Ship Log

Log shipping data so `/retro` can track velocity trends:

```bash
eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)"
~/.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}'
```

Substitute from earlier steps:
- **VERSION**: from Step 4
- **PR_URL**: the PR/MR URL from Step 8
- **review_findings** / **review_auto_fixed**: from Step 3.5 (0 if none)
- **greptile_comments** / **greptile_fixed** / **greptile_fps**: from Step 3.75 (0 if skipped)
- **todos_completed**: from Step 5.5 (0 if none)
- **tests_passed**: always `true` (Step 3 stops on failure)
- **coverage_before** / **coverage_after**: test file counts from Step 3.4 (0 if skipped)

This step is automatic — never skip it, never ask for confirmation. If `gstack-ship-log` is not found, warn once and continue.

---

Expand Down
4 changes: 3 additions & 1 deletion test/helpers/touchfiles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,9 +91,10 @@ export const E2E_TOUCHFILES: Record<string, string[]> = {
'review-dashboard-via': ['ship/**', 'scripts/resolvers/review.ts', 'codex/**', 'autoplan/**', 'land-and-deploy/**'],
'ship-plan-completion': ['ship/**', 'scripts/gen-skill-docs.ts'],
'ship-plan-verification': ['ship/**', 'scripts/gen-skill-docs.ts'],
'ship-log': ['ship/**', 'bin/gstack-ship-log'],

// Retro
'retro': ['retro/**'],
'retro': ['retro/**', 'bin/gstack-ship-log'],
'retro-base-branch': ['retro/**'],

// Global discover
Expand Down Expand Up @@ -259,6 +260,7 @@ export const E2E_TIERS: Record<string, 'gate' | 'periodic'> = {
'ship-plan-completion': 'gate',
'ship-plan-verification': 'gate',
'ship-idempotency': 'periodic',
'ship-log': 'periodic',

// Retro — gate for cheap branch detection, periodic for full Opus retro
'retro': 'periodic',
Expand Down
Loading