Skip to content

Commit fb18437

Browse files
kbitzclaude
andauthored
feat: add doc discovery and scattered TODO detection to /roadmap (v0.8.4) (#27)
* feat: add doc discovery and scattered TODO detection to roadmap audit New audit checks: check_doc_inventory() lists all .md files with TODO pattern counts and doc type classification. check_scattered_todos() flags non-standard docs containing TODO-like patterns. Shared find_scannable_md_files() helper with proper exclusion list. count_todo_patterns() with fenced code block exclusion supporting backtick and tilde fences including nesting. 17 new test cases covering all patterns, exclusions, code blocks, nested fences, mixed patterns, and edge cases. 65 total tests passing. * feat: add Step 1.5 Doc Discovery to /roadmap skill New step between audit and triage: discovers scattered TODOs in non-standard docs, extracts items one-by-one for user confirmation, deduplicates against existing TODOS.md/ROADMAP.md, merges with [discovered:filepath] provenance tags, and offers doc reclassification as primary resolution. Framed as one-time cleanup with drift detection, not ongoing vacuum. Also adds deferred E5 (doc type heuristics) to TODOS.md. * chore: bump version and changelog (v0.8.4) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 1130b50 commit fb18437

7 files changed

Lines changed: 637 additions & 8 deletions

File tree

CHANGELOG.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,17 @@
22

33
All notable changes to this project will be documented in this file.
44

5+
## [0.8.4] - 2026-04-07
6+
7+
### Added
8+
- Full doc discovery in `/roadmap`: scans all .md files for scattered TODOs (checkboxes, TODO:/FIXME:/HACK:/XXX: markers, section headings, effort markers), extracts actionable items, deduplicates against existing TODOS.md/ROADMAP.md, and merges confirmed items with `[discovered:<filepath>]` provenance tags.
9+
- Doc reclassification offers: after extracting TODOs from a file like plan.md, offers to rewrite the remaining content as a properly-named spec in docs/designs/, delete just the TODO sections, or leave as-is with drift detection.
10+
- Doc inventory audit check: lists all .md files with TODO-pattern counts and doc type classification.
11+
- Scattered TODOs audit check: flags non-standard .md files containing TODO-like patterns.
12+
- Shared `find_scannable_md_files()` helper with proper exclusion list (known docs, archive, .context, node_modules, vendor).
13+
- `count_todo_patterns()` with fenced code block exclusion supporting both backtick and tilde fences, including nested fence handling.
14+
- 17 new tests for doc discovery checks (65 total).
15+
516
## [0.8.3] - 2026-04-06
617

718
### Added

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
0.8.3
1+
0.8.4

bin/roadmap-audit

Lines changed: 194 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
#
44
# Scans TODOS.md, PROGRESS.md, VERSION, and CHANGELOG.md for vocabulary drift,
55
# structural violations, stale items, version mismatches, taxonomy issues,
6-
# doc location opinions, and archive candidates.
6+
# doc location opinions, archive candidates, and scattered TODOs.
77
#
88
# Output: structured report to stdout with ## CHECK_NAME / STATUS / FINDINGS.
99
#
@@ -32,6 +32,101 @@ find_doc() {
3232
fi
3333
}
3434

35+
# find_scannable_md_files
36+
# Find all .md files eligible for TODO scanning (maxdepth 2).
37+
# Excludes: known doc types (ROOT_DOCS, DOCS_DIR_DOCS), archived docs.
38+
# Prints one absolute path per line.
39+
find_scannable_md_files() {
40+
local -a exclude_names=()
41+
# Combine ROOT_DOCS + DOCS_DIR_DOCS into exclude list
42+
for doc in "${ROOT_DOCS[@]}"; do exclude_names+=("$doc"); done
43+
for doc in "${DOCS_DIR_DOCS[@]}"; do exclude_names+=("$doc"); done
44+
45+
find "$REPO_ROOT" -maxdepth 2 -name '*.md' -type f \
46+
-not -path '*/node_modules/*' \
47+
-not -path '*/.git/*' \
48+
-not -path '*/vendor/*' \
49+
-not -path '*/docs/archive/*' \
50+
-not -path '*/.context/*' \
51+
2>/dev/null | while IFS= read -r filepath; do
52+
local basename
53+
basename=$(basename "$filepath")
54+
local skip=0
55+
for excluded in "${exclude_names[@]}"; do
56+
if [ "$basename" = "$excluded" ]; then
57+
skip=1
58+
break
59+
fi
60+
done
61+
if [ "$skip" -eq 0 ]; then
62+
echo "$filepath"
63+
fi
64+
done
65+
}
66+
67+
# count_todo_patterns <filepath>
68+
# Count TODO-like patterns in a file, excluding fenced code blocks.
69+
# Patterns: checkboxes, TODO:/FIXME:/HACK:/XXX:, section headings
70+
# (## TODO, ## Tasks, ## Action Items, ## Backlog), and bold+effort markers.
71+
count_todo_patterns() {
72+
local filepath="$1"
73+
local count=0
74+
local in_code_block=0
75+
local fence_len=0
76+
local fence_char=""
77+
78+
while IFS= read -r line; do
79+
# Handle fenced code blocks: track fence char and count for nesting.
80+
# Supports both backtick (```) and tilde (~~~) fences.
81+
# A fence opens with N chars; only closes with >= N of the SAME char.
82+
local fence_match=""
83+
fence_match=$(echo "$line" | grep -oE '^\s*(`{3,}|~{3,})' 2>/dev/null | tr -d '[:space:]' || true)
84+
if [ -n "$fence_match" ]; then
85+
local fm_len=${#fence_match}
86+
local fm_char="${fence_match:0:1}"
87+
if [ "$in_code_block" -eq 0 ]; then
88+
in_code_block=1
89+
fence_len=$fm_len
90+
fence_char="$fm_char"
91+
elif [ "$fm_char" = "$fence_char" ] && [ "$fm_len" -ge "$fence_len" ]; then
92+
in_code_block=0
93+
fence_len=0
94+
fence_char=""
95+
fi
96+
continue
97+
fi
98+
99+
# Skip lines inside code blocks
100+
if [ "$in_code_block" -eq 1 ]; then
101+
continue
102+
fi
103+
104+
# Check for TODO-like patterns
105+
# 1. Checkboxes: - [ ] or - [x]
106+
if echo "$line" | grep -qE '^[[:space:]]*- \[[ xX]\]' 2>/dev/null; then
107+
count=$((count + 1))
108+
continue
109+
fi
110+
# 2. Inline markers: TODO:, FIXME:, HACK:, XXX:
111+
if echo "$line" | grep -qE '\b(TODO|FIXME|HACK|XXX):' 2>/dev/null; then
112+
count=$((count + 1))
113+
continue
114+
fi
115+
# 3. Section headings: ## TODO, ## Tasks, ## Action Items, ## Backlog
116+
if echo "$line" | grep -qiE '^#{1,3} (TODO|Tasks|Action Items|Backlog)' 2>/dev/null; then
117+
count=$((count + 1))
118+
continue
119+
fi
120+
# 4. Bold task title + effort marker at end: - **...** ... (S|M|L|XL)
121+
if echo "$line" | grep -qE '^[[:space:]]*- \*\*.*\*\*.*\((S|M|L|XL)\)[[:space:]]*$' 2>/dev/null; then
122+
count=$((count + 1))
123+
continue
124+
fi
125+
done < "$filepath"
126+
127+
echo "$count"
128+
}
129+
35130
TODOS_FILE=$(find_doc "TODOS.md")
36131
ROADMAP_FILE=$(find_doc "ROADMAP.md")
37132
PROGRESS_FILE=$(find_doc "PROGRESS.md")
@@ -764,6 +859,102 @@ detect_mode() {
764859
echo ""
765860
}
766861

862+
# ─── Check 9: Doc Inventory ──────────────────────────────────
863+
#
864+
# Lists all .md files found in the repo with TODO-pattern counts
865+
# and whether they are a known doc type. Informational output
866+
# consumed by the skill prompt for Step 1.5 (Doc Discovery).
867+
#
868+
869+
check_doc_inventory() {
870+
echo "## DOC_INVENTORY"
871+
local file_count=0
872+
local files_output=""
873+
874+
# Known doc types for labeling
875+
local -A known_types=()
876+
for doc in "${ROOT_DOCS[@]}"; do known_types["$doc"]="root doc"; done
877+
for doc in "${DOCS_DIR_DOCS[@]}"; do known_types["$doc"]="project doc"; done
878+
879+
# Scan all .md files (including known ones, for inventory)
880+
while IFS= read -r filepath; do
881+
[ -z "$filepath" ] && continue
882+
local basename
883+
basename=$(basename "$filepath")
884+
local relpath="${filepath#$REPO_ROOT/}"
885+
886+
local todo_count
887+
todo_count=$(count_todo_patterns "$filepath")
888+
889+
local doc_type="unknown"
890+
if [ -n "${known_types[$basename]+x}" ]; then
891+
doc_type="${known_types[$basename]}"
892+
elif echo "$filepath" | grep -q '/docs/designs/' 2>/dev/null; then
893+
doc_type="design doc"
894+
elif echo "$filepath" | grep -q '/docs/archive/' 2>/dev/null; then
895+
doc_type="archived doc"
896+
fi
897+
898+
files_output="${files_output}- ${relpath}: ${todo_count} TODO patterns (${doc_type})\n"
899+
file_count=$((file_count + 1))
900+
done < <(find "$REPO_ROOT" -maxdepth 2 -name '*.md' -type f \
901+
-not -path '*/node_modules/*' \
902+
-not -path '*/.git/*' \
903+
-not -path '*/vendor/*' \
904+
-not -path '*/docs/archive/*' \
905+
-not -path '*/.context/*' \
906+
2>/dev/null | sort)
907+
908+
echo "STATUS: info"
909+
echo "FILES:"
910+
if [ "$file_count" -gt 0 ]; then
911+
echo -e "$files_output"
912+
else
913+
echo "- (none)"
914+
fi
915+
echo "TOTAL_FILES: $file_count"
916+
echo ""
917+
}
918+
919+
# ─── Check 10: Scattered TODOs ──────────────────────────────
920+
#
921+
# Flags non-standard .md files that contain TODO-like patterns.
922+
# These are candidates for extraction by the skill prompt.
923+
#
924+
925+
check_scattered_todos() {
926+
echo "## SCATTERED_TODOS"
927+
local findings=""
928+
local found=0
929+
local total_scattered=0
930+
931+
while IFS= read -r filepath; do
932+
[ -z "$filepath" ] && continue
933+
local relpath="${filepath#$REPO_ROOT/}"
934+
935+
local todo_count
936+
todo_count=$(count_todo_patterns "$filepath")
937+
938+
if [ "$todo_count" -gt 0 ]; then
939+
findings="${findings}- ${relpath}: ${todo_count} items\n"
940+
total_scattered=$((total_scattered + todo_count))
941+
found=1
942+
fi
943+
done < <(find_scannable_md_files)
944+
945+
if [ "$found" -eq 0 ]; then
946+
echo "STATUS: pass"
947+
echo "FINDINGS:"
948+
echo "- (none)"
949+
else
950+
echo "STATUS: found"
951+
echo "FINDINGS:"
952+
echo -e "$findings"
953+
fi
954+
echo "TOTAL_SCATTERED: $total_scattered"
955+
echo ""
956+
}
957+
767958
# ─── Run all checks ──────────────────────────────────────────
768959

769960
check_vocab_lint
@@ -774,5 +965,7 @@ check_taxonomy
774965
check_doc_location
775966
check_archive_candidates
776967
check_dependencies
968+
check_doc_inventory
969+
check_scattered_todos
777970
check_unprocessed
778971
detect_mode

docs/PROGRESS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ Build the /browse-native skill and validate it against real macOS apps.
66

77
| Version | Date | Summary |
88
|---------|------|---------|
9+
| 0.8.4 | 2026-04-07 | /roadmap doc discovery: scans all .md files for scattered TODOs, extracts actionable items with one-by-one triage, deduplicates against existing TODOS.md/ROADMAP.md, merges with [discovered:filepath] provenance tags, and offers doc reclassification (rewrite as spec, delete TODO sections, or leave with drift detection). Hybrid architecture: deterministic bash audit for discovery, LLM for semantic extraction and dedup. 17 new tests (65 total). |
910
| 0.8.1 | 2026-04-06 | /roadmap phase triage step: keep/kill + phase assignment before Group/Track placement. Items triaged to current phase or deferred to Future. Version history audit: removed invalid 0.4.2 (doc-only rename folded into 0.4.1), added missing 0.8.0 CHANGELOG entry. |
1011
| 0.8.0 | 2026-04-06 | New /full-review skill: weekly codebase review pipeline with 3 specialized agents (reviewer, hygiene, consistency-auditor) dispatched in parallel, root-cause clustering for triage UX, human approve/reject/defer per cluster, approved findings written to TODOS.md as `[full-review]` source-tagged items. Dedup against ROADMAP.md prevents re-flagging tracked issues. Incremental state checkpointing for resume support. Designed to feed into /roadmap for execution topology. |
1112
| 0.7.0 | 2026-04-06 | New /roadmap skill: deterministic audit script (8 checks, 28 tests) + skill prompt for doc restructuring into Groups > Tracks > Tasks. TODOS.md/ROADMAP.md split (inbox vs execution plan). Two modes: overhaul (first run) and triage (process unprocessed items). /pair-review writes to TODOS.md Unprocessed section with source tags. Shared semver lib extracted. |

docs/TODOS.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,15 @@ canonical locations automatically.
1010
Auto-scaffolding makes first-run onboarding smoother.
1111
- **Effort:** S (human: ~1 day / CC: ~15 min)
1212
- **Depends on:** Doc location opinions shipping (this PR)
13+
14+
### Doc type detection heuristics in audit script
15+
Teach `bin/roadmap-audit` to classify .md files by content patterns: has
16+
requirements/acceptance criteria -> spec, has timeline/phases -> plan, has TODO
17+
markers -> inbox, has architecture diagrams -> design doc. Report mismatches
18+
(e.g., file named plan.md but content looks like a spec).
19+
- **Why:** Currently Step 1.5e relies on LLM judgment for doc type classification.
20+
Bash heuristics would make the audit output more actionable and reduce LLM load.
21+
Makes reclassification offers smarter and more consistent.
22+
- **Effort:** M (human: ~2 days / CC: ~20 min)
23+
- **Priority:** P2
24+
- **Depends on:** Doc discovery shipping (v0.9.0)

0 commit comments

Comments
 (0)