mindsystem-cc 3.18.1 → 3.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -25,8 +25,8 @@ Decimal phases enable urgent work insertion without renumbering:
25
25
  Create executable phase prompts (PLAN.md files) optimized for parallel execution.
26
26
 
27
27
  **Two-stage workflow:**
28
- 1. **Main context:** Task identification (steps 1-8) - collaborative, keeps user in loop
29
- 2. **Subagent (ms-plan-writer):** Plan writing (dependency graph, wave assignment, PLAN.md files, risk scoring) - autonomous, heavy lifting
28
+ 1. **Main context:** Task identification + grouping proposal (steps 1-9) - collaborative, keeps user in loop
29
+ 2. **Subagent (ms-plan-writer):** Plan writing (structural validation, PLAN.md files, risk scoring) - autonomous, heavy lifting
30
30
 
31
31
  PLAN.md IS the prompt that Claude executes. Plans are grouped into execution waves based on dependencies - independent plans run in parallel, dependent plans wait for predecessors.
32
32
  </purpose>
@@ -280,7 +280,7 @@ PHASE_NAME=$(grep -A2 "Phase ${PHASE}:" .planning/ROADMAP.md 2>/dev/null | head
280
280
  uv run ~/.claude/mindsystem/scripts/scan-planning-context.py \
281
281
  --phase "${PHASE}" \
282
282
  --phase-name "${PHASE_NAME}" \
283
- ${SUBSYSTEM:+--subsystem "${SUBSYSTEM}"}
283
+ ${SUBSYSTEM:+--subsystem="${SUBSYSTEM}"}
284
284
  ```
285
285
 
286
286
  The scanner outputs formatted markdown with sections for patterns, learnings,
@@ -389,7 +389,9 @@ Standard tasks (remain in standard plans):
389
389
  → Yes: Mark as tdd_candidate=true
390
390
  → No: Standard task
391
391
 
392
- Read `~/.claude/mindsystem/references/tdd.md` now for TDD criteria and plan structure.
392
+ **If any tasks were marked tdd_candidate=true:** Read `~/.claude/mindsystem/references/tdd.md` for TDD plan structure guidance.
393
+
394
+ **If no TDD candidates:** Skip — the heuristic above is sufficient for detection.
393
395
 
394
396
  **Decisions:** If you identify a task that requires choosing between approaches (which auth provider, which database, etc.), use AskUserQuestion to resolve it now. Don't defer decisions to execution. For purely technical choices where the user hasn't expressed preference, make the decision and document it in the plan's objective.
395
397
 
@@ -419,6 +421,56 @@ Format: numbered list with task name, key files, dependency hint, and `[TDD]` fl
419
421
  </output_format>
420
422
  </step>
421
423
 
424
+ <step name="propose_grouping">
425
+ **Propose plan boundaries before handing off to the plan-writer.**
426
+
427
+ After presenting the task list, analyze dependencies and propose how tasks should group into plans. This is a collaborative planning decision — the user sees it and can adjust.
428
+
429
+ **Process:**
430
+ 1. Map task dependencies from needs/creates
431
+ 2. Identify independent task clusters (parallel candidates = Wave 1)
432
+ 3. Group by vertical feature affinity (not horizontal layers)
433
+ 4. Estimate context budget per group using weight heuristics (L ~5%, M ~10%, H ~20%)
434
+ 5. Target 25-45% per plan, bias toward fewer plans
435
+
436
+ **Present to user:**
437
+
438
+ ```markdown
439
+ ### Proposed Plan Structure
440
+
441
+ **Plan 01: {title}** (~{budget}%)
442
+ Tasks: {task_ids} — {brief rationale}
443
+
444
+ **Plan 02: {title}** (~{budget}%)
445
+ Tasks: {task_ids} — {brief rationale}
446
+
447
+ **Waves:** {wave structure}
448
+ ```
449
+
450
+ The user may adjust, merge, or split plans. Once confirmed (or if the user proceeds without objection), pass the proposed grouping to the plan-writer.
451
+
452
+ **TDD plans are always standalone** — propose them as dedicated plans regardless of budget.
453
+ </step>
454
+
455
+ <step name="discover_skills">
456
+ **Identify relevant project skills for this phase.**
457
+
458
+ After the user confirms the plan structure, check if project skills could improve plan quality.
459
+
460
+ **Scan:** Review the skill list in your system-reminder. Match skills against:
461
+ - The phase's technology stack (Flutter, React, Node.js, etc.)
462
+ - The domain of the tasks identified (UI patterns, API design, state management, etc.)
463
+ - Keywords from RESEARCH.md or CONTEXT.md if they exist
464
+
465
+ **If matches found:** Present via AskUserQuestion:
466
+ - List each matching skill with its description
467
+ - Always include an escape hatch option for the user to name skills manually or skip entirely
468
+
469
+ **If no matches:** Skip silently — no need to ask the user.
470
+
471
+ **Store result:** Keep the confirmed skill names (may be empty) for the handoff step.
472
+ </step>
473
+
422
474
  <step name="handoff_to_writer">
423
475
  **Spawn ms-plan-writer subagent with task list and context.**
424
476
 
@@ -433,6 +485,14 @@ Assemble handoff payload:
433
485
  {Construct full task XML from your analysis. Each task needs: id, name, type, needs, creates, tdd_candidate, action_hint, verify_hint, done_hint. Use the same XML schema the plan-writer expects.}
434
486
  </task_list>
435
487
 
488
+ <proposed_grouping>
489
+ <plan id="01" title="{title}" budget="{estimated_%}" wave="{wave_number}">
490
+ <tasks>{comma-separated task IDs}</tasks>
491
+ <rationale>{why these tasks belong together}</rationale>
492
+ </plan>
493
+ <!-- More plans... -->
494
+ </proposed_grouping>
495
+
436
496
  <phase_context>
437
497
  <phase_number>{PHASE}</phase_number>
438
498
  <phase_name>{PHASE_NAME}</phase_name>
@@ -461,6 +521,10 @@ Assemble handoff payload:
461
521
  {list of services detected in task breakdown}
462
522
  </external_services>
463
523
 
524
+ <confirmed_skills>
525
+ {comma-separated skill names confirmed by user, or "none"}
526
+ </confirmed_skills>
527
+
464
528
  <learnings>
465
529
  <!-- Flat list from read_project_history step 6. Omit if no matches found. -->
466
530
  <learning type="debug" source=".planning/debug/resolved/{slug}.md">{root_cause} — fix: {resolution}</learning>
@@ -482,10 +546,11 @@ Task(
482
546
 
483
547
  The subagent handles:
484
548
  - Building dependency graph from needs/creates
549
+ - Validating proposed grouping (file conflicts, circular deps, missing dependency chains)
550
+ - Applying proposed plan boundaries (deviates only for structural issues, not budget math)
485
551
  - Assigning wave numbers
486
- - Grouping tasks into plans (budget-based, ~45% cost target)
487
552
  - Deriving Must-Haves (goal-backward)
488
- - Estimating scope, splitting if needed
553
+ - Estimating scope (informational, for grouping rationale)
489
554
  - Writing PLAN.md files + EXECUTION-ORDER.md
490
555
  - Git commit
491
556
  - Calculating risk score
@@ -543,24 +608,24 @@ Extract:
543
608
  **Skip tier (0-39):**
544
609
  - header: "Plan Verification"
545
610
  - question: "Risk Score: {score}/100 — Low risk\n\nPlans look straightforward. Verification optional."
546
- - Options: "Execute now" (first), "Verify anyway"
611
+ - Options: "Skip verification" (first), "Verify plans"
547
612
 
548
613
  **Optional tier (40-69):**
549
614
  - header: "Plan Verification"
550
615
  - question: "Risk Score: {score}/100 — Moderate complexity\n\nTop factors:\n- {factor_1}\n- {factor_2}\n\nVerification recommended but optional."
551
- - Options: "Verify first" (first), "Execute now", "Review plans manually"
616
+ - Options: "Verify plans" (first), "Skip verification", "Review plans manually"
552
617
 
553
618
  **Verify tier (70-100):**
554
619
  - header: "Plan Verification Recommended"
555
620
  - question: "Risk Score: {score}/100 — Higher complexity\n\nTop factors:\n- {factor_1}\n- {factor_2}\n- {factor_3}\n\nVerification strongly recommended."
556
- - Options: "Verify first (Recommended)" (first), "Execute anyway", "Review plans manually"
621
+ - Options: "Verify plans (Recommended)" (first), "Skip verification", "Review plans manually"
557
622
 
558
623
  **Handle response:**
559
624
 
560
- **"Execute now" / "Execute anyway":**
625
+ **"Skip verification":**
561
626
  Continue to offer_next.
562
627
 
563
- **"Verify first" / "Verify anyway":**
628
+ **"Verify plans":**
564
629
  Spawn ms-plan-checker:
565
630
 
566
631
  ```
@@ -658,13 +723,10 @@ Tasks are instructions for Claude, not Jira tickets.
658
723
  - [ ] Mandatory discovery completed (Level 0-3)
659
724
  - [ ] Prior decisions, issues, concerns synthesized
660
725
  - [ ] Tasks identified with needs/creates dependencies
661
- - [ ] Task list handed off to ms-plan-writer
662
- - [ ] PLAN file(s) created with pure markdown format
663
- - [ ] EXECUTION-ORDER.md created with wave groups
664
- - [ ] Each plan: Must-Haves section with observable truths
665
- - [ ] Each plan: budget-based grouping (target 25-45%, consolidate under 10%)
666
- - [ ] Wave structure maximizes parallelism
667
- - [ ] PLAN file(s) committed to git
726
+ - [ ] Plan grouping proposed and presented to user
727
+ - [ ] Task list + proposed grouping + confirmed skills handed off to ms-plan-writer
728
+ - [ ] PLAN files + EXECUTION-ORDER.md created (pure markdown, Must-Haves, follows proposed grouping)
729
+ - [ ] Plans committed with maximized wave parallelism
668
730
  - [ ] Risk assessment presented (score + top factors)
669
731
  - [ ] User chose verify/skip (or verified if chosen)
670
732
  - [ ] User knows next steps and wave structure
@@ -141,16 +141,17 @@ cat .planning/phases/XX-current/*-SUMMARY.md
141
141
  **Assess requirement changes:**
142
142
 
143
143
  1. **Requirements validated?**
144
- - Any Active requirements shipped in this phase?
145
- - Move to Validated with phase reference: `- ✓ [Requirement] — Phase X`
144
+ - Any requirements shipped in this phase?
145
+ - Add to Validated with phase reference: `- ✓ [Requirement] — Phase X`
146
146
 
147
147
  2. **Requirements invalidated?**
148
- - Any Active requirements discovered to be unnecessary or wrong?
149
- - Move to Out of Scope with reason: `- [Requirement] — [why invalidated]`
148
+ - Any requirements discovered to be unnecessary or wrong?
149
+ - Add to Out of Scope with reason: `- [Requirement] — [why invalidated]`
150
150
 
151
- 3. **Requirements emerged?**
152
- - Any new requirements discovered during building?
153
- - Add to Active: `- [ ] [New requirement]`
151
+ 3. **Business context evolved?**
152
+ - Has understanding of audience, problem, or differentiation changed?
153
+ - Update Who It's For, Core Problem, or How It's Different if so
154
+ - New key flows emerged? → Update Key User Flows
154
155
 
155
156
  4. **Decisions to log?**
156
157
  - Extract decisions from SUMMARY.md files
@@ -174,13 +175,11 @@ Make the edits inline. Update "Last updated" footer:
174
175
  Before:
175
176
 
176
177
  ```markdown
177
- ### Active
178
+ ## Validated
178
179
 
179
- - [ ] JWT authentication
180
- - [ ] Real-time sync < 500ms
181
- - [ ] Offline mode
180
+ - Canvas drawing tools — Phase 1
182
181
 
183
- ### Out of Scope
182
+ ## Out of Scope
184
183
 
185
184
  - OAuth2 — complexity not needed for v1
186
185
  ```
@@ -188,27 +187,23 @@ Before:
188
187
  After (Phase 2 shipped JWT auth, discovered rate limiting needed):
189
188
 
190
189
  ```markdown
191
- ### Validated
190
+ ## Validated
192
191
 
192
+ - ✓ Canvas drawing tools — Phase 1
193
193
  - ✓ JWT authentication — Phase 2
194
194
 
195
- ### Active
196
-
197
- - [ ] Real-time sync < 500ms
198
- - [ ] Offline mode
199
- - [ ] Rate limiting on sync endpoint
200
-
201
- ### Out of Scope
195
+ ## Out of Scope
202
196
 
203
197
  - OAuth2 — complexity not needed for v1
198
+ - Offline mode — real-time is core value, discovered Phase 2
204
199
  ```
205
200
 
206
201
  **Step complete when:**
207
202
 
208
203
  - [ ] Phase summaries reviewed for learnings
209
- - [ ] Validated requirements moved from Active
210
- - [ ] Invalidated requirements moved to Out of Scope with reason
211
- - [ ] Emerged requirements added to Active
204
+ - [ ] Shipped requirements added to Validated
205
+ - [ ] Invalidated requirements added to Out of Scope with reason
206
+ - [ ] Business context sections reviewed (Who It's For, Core Problem, How It's Different, Key User Flows)
212
207
  - [ ] New decisions logged with rationale
213
208
  - [ ] "What This Is" updated if product changed
214
209
  - [ ] "Last updated" footer reflects this transition
@@ -532,7 +532,7 @@ Group related gaps into fix plans:
532
532
  ```
533
533
 
534
534
  3. **Keep plans focused:**
535
- - Budget-based grouping (weights within 45%)
535
+ - Budget-aware grouping (target 25-45% per plan)
536
536
  - Single concern per plan
537
537
  - Include verification task
538
538
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mindsystem-cc",
3
- "version": "3.18.1",
3
+ "version": "3.20.0",
4
4
  "description": "A meta-prompting, context engineering and spec-driven development system for Claude Code by TÂCHES.",
5
5
  "bin": {
6
6
  "mindsystem-cc": "bin/install.js"
@@ -0,0 +1,402 @@
1
+ #!/bin/bash
2
+ #
3
+ # doctor-scan.sh
4
+ # Single-pass diagnostic scan of the .planning/ tree.
5
+ # Reports on 6 health check categories with structured output.
6
+ #
7
+ # Usage: ./scripts/doctor-scan.sh
8
+ #
9
+ # Exit codes:
10
+ # 0 — all checks pass
11
+ # 1 — one or more checks failed
12
+ # 2 — .planning/ or config.json missing (cannot scan)
13
+
14
+ set -e
15
+
16
+ # --- Find .planning from git root ---
17
+ GIT_ROOT=$(git rev-parse --show-toplevel 2>/dev/null)
18
+ if [ -z "$GIT_ROOT" ]; then
19
+ echo "Error: Not in a git repository"
20
+ exit 2
21
+ fi
22
+
23
+ PLANNING="$GIT_ROOT/.planning"
24
+ if [ ! -d "$PLANNING" ]; then
25
+ echo "Error: No .planning/ directory found"
26
+ exit 2
27
+ fi
28
+
29
+ CONFIG="$PLANNING/config.json"
30
+ if [ ! -f "$CONFIG" ]; then
31
+ echo "Error: No config.json found at $CONFIG"
32
+ exit 2
33
+ fi
34
+
35
+ MILESTONES_FILE="$PLANNING/MILESTONES.md"
36
+ PHASES_DIR="$PLANNING/phases"
37
+ MILESTONES_DIR="$PLANNING/milestones"
38
+ KNOWLEDGE_DIR="$PLANNING/knowledge"
39
+
40
+ PASS_COUNT=0
41
+ FAIL_COUNT=0
42
+ SKIP_COUNT=0
43
+ FAILED_CHECKS=""
44
+
45
+ # --- Helper: record check result ---
46
+ record_result() {
47
+ local status="$1"
48
+ local name="$2"
49
+ case "$status" in
50
+ PASS) PASS_COUNT=$((PASS_COUNT + 1)) ;;
51
+ FAIL) FAIL_COUNT=$((FAIL_COUNT + 1)); FAILED_CHECKS="$FAILED_CHECKS $name" ;;
52
+ SKIP) SKIP_COUNT=$((SKIP_COUNT + 1)) ;;
53
+ esac
54
+ }
55
+
56
+ # --- Helper: parse phase numbers from a "Phases completed" line ---
57
+ # Outputs one phase number per line. Handles:
58
+ # - Range format: "1-6" → 1 2 3 4 5 6
59
+ # - Comma-separated: "8, 8.3, 9, 10" → 8 8.3 9 10
60
+ # - Mixed (multiple milestone entries each handled independently)
61
+ parse_phase_numbers() {
62
+ local line="$1"
63
+ local range
64
+ range=$(echo "$line" | grep -o '[0-9]\+-[0-9]\+' || true)
65
+ if [ -n "$range" ]; then
66
+ local range_start range_end
67
+ range_start=$(echo "$range" | cut -d'-' -f1)
68
+ range_end=$(echo "$range" | cut -d'-' -f2)
69
+ seq "$range_start" "$range_end"
70
+ else
71
+ echo "$line" | sed 's/.*://' | grep -oE '[0-9]+(\.[0-9]+)?' || true
72
+ fi
73
+ }
74
+
75
+ # --- Helper: format phase number as zero-padded directory prefix ---
76
+ # Integer (9) → "09", Decimal (8.3) → "08.3"
77
+ format_phase_prefix() {
78
+ local phase="$1"
79
+ if echo "$phase" | grep -q '\.'; then
80
+ local int_part dec_part
81
+ int_part=$(echo "$phase" | cut -d'.' -f1)
82
+ dec_part=$(echo "$phase" | cut -d'.' -f2)
83
+ printf "%02d.%s" "$int_part" "$dec_part"
84
+ else
85
+ printf "%02d" "$phase"
86
+ fi
87
+ }
88
+
89
+ # ============================================================
90
+ # CHECK 1: Subsystem Vocabulary
91
+ # ============================================================
92
+ echo "=== Subsystem Vocabulary ==="
93
+
94
+ SUBSYSTEM_COUNT=$(jq -r '.subsystems // [] | length' "$CONFIG" 2>/dev/null || echo "0")
95
+
96
+ if [ "$SUBSYSTEM_COUNT" -eq 0 ]; then
97
+ echo "Status: FAIL"
98
+ echo "No subsystems array in config.json (or empty)"
99
+ record_result "FAIL" "Subsystem Vocabulary"
100
+ else
101
+ echo "Subsystems: $SUBSYSTEM_COUNT configured"
102
+ jq -r '.subsystems[]' "$CONFIG" 2>/dev/null | sed 's/^/ - /'
103
+
104
+ # Run artifact scan to check for mismatches
105
+ SCAN_SCRIPT="$(dirname "$0")/scan-artifact-subsystems.sh"
106
+ if [ -x "$SCAN_SCRIPT" ]; then
107
+ CANONICAL=$(jq -r '.subsystems[]' "$CONFIG" 2>/dev/null)
108
+ ARTIFACT_VALUES=$("$SCAN_SCRIPT" --values-only 2>/dev/null | grep -v '^===' | sort -u)
109
+ MISMATCHES=""
110
+ while IFS= read -r val; do
111
+ [ -z "$val" ] && continue
112
+ if ! echo "$CANONICAL" | grep -qx "$val"; then
113
+ MISMATCHES="$MISMATCHES $val"
114
+ fi
115
+ done <<< "$ARTIFACT_VALUES"
116
+
117
+ if [ -n "$MISMATCHES" ]; then
118
+ echo "Status: FAIL"
119
+ echo "Artifact values not in canonical list:$MISMATCHES"
120
+ record_result "FAIL" "Subsystem Vocabulary"
121
+ else
122
+ ARTIFACT_COUNT=$("$SCAN_SCRIPT" --values-only 2>/dev/null | grep -v '^===' | wc -l | tr -d ' ')
123
+ echo "Artifacts scanned: $ARTIFACT_COUNT (all OK)"
124
+ echo "Status: PASS"
125
+ record_result "PASS" "Subsystem Vocabulary"
126
+ fi
127
+ else
128
+ echo "Status: PASS"
129
+ echo "(scan-artifact-subsystems.sh not found — skipped artifact validation)"
130
+ record_result "PASS" "Subsystem Vocabulary"
131
+ fi
132
+ fi
133
+ echo ""
134
+
135
+ # ============================================================
136
+ # CHECK 2: Milestone Directory Structure
137
+ # ============================================================
138
+ echo "=== Milestone Directory Structure ==="
139
+
140
+ if [ ! -d "$MILESTONES_DIR" ]; then
141
+ # No milestones directory at all — check if MILESTONES.md has entries
142
+ if [ -f "$MILESTONES_FILE" ] && grep -q "^## " "$MILESTONES_FILE" 2>/dev/null; then
143
+ echo "Status: FAIL"
144
+ echo "MILESTONES.md has entries but no milestones/ directory"
145
+ record_result "FAIL" "Milestone Directory Structure"
146
+ else
147
+ echo "Status: SKIP"
148
+ echo "No completed milestones"
149
+ record_result "SKIP" "Milestone Directory Structure"
150
+ fi
151
+ else
152
+ # Look for flat files matching v*-*.md directly in milestones/
153
+ FLAT_FILES=""
154
+ FLAT_COUNT=0
155
+ for f in "$MILESTONES_DIR"/v*-*.md; do
156
+ [ -f "$f" ] || continue
157
+ FLAT_FILES="$FLAT_FILES $(basename "$f")"$'\n'
158
+ FLAT_COUNT=$((FLAT_COUNT + 1))
159
+ done
160
+
161
+ if [ "$FLAT_COUNT" -gt 0 ]; then
162
+ echo "Status: FAIL"
163
+ echo "Found $FLAT_COUNT flat file(s) in milestones/ (old format):"
164
+ echo "$FLAT_FILES"
165
+ # Check if corresponding versioned directories exist
166
+ for f in "$MILESTONES_DIR"/v*-*.md; do
167
+ [ -f "$f" ] || continue
168
+ fname=$(basename "$f")
169
+ # Extract version prefix: v0.1-ROADMAP.md -> v0.1
170
+ version=$(echo "$fname" | sed 's/^\(v[0-9.]*\)-.*/\1/')
171
+ if [ -d "$MILESTONES_DIR/$version" ]; then
172
+ echo " $fname → directory $version/ exists (can restructure)"
173
+ else
174
+ echo " $fname → directory $version/ missing (need to create)"
175
+ fi
176
+ done
177
+ record_result "FAIL" "Milestone Directory Structure"
178
+ else
179
+ # Count versioned directories
180
+ DIR_COUNT=0
181
+ for d in "$MILESTONES_DIR"/v*/; do
182
+ [ -d "$d" ] || continue
183
+ DIR_COUNT=$((DIR_COUNT + 1))
184
+ done
185
+
186
+ if [ "$DIR_COUNT" -eq 0 ]; then
187
+ echo "Status: SKIP"
188
+ echo "No completed milestones"
189
+ record_result "SKIP" "Milestone Directory Structure"
190
+ else
191
+ echo "Status: PASS"
192
+ echo "$DIR_COUNT versioned milestone directories"
193
+ record_result "PASS" "Milestone Directory Structure"
194
+ fi
195
+ fi
196
+ fi
197
+ echo ""
198
+
199
+ # ============================================================
200
+ # CHECK 3: Phase Archival
201
+ # ============================================================
202
+ echo "=== Phase Archival ==="
203
+
204
+ if [ ! -f "$MILESTONES_FILE" ] || ! grep -q "Phases completed" "$MILESTONES_FILE" 2>/dev/null; then
205
+ echo "Status: SKIP"
206
+ echo "No completed milestones with phase ranges in MILESTONES.md"
207
+ record_result "SKIP" "Phase Archival"
208
+ else
209
+ ORPHAN_COUNT=0
210
+ ORPHAN_LIST=""
211
+
212
+ # Parse each milestone's completed phases from MILESTONES.md
213
+ while IFS= read -r line; do
214
+ while IFS= read -r phase_num; do
215
+ [ -z "$phase_num" ] && continue
216
+ prefix=$(format_phase_prefix "$phase_num")
217
+ for dir in "$PHASES_DIR"/${prefix}-*/; do
218
+ [ -d "$dir" ] || continue
219
+ dirname=$(basename "$dir")
220
+ ORPHAN_COUNT=$((ORPHAN_COUNT + 1))
221
+ ORPHAN_LIST="$ORPHAN_LIST $dirname (should be archived)"$'\n'
222
+ done
223
+ done < <(parse_phase_numbers "$line")
224
+ done < <(grep "Phases completed" "$MILESTONES_FILE")
225
+
226
+ if [ "$ORPHAN_COUNT" -gt 0 ]; then
227
+ echo "Status: FAIL"
228
+ echo "Found $ORPHAN_COUNT orphaned phase directories from completed milestones:"
229
+ echo "$ORPHAN_LIST"
230
+ record_result "FAIL" "Phase Archival"
231
+ else
232
+ echo "Status: PASS"
233
+ echo "All completed milestone phases are archived"
234
+ record_result "PASS" "Phase Archival"
235
+ fi
236
+ fi
237
+ echo ""
238
+
239
+ # ============================================================
240
+ # CHECK 4: Knowledge Files
241
+ # ============================================================
242
+ echo "=== Knowledge Files ==="
243
+
244
+ if [ "$SUBSYSTEM_COUNT" -eq 0 ]; then
245
+ echo "Status: SKIP"
246
+ echo "No subsystems configured — knowledge check requires subsystem vocabulary"
247
+ record_result "SKIP" "Knowledge Files"
248
+ elif [ ! -d "$KNOWLEDGE_DIR" ]; then
249
+ echo "Status: FAIL"
250
+ echo "Knowledge directory missing: .planning/knowledge/"
251
+ echo "Expected files for $SUBSYSTEM_COUNT subsystems"
252
+ record_result "FAIL" "Knowledge Files"
253
+ else
254
+ MISSING_KNOWLEDGE=""
255
+ MISSING_COUNT=0
256
+ PRESENT_COUNT=0
257
+ while IFS= read -r subsystem; do
258
+ [ -z "$subsystem" ] && continue
259
+ if [ -f "$KNOWLEDGE_DIR/$subsystem.md" ]; then
260
+ PRESENT_COUNT=$((PRESENT_COUNT + 1))
261
+ else
262
+ MISSING_COUNT=$((MISSING_COUNT + 1))
263
+ MISSING_KNOWLEDGE="$MISSING_KNOWLEDGE $subsystem.md"$'\n'
264
+ fi
265
+ done < <(jq -r '.subsystems[]' "$CONFIG" 2>/dev/null)
266
+
267
+ # Check for orphaned knowledge files (not in subsystem list)
268
+ ORPHAN_KNOWLEDGE=""
269
+ ORPHAN_K_COUNT=0
270
+ for f in "$KNOWLEDGE_DIR"/*.md; do
271
+ [ -f "$f" ] || continue
272
+ fname=$(basename "$f" .md)
273
+ if ! jq -r '.subsystems[]' "$CONFIG" 2>/dev/null | grep -qx "$fname"; then
274
+ ORPHAN_K_COUNT=$((ORPHAN_K_COUNT + 1))
275
+ ORPHAN_KNOWLEDGE="$ORPHAN_KNOWLEDGE $fname.md (not in subsystems list)"$'\n'
276
+ fi
277
+ done
278
+
279
+ if [ "$MISSING_COUNT" -gt 0 ] || [ "$ORPHAN_K_COUNT" -gt 0 ]; then
280
+ echo "Status: FAIL"
281
+ echo "Coverage: $PRESENT_COUNT/$SUBSYSTEM_COUNT subsystems have knowledge files"
282
+ if [ "$MISSING_COUNT" -gt 0 ]; then
283
+ echo "Missing:"
284
+ echo "$MISSING_KNOWLEDGE"
285
+ fi
286
+ if [ "$ORPHAN_K_COUNT" -gt 0 ]; then
287
+ echo "Orphaned:"
288
+ echo "$ORPHAN_KNOWLEDGE"
289
+ fi
290
+ record_result "FAIL" "Knowledge Files"
291
+ else
292
+ echo "Status: PASS"
293
+ echo "All $SUBSYSTEM_COUNT subsystems have knowledge files"
294
+ record_result "PASS" "Knowledge Files"
295
+ fi
296
+ fi
297
+ echo ""
298
+
299
+ # ============================================================
300
+ # CHECK 5: Phase Summaries
301
+ # ============================================================
302
+ echo "=== Phase Summaries ==="
303
+
304
+ if [ ! -d "$MILESTONES_DIR" ]; then
305
+ echo "Status: SKIP"
306
+ echo "No milestones directory"
307
+ record_result "SKIP" "Phase Summaries"
308
+ else
309
+ MISSING_SUMMARIES=""
310
+ MISSING_S_COUNT=0
311
+ CHECKED=0
312
+
313
+ for d in "$MILESTONES_DIR"/v*/; do
314
+ [ -d "$d" ] || continue
315
+ CHECKED=$((CHECKED + 1))
316
+ version=$(basename "$d")
317
+ if [ ! -f "$d/PHASE-SUMMARIES.md" ]; then
318
+ MISSING_S_COUNT=$((MISSING_S_COUNT + 1))
319
+ MISSING_SUMMARIES="$MISSING_SUMMARIES $version/PHASE-SUMMARIES.md"$'\n'
320
+ fi
321
+ done
322
+
323
+ if [ "$CHECKED" -eq 0 ]; then
324
+ echo "Status: SKIP"
325
+ echo "No versioned milestone directories"
326
+ record_result "SKIP" "Phase Summaries"
327
+ elif [ "$MISSING_S_COUNT" -gt 0 ]; then
328
+ echo "Status: FAIL"
329
+ echo "Missing PHASE-SUMMARIES.md in $MISSING_S_COUNT milestone(s):"
330
+ echo "$MISSING_SUMMARIES"
331
+ record_result "FAIL" "Phase Summaries"
332
+ else
333
+ echo "Status: PASS"
334
+ echo "All $CHECKED milestones have PHASE-SUMMARIES.md"
335
+ record_result "PASS" "Phase Summaries"
336
+ fi
337
+ fi
338
+ echo ""
339
+
340
+ # ============================================================
341
+ # CHECK 6: PLAN Cleanup
342
+ # ============================================================
343
+ echo "=== PLAN Cleanup ==="
344
+
345
+ if [ ! -f "$MILESTONES_FILE" ] || ! grep -q "Phases completed" "$MILESTONES_FILE" 2>/dev/null; then
346
+ echo "Status: SKIP"
347
+ echo "No completed milestones — active phase PLANs are expected"
348
+ record_result "SKIP" "PLAN Cleanup"
349
+ else
350
+ LEFTOVER_PLANS=""
351
+ LEFTOVER_COUNT=0
352
+
353
+ # Check phases/ for PLANs belonging to completed milestones
354
+ while IFS= read -r line; do
355
+ while IFS= read -r phase_num; do
356
+ [ -z "$phase_num" ] && continue
357
+ prefix=$(format_phase_prefix "$phase_num")
358
+ for plan in "$PHASES_DIR"/${prefix}-*/*-PLAN.md; do
359
+ [ -f "$plan" ] || continue
360
+ LEFTOVER_COUNT=$((LEFTOVER_COUNT + 1))
361
+ LEFTOVER_PLANS="$LEFTOVER_PLANS $(echo "$plan" | sed "s|$GIT_ROOT/.planning/||")"$'\n'
362
+ done
363
+ done < <(parse_phase_numbers "$line")
364
+ done < <(grep "Phases completed" "$MILESTONES_FILE")
365
+
366
+ # Check archived milestone phase directories for leftover PLANs
367
+ for d in "$MILESTONES_DIR"/v*/phases/*/; do
368
+ [ -d "$d" ] || continue
369
+ for plan in "$d"*-PLAN.md; do
370
+ [ -f "$plan" ] || continue
371
+ LEFTOVER_COUNT=$((LEFTOVER_COUNT + 1))
372
+ LEFTOVER_PLANS="$LEFTOVER_PLANS $(echo "$plan" | sed "s|$GIT_ROOT/.planning/||")"$'\n'
373
+ done
374
+ done
375
+
376
+ if [ "$LEFTOVER_COUNT" -gt 0 ]; then
377
+ echo "Status: FAIL"
378
+ echo "Found $LEFTOVER_COUNT leftover PLAN file(s) in completed phases:"
379
+ echo "$LEFTOVER_PLANS"
380
+ record_result "FAIL" "PLAN Cleanup"
381
+ else
382
+ echo "Status: PASS"
383
+ echo "No leftover PLAN files in completed phases"
384
+ record_result "PASS" "PLAN Cleanup"
385
+ fi
386
+ fi
387
+ echo ""
388
+
389
+ # ============================================================
390
+ # SUMMARY
391
+ # ============================================================
392
+ TOTAL=$((PASS_COUNT + FAIL_COUNT + SKIP_COUNT))
393
+ echo "=== Summary ==="
394
+ echo "Checks: $TOTAL total, $PASS_COUNT passed, $FAIL_COUNT failed, $SKIP_COUNT skipped"
395
+
396
+ if [ "$FAIL_COUNT" -gt 0 ]; then
397
+ echo "Issues:$FAILED_CHECKS"
398
+ exit 1
399
+ else
400
+ echo "All checks passed"
401
+ exit 0
402
+ fi