diff --git a/.agents/skills/gstack-retro/SKILL.md b/.agents/skills/gstack-retro/SKILL.md index a0b796ba..a56cc3b1 100644 --- a/.agents/skills/gstack-retro/SKILL.md +++ b/.agents/skills/gstack-retro/SKILL.md @@ -329,8 +329,11 @@ git log origin/ --since="" --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/ --since="" --format="" --name-only | grep -E '\.(test|spec)\.' | sort -u | wc -l + +# 14. Ship run log (if available) +~/.codex/skills/gstack/bin/gstack-ship-log read --window 2>/dev/null || true ``` ### Step 2: Compute Metrics @@ -403,6 +406,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: @@ -598,7 +616,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 @@ -621,6 +639,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: @@ -650,13 +681,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 diff --git a/.agents/skills/gstack-ship/SKILL.md b/.agents/skills/gstack-ship/SKILL.md index 442c4a72..c438a359 100644 --- a/.agents/skills/gstack-ship/SKILL.md +++ b/.agents/skills/gstack-ship/SKILL.md @@ -1068,7 +1068,34 @@ EOF )" ``` -**Output the PR URL** — then proceed to Step 8.5. +**Output the PR URL** — then proceed to Step 8.25. + +--- + +## Step 8.25: Ship Log + +Append a structured entry to the ship log for `/retro` velocity tracking. This step is fully automatic and must never fail the workflow. + +Collect the data from earlier steps and write a single JSONL entry: + +```bash +source <(~/.codex/skills/gstack/bin/gstack-slug 2>/dev/null) +~/.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}' +``` + +Substitute the capitalized placeholders from data gathered in earlier steps: +- `VERSION`: the new version from Step 4 +- `PR_URL`: the PR URL output from Step 8 +- `review_findings`: total issues found in Step 3.5 (0 if none) +- `review_auto_fixed`: count of auto-fixed issues in Step 3.5 +- `greptile_comments`: total Greptile comments in Step 3.75 (0 if skipped) +- `greptile_fixed`: Greptile comments fixed in Step 3.75 +- `greptile_fps`: Greptile false positives in Step 3.75 +- `todos_completed`: items marked complete in Step 5.5 (0 if none) +- `tests_passed`: `true` (always true here — Step 3 would have stopped if tests failed) +- `coverage_before`/`coverage_after`: test file counts from Step 3.4 (0 if skipped) + +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. --- diff --git a/bin/gstack-ship-log b/bin/gstack-ship-log new file mode 100755 index 00000000..741d3267 --- /dev/null +++ b/bin/gstack-ship-log @@ -0,0 +1,71 @@ +#!/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}" +LOG_FILE="$STATE_DIR/analytics/ship-log.jsonl" + +case "${1:-}" in + read) + if [ ! -f "$LOG_FILE" ]; then + echo "NO_SHIP_LOG" + exit 0 + fi + # Optional --window filter + if [ "${2:-}" = "--window" ] && [ -n "${3:-}" ]; then + WINDOW="${3}" + # Extract numeric value and unit + 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 } + } + } + } + ' "$LOG_FILE" + else + cat "$LOG_FILE" + fi + else + cat "$LOG_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" + echo "$1" >> "$LOG_FILE" + ;; +esac diff --git a/retro/SKILL.md b/retro/SKILL.md index 635b5747..338f8684 100644 --- a/retro/SKILL.md +++ b/retro/SKILL.md @@ -336,8 +336,11 @@ git log origin/ --since="" --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/ --since="" --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 2>/dev/null || true ``` ### Step 2: Compute Metrics @@ -410,6 +413,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: @@ -605,7 +623,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 @@ -628,6 +646,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: @@ -657,13 +688,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 diff --git a/retro/SKILL.md.tmpl b/retro/SKILL.md.tmpl index b3fe8046..5caa938e 100644 --- a/retro/SKILL.md.tmpl +++ b/retro/SKILL.md.tmpl @@ -112,8 +112,11 @@ git log origin/ --since="" --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/ --since="" --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 2>/dev/null || true ``` ### Step 2: Compute Metrics @@ -186,6 +189,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: @@ -381,7 +399,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 @@ -404,6 +422,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: @@ -433,13 +464,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 diff --git a/ship/SKILL.md b/ship/SKILL.md index 6ad69ba7..344c35ec 100644 --- a/ship/SKILL.md +++ b/ship/SKILL.md @@ -1213,7 +1213,34 @@ EOF )" ``` -**Output the PR URL** — then proceed to Step 8.5. +**Output the PR URL** — then proceed to Step 8.25. + +--- + +## Step 8.25: Ship Log + +Append a structured entry to the ship log for `/retro` velocity tracking. This step is fully automatic and must never fail the workflow. + +Collect the data from earlier steps and write a single JSONL entry: + +```bash +source <(~/.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 the capitalized placeholders from data gathered in earlier steps: +- `VERSION`: the new version from Step 4 +- `PR_URL`: the PR URL output from Step 8 +- `review_findings`: total issues found in Step 3.5 (0 if none) +- `review_auto_fixed`: count of auto-fixed issues in Step 3.5 +- `greptile_comments`: total Greptile comments in Step 3.75 (0 if skipped) +- `greptile_fixed`: Greptile comments fixed in Step 3.75 +- `greptile_fps`: Greptile false positives in Step 3.75 +- `todos_completed`: items marked complete in Step 5.5 (0 if none) +- `tests_passed`: `true` (always true here — Step 3 would have stopped if tests failed) +- `coverage_before`/`coverage_after`: test file counts from Step 3.4 (0 if skipped) + +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. --- diff --git a/ship/SKILL.md.tmpl b/ship/SKILL.md.tmpl index a748314d..5dc78dbd 100644 --- a/ship/SKILL.md.tmpl +++ b/ship/SKILL.md.tmpl @@ -615,7 +615,34 @@ EOF )" ``` -**Output the PR URL** — then proceed to Step 8.5. +**Output the PR URL** — then proceed to Step 8.25. + +--- + +## Step 8.25: Ship Log + +Append a structured entry to the ship log for `/retro` velocity tracking. This step is fully automatic and must never fail the workflow. + +Collect the data from earlier steps and write a single JSONL entry: + +```bash +source <(~/.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 the capitalized placeholders from data gathered in earlier steps: +- `VERSION`: the new version from Step 4 +- `PR_URL`: the PR URL output from Step 8 +- `review_findings`: total issues found in Step 3.5 (0 if none) +- `review_auto_fixed`: count of auto-fixed issues in Step 3.5 +- `greptile_comments`: total Greptile comments in Step 3.75 (0 if skipped) +- `greptile_fixed`: Greptile comments fixed in Step 3.75 +- `greptile_fps`: Greptile false positives in Step 3.75 +- `todos_completed`: items marked complete in Step 5.5 (0 if none) +- `tests_passed`: `true` (always true here — Step 3 would have stopped if tests failed) +- `coverage_before`/`coverage_after`: test file counts from Step 3.4 (0 if skipped) + +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. ---