Skip to content

Commit b3ca3ad

Browse files
mpawliszynclaude
andcommitted
feat: add coverage-report.sh with tests
Shell script to generate a coverage summary from review-tree.md. Read-only, no file mutations. Output is structured markdown with a documented format contract for the orchestrator. Reports: - Node counts: pending, reviewed, accepted, total - Progress: decided/total with percentage - Confidence: examined in detail (reviewed) vs pattern-trusted (accepted) - Nodes with comments (flag-precise pattern), top-level concept count - File coverage: files in diff, files mapped, unmapped - Pending nodes list (only shown if any remain) Fixes from review: - Node-counting greps require [0-9] after status to prevent context block inflation (I1) - Comment flag pattern uses \{[^}]*comment\} for precision - File coverage data extracted from tree's Coverage section (E1) - Progress percentage added (E2) - Output format documented in script header as contract 18 bats tests covering: - Status counts, progress percentage, comment count - Confidence summary, top-level count, file coverage - Pending nodes (listed when present, hidden when none) - Section headers, input validation - Context inflation protection (regression test) - Edge cases (all pending with 0%, all reviewed with 100%) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 369d3e3 commit b3ca3ad

2 files changed

Lines changed: 354 additions & 0 deletions

File tree

scripts/coverage-report.sh

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
#!/usr/bin/env bash
2+
#
3+
# coverage-report.sh -- Generate a coverage summary from a review-tree.md file.
4+
#
5+
# Usage: coverage-report.sh <tree-file>
6+
#
7+
# Output: Structured markdown text suitable for the orchestrator to present
8+
# to the customer. The output format is a contract -- the orchestrator parses
9+
# these field names and section headers.
10+
#
11+
# Output format:
12+
# ## Status
13+
# pending: <N>
14+
# reviewed: <N>
15+
# accepted: <N>
16+
# total: <N>
17+
# decided: <N>/<total> (<pct>%)
18+
# with comments: <N>
19+
# examined in detail: <N>
20+
# pattern-trusted: <N>
21+
# top-level concepts: <N>
22+
# ## Files
23+
# files in diff: <N>
24+
# files mapped: <N>
25+
# unmapped: <text>
26+
# ## Pending (only if pending > 0)
27+
# Pending nodes:
28+
# <list>
29+
#
30+
# Exit codes:
31+
# 0 -- success
32+
# 1 -- invalid arguments or file not found
33+
34+
set -euo pipefail
35+
36+
# --- Input validation ---
37+
38+
if [ $# -lt 1 ]; then
39+
echo "Usage: coverage-report.sh <tree-file>" >&2
40+
exit 1
41+
fi
42+
43+
TREE_FILE="$1"
44+
45+
if [ ! -f "$TREE_FILE" ]; then
46+
echo "Error: file not found: $TREE_FILE" >&2
47+
exit 1
48+
fi
49+
50+
# --- Count nodes by status ---
51+
# Pattern includes [0-9] after status to avoid matching status-like text in context blocks
52+
53+
PENDING=$(grep -cE '^\s*- \[pending\] [0-9]' "$TREE_FILE" || true)
54+
REVIEWED=$(grep -cE '^\s*- \[reviewed\] [0-9]' "$TREE_FILE" || true)
55+
ACCEPTED=$(grep -cE '^\s*- \[accepted\] [0-9]' "$TREE_FILE" || true)
56+
TOTAL=$((PENDING + REVIEWED + ACCEPTED))
57+
58+
# --- Progress ---
59+
60+
DECIDED=$((REVIEWED + ACCEPTED))
61+
if [ "$TOTAL" -gt 0 ]; then
62+
PCT=$((DECIDED * 100 / TOTAL))
63+
else
64+
PCT=0
65+
fi
66+
67+
# --- Count nodes with comments ---
68+
# Pattern matches "comment" inside flag braces only, not in titles or context
69+
70+
WITH_COMMENTS=$(grep -cE '\{[^}]*comment\}' "$TREE_FILE" || true)
71+
72+
# --- Count top-level concepts ---
73+
74+
TOP_LEVEL=$(grep -cE '^- \[(pending|reviewed|accepted)\] [0-9]' "$TREE_FILE" || true)
75+
76+
# --- File coverage from tree's Coverage section ---
77+
78+
TOTAL_FILES=$(grep 'Total files in diff:' "$TREE_FILE" | grep -oE '[0-9]+' || true)
79+
MAPPED_FILES=$(grep 'Files mapped to tree:' "$TREE_FILE" | grep -oE '[0-9]+' || true)
80+
UNMAPPED=$(grep 'Unmapped files:' "$TREE_FILE" | sed 's/.*: //' || true)
81+
82+
# --- List pending nodes ---
83+
84+
PENDING_LIST=$(grep -E '^\s*- \[pending\] [0-9]' "$TREE_FILE" | sed -E 's/^\s*- \[pending\] / /' || true)
85+
86+
# --- Output report ---
87+
88+
echo "## Status"
89+
echo ""
90+
echo " pending: $PENDING"
91+
echo " reviewed: $REVIEWED"
92+
echo " accepted: $ACCEPTED"
93+
echo " total: $TOTAL"
94+
echo " decided: $DECIDED/$TOTAL ($PCT%)"
95+
echo " with comments: $WITH_COMMENTS"
96+
echo ""
97+
echo " examined in detail: $REVIEWED"
98+
echo " pattern-trusted: $ACCEPTED"
99+
echo " top-level concepts: $TOP_LEVEL"
100+
101+
if [ -n "$TOTAL_FILES" ]; then
102+
echo ""
103+
echo "## Files"
104+
echo ""
105+
echo " files in diff: $TOTAL_FILES"
106+
echo " files mapped: $MAPPED_FILES"
107+
echo " unmapped: $UNMAPPED"
108+
fi
109+
110+
if [ "$PENDING" -gt 0 ]; then
111+
echo ""
112+
echo "## Pending"
113+
echo ""
114+
echo " Pending nodes:"
115+
echo "$PENDING_LIST"
116+
fi
Lines changed: 238 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,238 @@
1+
#!/usr/bin/env bats
2+
3+
# Tests for scripts/coverage-report.sh
4+
# TDD: write tests first, then implement the script.
5+
6+
REPO_ROOT="$(cd "$BATS_TEST_DIRNAME/../.." && pwd)"
7+
SAMPLE="$REPO_ROOT/tests/formats/sample-tree-hawksbury.md"
8+
SCRIPT="$REPO_ROOT/scripts/coverage-report.sh"
9+
10+
# --- Status counts ---
11+
12+
@test "reports count of pending nodes" {
13+
run "$SCRIPT" "$SAMPLE"
14+
[ "$status" -eq 0 ]
15+
[[ "$output" == *"pending: 1"* ]]
16+
}
17+
18+
@test "reports count of reviewed nodes" {
19+
run "$SCRIPT" "$SAMPLE"
20+
[ "$status" -eq 0 ]
21+
[[ "$output" == *"reviewed: 14"* ]]
22+
}
23+
24+
@test "reports count of accepted nodes" {
25+
run "$SCRIPT" "$SAMPLE"
26+
[ "$status" -eq 0 ]
27+
[[ "$output" == *"accepted: 28"* ]]
28+
}
29+
30+
@test "reports total node count" {
31+
run "$SCRIPT" "$SAMPLE"
32+
[ "$status" -eq 0 ]
33+
[[ "$output" == *"total: 43"* ]]
34+
}
35+
36+
# --- Progress ---
37+
38+
@test "reports progress percentage" {
39+
run "$SCRIPT" "$SAMPLE"
40+
[ "$status" -eq 0 ]
41+
# 42 decided out of 43 = 97%
42+
[[ "$output" == *"decided: 42/43 (97%)"* ]]
43+
}
44+
45+
# --- Comment counts ---
46+
47+
@test "reports count of nodes with comments" {
48+
run "$SCRIPT" "$SAMPLE"
49+
[ "$status" -eq 0 ]
50+
[[ "$output" == *"with comments: 4"* ]]
51+
}
52+
53+
# --- Pending nodes list ---
54+
55+
@test "lists pending nodes" {
56+
run "$SCRIPT" "$SAMPLE"
57+
[ "$status" -eq 0 ]
58+
[[ "$output" == *"5. CLAUDE.md"* ]]
59+
}
60+
61+
@test "no pending nodes listed when all reviewed or accepted" {
62+
local tmpfile="$BATS_TEST_TMPDIR/tree.md"
63+
cp "$SAMPLE" "$tmpfile"
64+
"$REPO_ROOT/scripts/update-node-status.sh" "$tmpfile" "5" "reviewed"
65+
run "$SCRIPT" "$tmpfile"
66+
[ "$status" -eq 0 ]
67+
[[ "$output" == *"pending: 0"* ]]
68+
[[ "$output" != *"Pending nodes:"* ]]
69+
}
70+
71+
# --- Confidence summary ---
72+
73+
@test "reports confidence summary" {
74+
run "$SCRIPT" "$SAMPLE"
75+
[ "$status" -eq 0 ]
76+
[[ "$output" == *"examined in detail: 14"* ]]
77+
[[ "$output" == *"pattern-trusted: 28"* ]]
78+
}
79+
80+
# --- Top-level summary ---
81+
82+
@test "reports top-level concept count" {
83+
run "$SCRIPT" "$SAMPLE"
84+
[ "$status" -eq 0 ]
85+
[[ "$output" == *"top-level concepts: 5"* ]]
86+
}
87+
88+
# --- File coverage ---
89+
90+
@test "reports file coverage from tree" {
91+
run "$SCRIPT" "$SAMPLE"
92+
[ "$status" -eq 0 ]
93+
[[ "$output" == *"files in diff: 32"* ]]
94+
[[ "$output" == *"files mapped: 32"* ]]
95+
[[ "$output" == *"unmapped: none"* ]]
96+
}
97+
98+
@test "output has Files section header" {
99+
run "$SCRIPT" "$SAMPLE"
100+
[ "$status" -eq 0 ]
101+
[[ "$output" == *"## Files"* ]]
102+
}
103+
104+
# --- Output structure ---
105+
106+
@test "output has section headers" {
107+
run "$SCRIPT" "$SAMPLE"
108+
[ "$status" -eq 0 ]
109+
[[ "$output" == *"## Status"* ]]
110+
[[ "$output" == *"## Pending"* ]]
111+
}
112+
113+
# --- Input validation ---
114+
115+
@test "rejects missing file argument" {
116+
run "$SCRIPT"
117+
[ "$status" -ne 0 ]
118+
}
119+
120+
@test "rejects non-existent file" {
121+
run "$SCRIPT" "/tmp/nonexistent-tree.md"
122+
[ "$status" -ne 0 ]
123+
}
124+
125+
# --- Context inflation protection ---
126+
127+
@test "context block with status-like text does not inflate counts" {
128+
local tmpfile="$BATS_TEST_TMPDIR/tree.md"
129+
cat > "$tmpfile" << 'EOF'
130+
# Review Tree: Test
131+
132+
| Field | Value |
133+
|-------------|-------|
134+
| PR | test/test#1 |
135+
| HEAD | abc123 |
136+
| Revision | 1 |
137+
| Tree Built | 2026-02-25T10:00:00Z |
138+
| Updated | 2026-02-25T10:00:00Z |
139+
140+
## Tree
141+
142+
- [reviewed] 1. Guard mechanism
143+
context: |
144+
Status markers in other systems:
145+
- [pending] means waiting for review
146+
- [accepted] means done
147+
148+
## Description Verification
149+
150+
| # | Claim | Status | Evidence |
151+
152+
## Coverage
153+
154+
Total files in diff: 0
155+
Files mapped to tree: 0
156+
Unmapped files: none
157+
EOF
158+
run "$SCRIPT" "$tmpfile"
159+
[ "$status" -eq 0 ]
160+
[[ "$output" == *"pending: 0"* ]]
161+
[[ "$output" == *"reviewed: 1"* ]]
162+
[[ "$output" == *"accepted: 0"* ]]
163+
[[ "$output" == *"total: 1"* ]]
164+
}
165+
166+
# --- Edge cases ---
167+
168+
@test "handles tree with all nodes pending" {
169+
local tmpfile="$BATS_TEST_TMPDIR/tree.md"
170+
cat > "$tmpfile" << 'EOF'
171+
# Review Tree: Test
172+
173+
| Field | Value |
174+
|-------------|-------|
175+
| PR | test/test#1 |
176+
| HEAD | abc123 |
177+
| Revision | 1 |
178+
| Tree Built | 2026-02-25T10:00:00Z |
179+
| Updated | 2026-02-25T10:00:00Z |
180+
181+
## Tree
182+
183+
- [pending] 1. First concept
184+
- [pending] 2. Second concept
185+
186+
## Description Verification
187+
188+
| # | Claim | Status | Evidence |
189+
190+
## Coverage
191+
192+
Total files in diff: 0
193+
Files mapped to tree: 0
194+
Unmapped files: none
195+
EOF
196+
run "$SCRIPT" "$tmpfile"
197+
[ "$status" -eq 0 ]
198+
[[ "$output" == *"pending: 2"* ]]
199+
[[ "$output" == *"reviewed: 0"* ]]
200+
[[ "$output" == *"accepted: 0"* ]]
201+
[[ "$output" == *"decided: 0/2 (0%)"* ]]
202+
}
203+
204+
@test "handles tree with all nodes reviewed" {
205+
local tmpfile="$BATS_TEST_TMPDIR/tree.md"
206+
cat > "$tmpfile" << 'EOF'
207+
# Review Tree: Test
208+
209+
| Field | Value |
210+
|-------------|-------|
211+
| PR | test/test#1 |
212+
| HEAD | abc123 |
213+
| Revision | 1 |
214+
| Tree Built | 2026-02-25T10:00:00Z |
215+
| Updated | 2026-02-25T10:00:00Z |
216+
217+
## Tree
218+
219+
- [reviewed] 1. First concept
220+
- [reviewed] 2. Second concept
221+
222+
## Description Verification
223+
224+
| # | Claim | Status | Evidence |
225+
226+
## Coverage
227+
228+
Total files in diff: 0
229+
Files mapped to tree: 0
230+
Unmapped files: none
231+
EOF
232+
run "$SCRIPT" "$tmpfile"
233+
[ "$status" -eq 0 ]
234+
[[ "$output" == *"pending: 0"* ]]
235+
[[ "$output" == *"reviewed: 2"* ]]
236+
[[ "$output" == *"decided: 2/2 (100%)"* ]]
237+
[[ "$output" != *"Pending nodes:"* ]]
238+
}

0 commit comments

Comments
 (0)