Skip to content

Commit ec77a96

Browse files
committed
feat(board): add verification pipeline and update board hierarchy
- Add verify.just module with tiered verification commands: - Tier 1: Snapshots (~30s) for key screen captures - Tier 2: Checkpoints (~2min) for interaction sequences - Tier 3: Videos (~5min) for CLI + Web recordings with stitching - Update board hierarchy to Epic > Milestone > Story (like Linear) - Add icebox stage with ice/thaw commands for blocked work - Implement new story naming: [TYPE][NNNN]-verb-phrase.md - Add ffmpeg to nix flake for video processing - Update CONVENTIONS.md with new hierarchy and commands
1 parent 8d4e146 commit ec77a96

File tree

8 files changed

+526
-69
lines changed

8 files changed

+526
-69
lines changed

.justfiles/board.just

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -229,21 +229,30 @@ _next-id TYPE:
229229
#!/usr/bin/env bash
230230
cd "{{board_dir}}"
231231
type="{{TYPE}}"
232+
type_upper="${type^^}"
232233
max=0
233234

234-
# Search in all stages for stories with this type prefix
235+
# Search for new format: [TYPE][NNNN]-*.md
236+
for f in $(find stages -name "\[${type_upper}\]\[*\]*.md" 2>/dev/null); do
237+
# Extract sequence number from [TYPE][NNNN] format
238+
num=$(basename "$f" .md | grep -oE "\[${type_upper}\]\[[0-9]+\]" | grep -oE "[0-9]+")
239+
[[ -z "$num" ]] && continue
240+
num=$((10#$num))
241+
if [[ "$num" -gt "$max" ]]; then
242+
max=$num
243+
fi
244+
done
245+
246+
# Also search old format for backwards compatibility: type-NNNN-*.md
235247
for f in $(find stages -name "${type}-*.md" -o -name "*-${type}-*.md" 2>/dev/null); do
236-
# Extract sequence number after type prefix (e.g., feat-01, m37-feat-01)
237-
# Pattern: TYPE-NN where NN is the sequence number we want
238248
num=$(basename "$f" .md | grep -oE "(feat|bug|chore|refactor|fix|docs)-[0-9]+" | grep -oE "[0-9]+$")
239-
# Skip if no match found
240249
[[ -z "$num" ]] && continue
241-
# Strip leading zeros to avoid octal interpretation (e.g., 08 -> 8)
242250
num=$((10#$num))
243251
if [[ "$num" -gt "$max" ]]; then
244252
max=$num
245253
fi
246254
done
255+
247256
printf "%04d" $((max + 1))
248257

249258
# Get next milestone number
@@ -337,24 +346,16 @@ _new-story TITLE TYPE="" EPICS="" MILESTONE="":
337346
# Create slug from title
338347
slug=$(echo "$title" | tr '[:upper:]' '[:lower:]' | tr ' ' '-' | tr -cd 'a-z0-9-')
339348

340-
# Build filename - include milestone prefix if provided
341-
if [[ -n "$milestone" ]]; then
342-
# Extract milestone number (e.g., "27" from "27-crt-design-system")
343-
ms_num=$(echo "$milestone" | grep -oE '^[0-9]+' || echo "")
344-
if [[ -n "$ms_num" ]]; then
345-
filename="stages/backlog/stories/m${ms_num}-${type}-${id}-${slug}.md"
346-
else
347-
filename="stages/backlog/stories/${type}-${id}-${slug}.md"
348-
fi
349-
else
350-
filename="stages/backlog/stories/${type}-${id}-${slug}.md"
351-
fi
349+
# Build filename with new [TYPE][NNNN] format
350+
# Format: [TYPE][NNNN]-verb-phrase.md
351+
type_upper="${type^^}"
352+
filename="stages/backlog/stories/[${type_upper}][${id}]-${slug}.md"
352353

353354
# Create the story file
354355
date=$(date +%Y-%m-%d)
355356
{
356357
echo '---'
357-
echo "id: ${type^^}${id}"
358+
echo "id: ${type_upper}${id}"
358359
echo "title: ${title}"
359360
echo "type: ${type}"
360361
echo "status: backlog"

.justfiles/verify.just

Lines changed: 313 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,313 @@
1+
# Verification commands for coherence testing
2+
# Captures visual artifacts to verify implementation matches specifications
3+
4+
# Run commands from project root
5+
set working-directory := '..'
6+
7+
# Verification output directory
8+
verify_dir := "verification"
9+
10+
# Default: show available verify commands
11+
default:
12+
@echo "Verification Commands"
13+
@echo ""
14+
@echo "Artifact capture:"
15+
@echo " just verify snapshots Tier 1: Capture key screen snapshots (~30s)"
16+
@echo " just verify checkpoints Tier 2: Capture interaction sequences (~2min)"
17+
@echo " just verify videos Tier 3: Record CLI + Web videos (~5min)"
18+
@echo " just verify all Run all tiers"
19+
@echo ""
20+
@echo "Reporting:"
21+
@echo " just verify report Generate verification/report.md"
22+
@echo " just verify summary Show artifact counts"
23+
@echo ""
24+
@echo "Utility:"
25+
@echo " just verify clean Remove all artifacts"
26+
@echo " just verify init Create verification directory structure"
27+
28+
# Initialize verification directory structure
29+
init:
30+
#!/usr/bin/env bash
31+
set -euo pipefail
32+
mkdir -p {{verify_dir}}/snapshots
33+
mkdir -p {{verify_dir}}/checkpoints
34+
mkdir -p {{verify_dir}}/videos/cli
35+
mkdir -p {{verify_dir}}/videos/web
36+
mkdir -p {{verify_dir}}/videos/stitched
37+
38+
# Create .gitignore to only track report.md
39+
echo '# Ignore all artifacts except the report' > {{verify_dir}}/.gitignore
40+
echo '*' >> {{verify_dir}}/.gitignore
41+
echo '!.gitignore' >> {{verify_dir}}/.gitignore
42+
echo '!report.md' >> {{verify_dir}}/.gitignore
43+
echo '!snapshots.json' >> {{verify_dir}}/.gitignore
44+
echo '!checkpoints.json' >> {{verify_dir}}/.gitignore
45+
46+
echo "✓ Verification directory initialized"
47+
48+
# Tier 1: Capture key screen snapshots
49+
snapshots: init
50+
#!/usr/bin/env bash
51+
set -euo pipefail
52+
53+
echo "Capturing snapshots..."
54+
55+
# Check if snapshots.json exists
56+
if [[ ! -f {{verify_dir}}/snapshots.json ]]; then
57+
echo "No snapshots.json found. Creating default..."
58+
echo '{' > {{verify_dir}}/snapshots.json
59+
echo ' "baseUrl": "http://localhost:3000",' >> {{verify_dir}}/snapshots.json
60+
echo ' "snapshots": [' >> {{verify_dir}}/snapshots.json
61+
echo ' {"name": "dashboard", "url": "/groove/status", "waitFor": "[data-testid=status-indicator]"},' >> {{verify_dir}}/snapshots.json
62+
echo ' {"name": "sessions", "url": "/sessions", "waitFor": ".session-row"},' >> {{verify_dir}}/snapshots.json
63+
echo ' {"name": "stream", "url": "/groove/stream", "waitFor": "[data-testid=stream-view]"}' >> {{verify_dir}}/snapshots.json
64+
echo ' ]' >> {{verify_dir}}/snapshots.json
65+
echo '}' >> {{verify_dir}}/snapshots.json
66+
fi
67+
68+
# Run Playwright to capture snapshots
69+
npx playwright test e2e-tests/tests/snapshots.spec.ts --reporter=list 2>/dev/null || {
70+
echo "Note: Snapshot spec not found or server not running."
71+
echo "To capture snapshots, ensure vibes server is running."
72+
}
73+
74+
# Count captured snapshots
75+
count=$(find {{verify_dir}}/snapshots -name "*.png" 2>/dev/null | wc -l | tr -d ' ')
76+
echo "✓ Captured $count snapshots"
77+
78+
# Tier 2: Capture interaction checkpoint sequences
79+
checkpoints: init
80+
#!/usr/bin/env bash
81+
set -euo pipefail
82+
83+
echo "Capturing interaction checkpoints..."
84+
85+
# Check if checkpoints.json exists
86+
if [[ ! -f {{verify_dir}}/checkpoints.json ]]; then
87+
echo "No checkpoints.json found. Creating default..."
88+
echo '{' > {{verify_dir}}/checkpoints.json
89+
echo ' "baseUrl": "http://localhost:3000",' >> {{verify_dir}}/checkpoints.json
90+
echo ' "checkpoints": [' >> {{verify_dir}}/checkpoints.json
91+
echo ' {"name": "navigate-groove", "steps": [' >> {{verify_dir}}/checkpoints.json
92+
echo ' {"action": "goto", "url": "/", "screenshot": "01-home"}' >> {{verify_dir}}/checkpoints.json
93+
echo ' ]}' >> {{verify_dir}}/checkpoints.json
94+
echo ' ]' >> {{verify_dir}}/checkpoints.json
95+
echo '}' >> {{verify_dir}}/checkpoints.json
96+
fi
97+
98+
# Run Playwright checkpoint tests
99+
npx playwright test e2e-tests/tests/checkpoints.spec.ts --reporter=list 2>/dev/null || {
100+
echo "Note: Checkpoint spec not found or server not running."
101+
}
102+
103+
# Count captured checkpoints
104+
count=$(find {{verify_dir}}/checkpoints -name "*.png" 2>/dev/null | wc -l | tr -d ' ')
105+
echo "✓ Captured $count checkpoint images"
106+
107+
# Tier 3: Record CLI + Web videos and stitch together
108+
videos: init
109+
#!/usr/bin/env bash
110+
set -euo pipefail
111+
112+
echo "Recording videos..."
113+
114+
# Record CLI via VHS (if tapes exist)
115+
if [[ -d cli/recordings/tapes ]]; then
116+
echo "Recording CLI videos..."
117+
cd cli/recordings
118+
for tape in tapes/*.tape; do
119+
[[ -f "$tape" ]] || continue
120+
name=$(basename "$tape" .tape)
121+
echo " Recording: $name"
122+
vhs "$tape" -o "../../{{verify_dir}}/videos/cli/${name}.mp4" 2>/dev/null || {
123+
vhs "$tape" 2>/dev/null || true
124+
if [[ -f "output/${name}.gif" ]]; then
125+
ffmpeg -i "output/${name}.gif" -movflags faststart -pix_fmt yuv420p \
126+
"../../{{verify_dir}}/videos/cli/${name}.mp4" -y 2>/dev/null || true
127+
fi
128+
}
129+
done
130+
cd ../..
131+
fi
132+
133+
# Record Web via Playwright (if configured)
134+
echo "Recording Web videos..."
135+
npx playwright test e2e-tests/tests/videos.spec.ts --reporter=list 2>/dev/null || {
136+
echo "Note: Video spec not found. Using existing Playwright recordings if available."
137+
}
138+
139+
# Copy any Playwright video recordings
140+
if [[ -d e2e-tests/test-results ]]; then
141+
find e2e-tests/test-results -name "*.webm" -exec cp {} {{verify_dir}}/videos/web/ \; 2>/dev/null || true
142+
fi
143+
144+
# Stitch CLI + Web videos together if both exist
145+
echo "Stitching videos..."
146+
cli_count=$(find {{verify_dir}}/videos/cli -name "*.mp4" 2>/dev/null | wc -l | tr -d ' ')
147+
web_count=$(find {{verify_dir}}/videos/web \( -name "*.webm" -o -name "*.mp4" \) 2>/dev/null | wc -l | tr -d ' ')
148+
149+
if [[ "$cli_count" -gt 0 ]] && [[ "$web_count" -gt 0 ]]; then
150+
cli_video=$(find {{verify_dir}}/videos/cli -name "*.mp4" | head -1)
151+
web_video=$(find {{verify_dir}}/videos/web \( -name "*.webm" -o -name "*.mp4" \) | head -1)
152+
153+
if [[ -n "$cli_video" ]] && [[ -n "$web_video" ]]; then
154+
ffmpeg -i "$cli_video" -i "$web_video" \
155+
-filter_complex "[0:v]scale=640:-1[left];[1:v]scale=640:-1[right];[left][right]hstack=inputs=2" \
156+
{{verify_dir}}/videos/stitched/combined.mp4 -y 2>/dev/null && \
157+
echo "✓ Created stitched video: verification/videos/stitched/combined.mp4"
158+
fi
159+
fi
160+
161+
echo "✓ Video recording complete"
162+
echo " CLI videos: $cli_count"
163+
echo " Web videos: $web_count"
164+
165+
# Run all verification tiers
166+
all: snapshots checkpoints videos report
167+
@echo ""
168+
@echo "✓ All verification tiers complete"
169+
170+
# Generate verification report
171+
report: init
172+
#!/usr/bin/env bash
173+
set -euo pipefail
174+
175+
echo "Generating verification report..."
176+
177+
branch=$(git branch --show-current 2>/dev/null || echo "unknown")
178+
report_date=$(date -Iseconds)
179+
180+
snapshot_count=$(find {{verify_dir}}/snapshots -name "*.png" 2>/dev/null | wc -l | tr -d ' ')
181+
checkpoint_count=$(find {{verify_dir}}/checkpoints -name "*.png" 2>/dev/null | wc -l | tr -d ' ')
182+
cli_video_count=$(find {{verify_dir}}/videos/cli -name "*.mp4" 2>/dev/null | wc -l | tr -d ' ')
183+
web_video_count=$(find {{verify_dir}}/videos/web \( -name "*.webm" -o -name "*.mp4" \) 2>/dev/null | wc -l | tr -d ' ')
184+
stitched_count=$(find {{verify_dir}}/videos/stitched -name "*.mp4" 2>/dev/null | wc -l | tr -d ' ')
185+
186+
# Find related stories
187+
stories=""
188+
if [[ "$branch" =~ ^(feat|fix|chore|refactor)/([0-9]+) ]]; then
189+
story_id="${BASH_REMATCH[2]}"
190+
stories=$(find docs/board/stages -name "*${story_id}*" -type f 2>/dev/null | head -3 | xargs -I{} basename {} .md | tr '\n' ', ' | sed 's/,$//')
191+
fi
192+
[[ -z "$stories" ]] && stories="(none detected)"
193+
194+
# Status icons
195+
snap_status="⚠️"; [[ "$snapshot_count" -gt 0 ]] && snap_status="✅"
196+
check_status="⚠️"; [[ "$checkpoint_count" -gt 0 ]] && check_status="✅"
197+
cli_status="⚠️"; [[ "$cli_video_count" -gt 0 ]] && cli_status="✅"
198+
web_status="⚠️"; [[ "$web_video_count" -gt 0 ]] && web_status="✅"
199+
stitch_status="⚠️"; [[ "$stitched_count" -gt 0 ]] && stitch_status="✅"
200+
201+
# Write report header
202+
echo "# Verification Report" > {{verify_dir}}/report.md
203+
echo "" >> {{verify_dir}}/report.md
204+
echo "Generated: ${report_date}" >> {{verify_dir}}/report.md
205+
echo "Branch: ${branch}" >> {{verify_dir}}/report.md
206+
echo "Stories: ${stories}" >> {{verify_dir}}/report.md
207+
echo "" >> {{verify_dir}}/report.md
208+
echo "## Summary" >> {{verify_dir}}/report.md
209+
echo "" >> {{verify_dir}}/report.md
210+
echo "| Tier | Artifact Type | Count | Status |" >> {{verify_dir}}/report.md
211+
echo "|------|---------------|-------|--------|" >> {{verify_dir}}/report.md
212+
echo "| 1 | Snapshots | ${snapshot_count} | ${snap_status} |" >> {{verify_dir}}/report.md
213+
echo "| 2 | Checkpoints | ${checkpoint_count} | ${check_status} |" >> {{verify_dir}}/report.md
214+
echo "| 3 | CLI Videos | ${cli_video_count} | ${cli_status} |" >> {{verify_dir}}/report.md
215+
echo "| 3 | Web Videos | ${web_video_count} | ${web_status} |" >> {{verify_dir}}/report.md
216+
echo "| 3 | Stitched Videos | ${stitched_count} | ${stitch_status} |" >> {{verify_dir}}/report.md
217+
echo "" >> {{verify_dir}}/report.md
218+
219+
# Artifacts section
220+
echo "## Artifacts" >> {{verify_dir}}/report.md
221+
echo "" >> {{verify_dir}}/report.md
222+
echo "### Snapshots (Tier 1)" >> {{verify_dir}}/report.md
223+
if [[ "$snapshot_count" -gt 0 ]]; then
224+
find {{verify_dir}}/snapshots -name "*.png" | while read -r f; do
225+
echo "- $(basename "$f")" >> {{verify_dir}}/report.md
226+
done
227+
else
228+
echo "*No snapshots captured*" >> {{verify_dir}}/report.md
229+
fi
230+
echo "" >> {{verify_dir}}/report.md
231+
232+
echo "### Checkpoints (Tier 2)" >> {{verify_dir}}/report.md
233+
if [[ "$checkpoint_count" -gt 0 ]]; then
234+
find {{verify_dir}}/checkpoints -name "*.png" | sort | while read -r f; do
235+
echo "- $(basename "$f")" >> {{verify_dir}}/report.md
236+
done
237+
else
238+
echo "*No checkpoints captured*" >> {{verify_dir}}/report.md
239+
fi
240+
echo "" >> {{verify_dir}}/report.md
241+
242+
echo "### Videos (Tier 3)" >> {{verify_dir}}/report.md
243+
total_videos=$((cli_video_count + web_video_count + stitched_count))
244+
if [[ "$total_videos" -gt 0 ]]; then
245+
if [[ "$cli_video_count" -gt 0 ]]; then
246+
echo "**CLI:**" >> {{verify_dir}}/report.md
247+
find {{verify_dir}}/videos/cli -name "*.mp4" 2>/dev/null | while read -r f; do
248+
echo "- $(basename "$f")" >> {{verify_dir}}/report.md
249+
done
250+
fi
251+
if [[ "$web_video_count" -gt 0 ]]; then
252+
echo "" >> {{verify_dir}}/report.md
253+
echo "**Web:**" >> {{verify_dir}}/report.md
254+
find {{verify_dir}}/videos/web \( -name "*.webm" -o -name "*.mp4" \) 2>/dev/null | while read -r f; do
255+
echo "- $(basename "$f")" >> {{verify_dir}}/report.md
256+
done
257+
fi
258+
if [[ "$stitched_count" -gt 0 ]]; then
259+
echo "" >> {{verify_dir}}/report.md
260+
echo "**Stitched:**" >> {{verify_dir}}/report.md
261+
find {{verify_dir}}/videos/stitched -name "*.mp4" 2>/dev/null | while read -r f; do
262+
echo "- $(basename "$f")" >> {{verify_dir}}/report.md
263+
done
264+
fi
265+
else
266+
echo "*No videos recorded*" >> {{verify_dir}}/report.md
267+
fi
268+
269+
echo "" >> {{verify_dir}}/report.md
270+
echo "## Notes" >> {{verify_dir}}/report.md
271+
echo "" >> {{verify_dir}}/report.md
272+
echo "- Artifacts are stored locally (gitignored)" >> {{verify_dir}}/report.md
273+
echo "- Only this report is committed" >> {{verify_dir}}/report.md
274+
echo "- Review artifacts locally before creating PR" >> {{verify_dir}}/report.md
275+
276+
echo "✓ Generated {{verify_dir}}/report.md"
277+
278+
# Show artifact summary
279+
summary:
280+
#!/usr/bin/env bash
281+
echo "Verification Artifacts"
282+
echo "======================"
283+
echo ""
284+
285+
snapshot_count=$(find {{verify_dir}}/snapshots -name "*.png" 2>/dev/null | wc -l | tr -d ' ')
286+
checkpoint_count=$(find {{verify_dir}}/checkpoints -name "*.png" 2>/dev/null | wc -l | tr -d ' ')
287+
cli_video_count=$(find {{verify_dir}}/videos/cli -name "*.mp4" 2>/dev/null | wc -l | tr -d ' ')
288+
web_video_count=$(find {{verify_dir}}/videos/web \( -name "*.webm" -o -name "*.mp4" \) 2>/dev/null | wc -l | tr -d ' ')
289+
stitched_count=$(find {{verify_dir}}/videos/stitched -name "*.mp4" 2>/dev/null | wc -l | tr -d ' ')
290+
291+
echo "Tier 1 - Snapshots: $snapshot_count"
292+
echo "Tier 2 - Checkpoints: $checkpoint_count"
293+
echo "Tier 3 - CLI Videos: $cli_video_count"
294+
echo "Tier 3 - Web Videos: $web_video_count"
295+
echo "Tier 3 - Stitched: $stitched_count"
296+
echo ""
297+
298+
if [[ -f {{verify_dir}}/report.md ]]; then
299+
echo "Report: {{verify_dir}}/report.md"
300+
else
301+
echo "Report: not generated (run 'just verify report')"
302+
fi
303+
304+
# Remove all verification artifacts
305+
clean:
306+
#!/usr/bin/env bash
307+
echo "Cleaning verification artifacts..."
308+
rm -rf {{verify_dir}}/snapshots/*
309+
rm -rf {{verify_dir}}/checkpoints/*
310+
rm -rf {{verify_dir}}/videos/cli/*
311+
rm -rf {{verify_dir}}/videos/web/*
312+
rm -rf {{verify_dir}}/videos/stitched/*
313+
echo "✓ Artifacts cleaned (report.md preserved if present)"

0 commit comments

Comments
 (0)